Initial MSVC 2008 projects workspace

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

316
L2C_Login.vcproj Normal file
View File

@ -0,0 +1,316 @@
<?xml version="1.0" encoding="windows-1251"?>
<VisualStudioProject
ProjectType="Visual C++"
Version="9,00"
Name="L2C_Login"
ProjectGUID="{76233D4B-4DB6-4757-9F9E-572738DA9CDB}"
RootNamespace="L2C_Login"
Keyword="Win32Proj"
TargetFrameworkVersion="196613"
>
<Platforms>
<Platform
Name="Win32"
/>
</Platforms>
<ToolFiles>
</ToolFiles>
<Configurations>
<Configuration
Name="Debug|Win32"
OutputDirectory="_build\L2C_Login_Debug"
IntermediateDirectory="_build\L2C_Login_Debug"
ConfigurationType="1"
CharacterSet="1"
>
<Tool
Name="VCPreBuildEventTool"
/>
<Tool
Name="VCCustomBuildTool"
/>
<Tool
Name="VCXMLDataGeneratorTool"
/>
<Tool
Name="VCWebServiceProxyGeneratorTool"
/>
<Tool
Name="VCMIDLTool"
/>
<Tool
Name="VCCLCompilerTool"
Optimization="0"
AdditionalIncludeDirectories="l2packets;libl2c_utils;lib"
PreprocessorDefinitions="WIN32;_DEBUG;_WINDOWS"
MinimalRebuild="true"
BasicRuntimeChecks="3"
RuntimeLibrary="1"
UsePrecompiledHeader="2"
PrecompiledHeaderThrough="pch.h"
ProgramDataBaseFileName="$(IntDir)\$(TargetName).pdb"
WarningLevel="4"
DebugInformationFormat="4"
/>
<Tool
Name="VCManagedResourceCompilerTool"
/>
<Tool
Name="VCResourceCompilerTool"
/>
<Tool
Name="VCPreLinkEventTool"
/>
<Tool
Name="VCLinkerTool"
AdditionalDependencies="ws2_32.lib comctl32.lib libeay32_st_MTd.lib ssleay32_st_MTd.lib"
OutputFile="$(OutDir)\$(ProjectName)_d.exe"
LinkIncremental="2"
AdditionalLibraryDirectories="lib\openssl"
GenerateManifest="true"
GenerateDebugInformation="true"
SubSystem="2"
TargetMachine="1"
/>
<Tool
Name="VCALinkTool"
/>
<Tool
Name="VCManifestTool"
/>
<Tool
Name="VCXDCMakeTool"
/>
<Tool
Name="VCBscMakeTool"
/>
<Tool
Name="VCFxCopTool"
/>
<Tool
Name="VCAppVerifierTool"
/>
<Tool
Name="VCPostBuildEventTool"
Description="Copy files to dist folder..."
CommandLine="L2C_Login\PostBuildEvent.cmd $(TargetName) $(TargetDir)"
/>
</Configuration>
<Configuration
Name="Release|Win32"
OutputDirectory="_build\L2C_Login_Release"
IntermediateDirectory="_build\L2C_Login_Release"
ConfigurationType="1"
CharacterSet="1"
WholeProgramOptimization="1"
>
<Tool
Name="VCPreBuildEventTool"
/>
<Tool
Name="VCCustomBuildTool"
/>
<Tool
Name="VCXMLDataGeneratorTool"
/>
<Tool
Name="VCWebServiceProxyGeneratorTool"
/>
<Tool
Name="VCMIDLTool"
/>
<Tool
Name="VCCLCompilerTool"
Optimization="2"
EnableIntrinsicFunctions="true"
AdditionalIncludeDirectories="l2packets;libl2c_utils;lib"
PreprocessorDefinitions="WIN32;NDEBUG;_WINDOWS"
RuntimeLibrary="0"
EnableFunctionLevelLinking="true"
UsePrecompiledHeader="2"
PrecompiledHeaderThrough="pch.h"
ProgramDataBaseFileName="$(IntDir)\$(TargetName).pdb"
WarningLevel="3"
DebugInformationFormat="3"
/>
<Tool
Name="VCManagedResourceCompilerTool"
/>
<Tool
Name="VCResourceCompilerTool"
/>
<Tool
Name="VCPreLinkEventTool"
/>
<Tool
Name="VCLinkerTool"
AdditionalDependencies="ws2_32.lib comctl32.lib libeay32_st_MT.lib ssleay32_st_MT.lib"
LinkIncremental="1"
AdditionalLibraryDirectories="lib\openssl"
GenerateManifest="true"
GenerateDebugInformation="true"
SubSystem="2"
OptimizeReferences="2"
EnableCOMDATFolding="2"
TargetMachine="1"
/>
<Tool
Name="VCALinkTool"
/>
<Tool
Name="VCManifestTool"
/>
<Tool
Name="VCXDCMakeTool"
/>
<Tool
Name="VCBscMakeTool"
/>
<Tool
Name="VCFxCopTool"
/>
<Tool
Name="VCAppVerifierTool"
/>
<Tool
Name="VCPostBuildEventTool"
Description="Copy files to dist folder..."
CommandLine="L2C_Login\PostBuildEvent.cmd $(TargetName) $(TargetDir)"
/>
</Configuration>
</Configurations>
<References>
</References>
<Files>
<Filter
Name="rc"
>
<File
RelativePath=".\L2C_Login\L2C.ico"
>
</File>
<File
RelativePath=".\L2C_Login\L2Logind.rc"
>
</File>
<File
RelativePath=".\L2C_Login\Resource.h"
>
</File>
</Filter>
<Filter
Name="main"
>
<File
RelativePath=".\L2C_Login\L2Logind.cpp"
>
</File>
<File
RelativePath=".\L2C_Login\Log.cpp"
>
</File>
<File
RelativePath=".\L2C_Login\Log.h"
>
</File>
<File
RelativePath=".\L2C_Login\pch.cpp"
>
<FileConfiguration
Name="Debug|Win32"
>
<Tool
Name="VCCLCompilerTool"
UsePrecompiledHeader="1"
/>
</FileConfiguration>
<FileConfiguration
Name="Release|Win32"
>
<Tool
Name="VCCLCompilerTool"
UsePrecompiledHeader="1"
/>
</FileConfiguration>
</File>
<File
RelativePath=".\L2C_Login\pch.h"
>
</File>
</Filter>
<Filter
Name="LS"
>
<File
RelativePath=".\L2C_Login\BanInfo.cpp"
>
</File>
<File
RelativePath=".\L2C_Login\BanInfo.h"
>
</File>
<File
RelativePath=".\L2C_Login\FailedLoginAttempt.cpp"
>
</File>
<File
RelativePath=".\L2C_Login\FailedLoginAttempt.h"
>
</File>
<File
RelativePath=".\L2C_Login\GameServerInfo.cpp"
>
</File>
<File
RelativePath=".\L2C_Login\GameServerInfo.h"
>
</File>
<File
RelativePath=".\L2C_Login\GameServerTable.cpp"
>
</File>
<File
RelativePath=".\L2C_Login\GameServerTable.h"
>
</File>
<File
RelativePath=".\L2C_Login\GS_ConnectionInfo.h"
>
</File>
<File
RelativePath=".\L2C_Login\LoginClient.cpp"
>
</File>
<File
RelativePath=".\L2C_Login\LoginClient.h"
>
</File>
<File
RelativePath=".\L2C_Login\LoginConfig.cpp"
>
</File>
<File
RelativePath=".\L2C_Login\LoginConfig.h"
>
</File>
<File
RelativePath=".\L2C_Login\LoginController.cpp"
>
</File>
<File
RelativePath=".\L2C_Login\LoginController.h"
>
</File>
<File
RelativePath=".\L2C_Login\LoginServer.cpp"
>
</File>
<File
RelativePath=".\L2C_Login\LoginServer.h"
>
</File>
</Filter>
</Files>
<Globals>
</Globals>
</VisualStudioProject>

40
L2C_Login/BanInfo.cpp Normal file
View File

@ -0,0 +1,40 @@
#include "pch.h"
#include "BanInfo.h"
BanInfo::BanInfo()
{
m_ipAddress.clear();
m_expiration = 0;
}
BanInfo::BanInfo( const BanInfo& other )
{
this->operator=( other );
}
BanInfo::BanInfo( const char *ipAddress, time_t expiration )
{
m_ipAddress = ipAddress;
m_expiration = expiration;
}
const BanInfo& BanInfo::operator=( const BanInfo& other )
{
if( this == &other ) return (*this);
this->m_ipAddress = other.m_ipAddress;
this->m_expiration = other.m_expiration;
return (*this);
}
const char *BanInfo::getAddress() const
{
return m_ipAddress.c_str();
}
bool BanInfo::hasExpired() const
{
time_t nowTime = time( NULL );
if( m_expiration > 0 && nowTime > m_expiration ) return true;
return false;
}

16
L2C_Login/BanInfo.h Normal file
View File

@ -0,0 +1,16 @@
#pragma once
class BanInfo
{
public:
BanInfo();
BanInfo( const BanInfo& other );
BanInfo( const char *ipAddress, time_t expiration );
const BanInfo& operator=( const BanInfo& other );
public:
const char *getAddress() const;
bool hasExpired() const;
protected:
std::string m_ipAddress;
time_t m_expiration;
};

View File

@ -0,0 +1,56 @@
#include "pch.h"
#include "FailedLoginAttempt.h"
FailedLoginAttempt::FailedLoginAttempt()
{
m_count = 0;
m_lastPassword.clear();
m_lastAttemptTime = 0;
}
FailedLoginAttempt::FailedLoginAttempt( const char *lastPassword )
{
m_count = 1;
m_lastPassword = lastPassword;
m_lastAttemptTime = time( NULL );
}
FailedLoginAttempt::FailedLoginAttempt( const FailedLoginAttempt& other )
{
this->operator=( other );
}
const FailedLoginAttempt& FailedLoginAttempt::operator=( const FailedLoginAttempt& other )
{
if( this == &other ) return (*this);
m_count = other.m_count;
m_lastPassword = other.m_lastPassword;
m_lastAttemptTime = other.m_lastAttemptTime;
return (*this);
}
void FailedLoginAttempt::increaseCounter( const char *password )
{
std::string new_pass = password;
time_t new_time = time( NULL );
if( m_lastPassword != new_pass )
{
// check if theres a long time since last wrong try
if( new_time - m_lastAttemptTime > 300 ) // 5 min
m_count++;
else // restart counter
m_count = 1;
m_lastPassword = new_pass;
m_lastAttemptTime = new_time;
}
else // trying the same password is not brute force
{
m_lastAttemptTime = new_time;
}
}
int FailedLoginAttempt::getCount() const
{
return m_count;
}

View File

@ -0,0 +1,19 @@
#pragma once
class FailedLoginAttempt
{
public:
FailedLoginAttempt();
FailedLoginAttempt( const char *lastPassword );
FailedLoginAttempt( const FailedLoginAttempt& other );
const FailedLoginAttempt& operator=( const FailedLoginAttempt& other );
public:
void increaseCounter( const char *password );
int getCount() const;
protected:
int m_count;
time_t m_lastAttemptTime;
std::string m_lastPassword;
};

View File

@ -0,0 +1,13 @@
#pragma once
struct GS_ConnectionInfo
{
bool flagBreak;
char ip[32];
unsigned int sock;
GameServerTable *gst;
RSA *rsaKey;
unsigned char bfKey[64];
unsigned int bfKeyLen;
GameServerInfo *gsi;
};

View File

@ -0,0 +1,241 @@
#include "pch.h"
#include "GameServerInfo.h"
#include "GameServerTable.h"
#include "GS_ConnectionInfo.h"
#include "l2c_utils.h"
GameServerInfo::GameServerInfo( int id, const char *hexid )
{
m_Id = id;
if( hexid ) strcpy( m_hexId, hexid );
m_isAuthed = false;
m_status = STATUS_DOWN;
m_internalIp[0] = 0;
m_externalIp[0] = 0;
m_externalHost[0] = 0;
m_port = 0;
m_isPvp = false;
m_isTestServer = false;
m_isShowingClock = false;
m_isShowingBrackets = false;
m_maxPlayers = 0;
m_pthread = NULL;
//
m_accountsOnGS.clear();
}
GameServerInfo::~GameServerInfo()
{
//
m_Id = 0;
m_hexId[0] = 0;
m_isAuthed = false;
m_status = STATUS_DOWN;
m_internalIp[0] = 0;
m_externalIp[0] = 0;
m_externalHost[0] = 0;
m_port = 0;
m_isPvp = false;
m_isTestServer = false;
m_isShowingClock = false;
m_isShowingBrackets = false;
m_maxPlayers = 0;
//
stopThread();
m_accountsOnGS.clear();
}
int GameServerInfo::getId() const
{
return m_Id;
}
const char *GameServerInfo::getHexId() const
{
return m_hexId;
}
bool GameServerInfo::isAuthed() const
{
return m_isAuthed;
}
GameServerInfo::ServerStatus GameServerInfo::getStatus() const
{
return m_status;
}
const char *GameServerInfo::getInternalIp() const
{
return m_internalIp;
}
const char *GameServerInfo::getExternalIp() const
{
return m_externalIp;
}
const char *GameServerInfo::getExternalHost() const
{
return m_externalHost;
}
int GameServerInfo::getPort() const
{
return m_port;
}
bool GameServerInfo::isPvP() const
{
return m_isPvp;
}
bool GameServerInfo::isTestServer() const
{
return m_isTestServer;
}
bool GameServerInfo::isShowingClock() const
{
return m_isShowingClock;
}
bool GameServerInfo::isShowingBrackets() const
{
return m_isShowingBrackets;
}
int GameServerInfo::getMaxPlayers() const
{
return m_maxPlayers;
}
void GameServerInfo::setAuthed( bool a )
{
m_isAuthed = a;
}
void GameServerInfo::setGameServerInfo( const char *ip, const wchar_t *internalHost, const wchar_t *externalHost, int port, int maxPlayers )
{
// TODO: better address conversion
l2c_unicode_to_ansi( internalHost, m_internalIp, sizeof(m_internalIp) );
l2c_unicode_to_ansi( externalHost, m_externalIp, sizeof(m_externalIp) );
if( m_internalIp[0] == '*' ) strcpy( m_internalIp, ip );
if( m_externalIp[0] == '*' ) strcpy( m_externalIp, ip );
m_port = port;
m_maxPlayers = maxPlayers;
}
void GameServerInfo::setStatus( GameServerInfo::ServerStatus st )
{
m_status = st;
}
void GameServerInfo::getStatusStr( GameServerInfo::ServerStatus st, std::wstring& ws )
{
ws = L"STATUS_UNKNOWN";
switch( st )
{
case STATUS_AUTO: ws = L"STATUS_AUTO"; break;
case STATUS_GOOD: ws = L"STATUS_GOOD"; break;
case STATUS_NORMAL: ws = L"STATUS_NORMAL"; break;
case STATUS_FULL: ws = L"STATUS_FULL"; break;
case STATUS_DOWN: ws = L"STATUS_DOWN"; break;
case STATUS_GM_ONLY: ws = L"STATUS_GM_ONLY"; break;
}
}
void GameServerInfo::setShowingClock( bool b )
{
m_isShowingClock = b;
}
void GameServerInfo::setShowingBrackets( bool b )
{
m_isShowingBrackets = b;
}
void GameServerInfo::setTestServer( bool b )
{
m_isTestServer = b;
}
void GameServerInfo::setMaxPlayers( int m )
{
m_maxPlayers = m;
}
void GameServerInfo::setGameServerThread( void *pv_GS_ConnectionInfo )
{
m_pthread = pv_GS_ConnectionInfo;
}
void GameServerInfo::setDown()
{
m_isAuthed = false;
m_status = STATUS_DOWN;
m_port = 0;
m_pthread = NULL;
// not change IPs and hosts?
}
////////////////////////////////////////////////////////////////
bool GameServerInfo::stopThread()
{
if( m_pthread )
{
GS_ConnectionInfo *pinfo = (GS_ConnectionInfo *)m_pthread;
//
pinfo->flagBreak = true;
Sleep( 100 ); // O_o xDDD
//
m_pthread = NULL;
}
return true;
}
void GameServerInfo::kickPlayer( const char *account )
{
if( !account ) return;
if( m_pthread )
{
GS_ConnectionInfo *pinfo = (GS_ConnectionInfo *)m_pthread;
wchar_t waccount[256];
memset( waccount, 0, sizeof(waccount) );
l2c_ansi_to_unicode( account, waccount, 256 );
GameServerTable::send_KickPlayer( pinfo, waccount );
}
}
int GameServerInfo::getPlayerCount() const
{
return m_accountsOnGS.size();
}
bool GameServerInfo::hasAccountOnGS( const char *account ) const
{
if( !account ) return false;
std::string a = account;
std::set<std::string>::const_iterator iter = m_accountsOnGS.find( a );
if( iter == m_accountsOnGS.end() ) return false;
return true;
}
void GameServerInfo::addAccountOnGS( const char *account )
{
if( !account ) return;
std::string a = account;
m_accountsOnGS.insert( a );
}
void GameServerInfo::removeAccountFromGS( const char *account )
{
if( !account ) return;
std::string a = account;
std::set<std::string>::iterator iter = m_accountsOnGS.find( a );
if( iter == m_accountsOnGS.end() ) return;
m_accountsOnGS.erase( iter );
}

View File

@ -0,0 +1,71 @@
#pragma once
class GameServerInfo
{
public:
enum ServerStatus
{
STATUS_AUTO = 0x00,
STATUS_GOOD = 0x01,
STATUS_NORMAL = 0x02,
STATUS_FULL = 0x03,
STATUS_DOWN = 0x04,
STATUS_GM_ONLY = 0x05
};
public:
GameServerInfo( int id, const char *hexid );
~GameServerInfo();
public:
int getId() const;
const char *getHexId() const;
bool isAuthed() const;
ServerStatus getStatus() const;
const char *getInternalIp() const;
const char *getExternalIp() const;
const char *getExternalHost() const;
int getPort() const;
bool isPvP() const;
bool isTestServer() const;
bool isShowingClock() const;
bool isShowingBrackets() const;
int getMaxPlayers() const;
public:
void setAuthed( bool a );
void setGameServerInfo( const char *ip, const wchar_t *internalHost, const wchar_t *externalHost, int port, int maxPlayers );
void setStatus( GameServerInfo::ServerStatus st );
static void getStatusStr( GameServerInfo::ServerStatus st, std::wstring& ws );
void setShowingClock( bool b );
void setShowingBrackets( bool b );
void setTestServer( bool b );
void setMaxPlayers( int m );
void setGameServerThread( void *pv_GS_ConnectionInfo );
void setDown();
public:
void kickPlayer( const char *account );
bool stopThread();
int getPlayerCount() const;
bool hasAccountOnGS( const char *account ) const;
void addAccountOnGS( const char *account );
void removeAccountFromGS( const char *account );
protected:
int m_Id;
char m_hexId[64];
bool m_isAuthed;
ServerStatus m_status;
char m_internalIp[16];
char m_externalIp[16];
char m_externalHost[128];
int m_port;
bool m_isPvp;
bool m_isTestServer;
bool m_isShowingClock;
bool m_isShowingBrackets;
int m_maxPlayers;
std::set<std::string> m_accountsOnGS;
void *m_pthread;
};

View File

@ -0,0 +1,904 @@
#include "pch.h"
#include "Log.h"
#include "GameServerTable.h"
#include "LoginServer.h"
GameServerTable::GameServerTable( LoginServer *pLogin )
{
m_loginServer = pLogin;
m_serverNames.clear();
m_gameServerTable.clear();
int i;
for( i=0; i<MAX_RSA_KEYS; i++ ) m_rsa_keys[i] = NULL;
InitializeCriticalSectionAndSpinCount( &m_lock, 10 );
}
GameServerTable::~GameServerTable()
{
cleanup();
DeleteCriticalSection( &m_lock );
}
void GameServerTable::cleanup()
{
// delete() every gameserver info
std::map<int, GameServerInfo *>::iterator iter = m_gameServerTable.begin();
while( iter != m_gameServerTable.end() )
{
GameServerInfo *gsi = iter->second;
if( gsi )
{
gsi->stopThread();
delete gsi;
iter->second = NULL;
}
iter++;
}
//
m_serverNames.clear();
m_gameServerTable.clear();
int i;
for( i=0; i<MAX_RSA_KEYS; i++ )
{
if( m_rsa_keys[i] ) RSA_free( m_rsa_keys[i] );
m_rsa_keys[i] = NULL;
}
}
const LoginConfig *GameServerTable::getConfig() const
{
return m_loginServer->getConfig();
}
bool GameServerTable::init()
{
Log( L"Loading GameServerTable..." );
loadServerNames();
Log( L"Loaded %d server names.", m_serverNames.size() );
loadRegisteredGameServers();
Log( L"Loaded %d registered game servers.", m_gameServerTable.size() );
loadRSAKeys();
return true;
}
RSA *GameServerTable::getRandomRSAKey()
{
srand( (unsigned int)time(NULL) );
int idx = rand() % MAX_RSA_KEYS;
RSA *key_ret = RSAPrivateKey_dup( m_rsa_keys[idx] );
return key_ret;
}
void GameServerTable::loadServerNames()
{
FILE *f = _wfopen( L".\\config_login\\servername.xml", L"rt" );
if( !f )
{
LogError( L"GameServerTable: cannot open file 'config_login\\servername.xml'!" );
return;
}
//
wchar_t line[1024];
while( !feof( f ) )
{
line[0] = 0;
fgetws( line, 1023, f );
if( line[0] == 0 ) continue;
if( wcsstr( line, L"<server id=\"" ) && wcsstr( line, L"name=\"" ) )
{
wchar_t *pServerId = wcsstr( line, L" id=\"" );
pServerId += 5; // wcslen( L" id=\"" );
int iServerId = 0;
swscanf( pServerId, L"%d", &iServerId );
wchar_t *pServerName = wcsstr( line, L"name=\"" );
wchar_t wServerName[256];
wchar_t *pName = wServerName;
pServerName += 6; // wcslen( L"name=\"" );
while( (*pServerName) != L'\"' ) *pName++ = *pServerName++;
*pName = 0;
// add
m_serverNames[iServerId] = wServerName;
}
}
//
fclose( f );
}
void GameServerTable::loadRSAKeys()
{
wchar_t mes[256];
wsprintfW( mes, L"Generating %d RSA keys for GS communication... [", MAX_RSA_KEYS );
LogFull( true, false, RGB(0,0,0), RGB(255,255,255), mes );
int i;
for( i=0; i<MAX_RSA_KEYS; i++ )
{
m_rsa_keys[i] = RSA_generate_key( RSA_NUM_BITS, RSA_PUBLIC_EXPONENT, NULL, NULL );
Log_np( L"|" );
if( m_rsa_keys[i] == NULL )
{
Log_np( L"\n" );
LogError( L"ERROR: RSA_generate_key() returned NULL!\n" );
//ERR_print_errors_fp( stderr );
}
}
Log_np( L"] OK\n" );
}
bool GameServerTable::getServerNameById( int id, std::wstring& name )
{
std::map<int, std::wstring>::const_iterator iter = m_serverNames.find( id );
if( iter == m_serverNames.end() )
{
name = L"<unknown>";
return false;
}
name = iter->second;
return true;
}
void GameServerTable::loadRegisteredGameServers()
{
MysqlConnection *con = m_loginServer->getDBConnection();
if( !con )
{
LogError( L"GameServerTable: cannot load reg. game servers (no db connection)." );
return;
}
MysqlQuery q;
q.create( L"SELECT server_id, hexid FROM gameservers" );
if( con->executeQuery( q ) )
{
while( q.getNextRow() )
{
int sid = q.getFieldInt( "server_id" );
char *shexid = q.getFieldStr( "hexid" );
GameServerInfo *srv_info = new GameServerInfo( sid, shexid );
// save
m_gameServerTable[sid] = srv_info;
}
}
else
{
LogError( L"GameServerTable::loadRegisteredGameServers(): Mysql error: %s", con->getErrorStr() );
}
m_loginServer->releaseDBConnection( con );
}
GameServerInfo *GameServerTable::getRegisteredGameServerById( int id )
{
std::map<int, GameServerInfo *>::const_iterator iter = m_gameServerTable.find( id );
if( iter == m_gameServerTable.end() ) return NULL;
return iter->second;
}
bool GameServerTable::hasRegisteredGameServerOnId( int id )
{
std::map<int, GameServerInfo *>::const_iterator iter = m_gameServerTable.find( id );
if( iter == m_gameServerTable.end() ) return false;
return true;
}
bool GameServerTable::registerServer( int id, GameServerInfo *gsinfo )
{
if( !hasRegisteredGameServerOnId( id ) )
{
m_gameServerTable[id] = gsinfo;
return true;
}
return false;
}
bool GameServerTable::registerServerInDb( GameServerInfo *gsinfo )
{
return this->registerServerInDb( gsinfo->getId(), gsinfo->getHexId(), gsinfo->getExternalHost() );
}
bool GameServerTable::registerServerInDb( int id, const char *hexid, const char *externalHost )
{
MysqlConnection *con = m_loginServer->getDBConnection();
if( !con ) return false;
char *escape_hexId = con->escapeStringMalloc( hexid );
char *escape_hn = con->escapeStringMalloc( externalHost );
MysqlQuery q;
q.create( L"INSERT INTO gameservers (hexid, server_id, host) VALUES ('%s','%s','%s')",
escape_hexId, id, escape_hn );
free( escape_hexId );
free( escape_hn );
if( !con->executeQuery( q ) )
LogError( L"GameServerTable: failed to register game server in DB: %s", con->getErrorStr() );
else
Log( L"GameServerTable: registered new GameServer id %d", id );
m_loginServer->releaseDBConnection( con );
return true;
}
bool GameServerTable::registerWithFirstAvaliableId( GameServerInfo *gsinfo )
{
if( !gsinfo ) return false;
bool ret = false;
Lock();
{
int i;
for( i=0; i<(int)m_serverNames.size(); i++ )
{
if( !hasRegisteredGameServerOnId( i ) )
{
ret = registerServer( i, gsinfo );
break;
}
}
}
Unlock();
return ret;
}
const std::map<int, GameServerInfo *>& GameServerTable::getRegisteredGameServers() const
{
return this->m_gameServerTable;
}
bool GameServerTable::isAccountInAnyGameServer( const char *account )
{
if( !account ) return false;
std::map<int, GameServerInfo *>::const_iterator iter = m_gameServerTable.begin();
while( iter != m_gameServerTable.end() )
{
if( iter->second->hasAccountOnGS( account ) )
{
if( getConfig()->Debug )
LogDebug( L"Account %S is already on GS #%d", account, iter->first );
return true;
}
iter++;
}
return false;
}
GameServerInfo *GameServerTable::getGameServerThreadForAccount( const char *account )
{
if( !account ) return false;
std::map<int, GameServerInfo *>::const_iterator iter = m_gameServerTable.begin();
while( iter != m_gameServerTable.end() )
{
if( iter->second->hasAccountOnGS( account ) )
{
return iter->second;
}
iter++;
}
//throw std::exception( "GameServerTable::getGameServerThreadForAccount(): returning NULL, while shouldn't" );
return NULL;
}
bool GameServerTable::isLoginPossible( int serverId, int accesslevel, const char *account, int lastServer )
{
GameServerInfo *gsi = getRegisteredGameServerById( serverId );
if( !gsi ) return false;
if( !gsi->isAuthed() ) return false;
// check GM only
if( gsi->getStatus() == GameServerInfo::STATUS_GM_ONLY )
{
if( accesslevel <= 0 ) return false;
}
// check max players
if( gsi->getPlayerCount() >= gsi->getMaxPlayers() ) return false;
// login is possible... update last server in DB if needed
if( serverId != lastServer )
{
MysqlConnection *con = m_loginServer->getDBConnection();
MysqlQuery q;
char *e_acc = con->escapeStringMalloc( account );
q.create( L"UPDATE accounts SET lastServer=%d WHERE login='%S'", serverId, e_acc );
free( e_acc );
if( !con->executeQuery( q ) )
LogError( L"Error updating accounts (lastServer): %s", con->getErrorStr() );
m_loginServer->releaseDBConnection( con );
}
return true;
}
void GameServerTable::Lock()
{
EnterCriticalSection( &m_lock );
}
void GameServerTable::Unlock()
{
LeaveCriticalSection( &m_lock );
}
#include "GS_ConnectionInfo.h"
void GameServerTable::beginProcessGameServerClient( const char *ip, unsigned int sock )
{
GS_ConnectionInfo *pinfo = new GS_ConnectionInfo();
pinfo->sock = sock;
strcpy( pinfo->ip, ip );
pinfo->gst = this;
pinfo->flagBreak = false;
pinfo->bfKeyLen = 0;
pinfo->bfKey[0] = 0;
pinfo->rsaKey = NULL;
pinfo->gsi = NULL;
unsigned int tid = 0;
HANDLE hThread = (HANDLE)_beginthreadex( NULL, 0,
(unsigned int (__stdcall *)(void *))GSConnThread, (void *)pinfo, 0, &tid );
if( hThread ) CloseHandle( hThread );
}
DWORD WINAPI GameServerTable::GSConnThread( LPVOID lpParam )
{
GS_ConnectionInfo *pinfo = (GS_ConnectionInfo *)lpParam;
pinfo->rsaKey = pinfo->gst->getRandomRSAKey();
unsigned char rsa_modulus[64];
BN_bn2bin( pinfo->rsaKey->n, rsa_modulus );
char initial_bfKey[] = "_;v.]05-31!|+-%xT!^[$\00";
memcpy( pinfo->bfKey, initial_bfKey, 23 );
pinfo->bfKey[22] = '0';
pinfo->bfKeyLen = 22;
// send InitLS to server
L2LoginPacket *pack = new L2LoginPacket();
pack->setPacketType( 0x00 ); // InitLS
pack->writeUInt( LoginServer::LOGIN_PROTOCOL_REV ); // login protocol rev
pack->writeUInt( 64+1 ); // why +1?
pack->writeC( 0x00 ); // ?? wtf
pack->writeBytes( rsa_modulus, 64 );
// pad
pack->writeUShort( 0x00 );
sendGSPacket( pinfo, pack );
delete pack;
pack = NULL;
#ifdef _DEBUG
LogDebug( L"GSConnThread: sent InitLS" );
#endif
unsigned int rcvdLen = 0;
int r;
unsigned char *packbuffer = (unsigned char *)malloc( 16384 ); // 16 kb
while( !pinfo->flagBreak )
{
// first run select()
int bReadyRead = 0;
r = L2PNet_select( pinfo->sock, L2PNET_SELECT_READ, 100, &bReadyRead, NULL );
if( r == 0 || !bReadyRead ) continue; // select() timeout, nothing was received
if( r < 0 ) // select() error
{
LogError( L"L2PNet_select() returned %d!", r );
pinfo->flagBreak = true;
break;
}
if( pinfo->flagBreak ) break;
// select OK, now recv
r = L2PacketReceive_buffer( pinfo->sock, 5000, &rcvdLen, packbuffer );
if( r <= 0 )
{
LogError( L"L2PacketReceive_buffer() returned %d!", r );
pinfo->flagBreak = true;
break;
}
if( pinfo->flagBreak ) break;
// create packet
pack = new L2LoginPacket( packbuffer, rcvdLen );
// decrypt blowfish
pack->setDynamicBFKey( pinfo->bfKey, pinfo->bfKeyLen );
pack->decodeBlowfish( false );
// verify received packet checksum?
if( !pack->verifyChecksum() )
{
LogError( L"Incorrect checksum from GameServer %S. Close connection", pinfo->ip );
forceClose( pinfo, NOT_AUTHED );
continue;
}
//
unsigned char opcode = pack->getByteAt( 2 );
//
#ifdef _DEBUG
LogDebug( L"GSConnThread: opcode %02X", (unsigned int)opcode );
#endif
//
switch( opcode )
{
case 00:
ph_ReceiveBlowfishKey( pinfo, pack );
break;
case 01:
ph_ReceiveGameServerAuth( pinfo, pack );
break;
case 02:
ph_ReceivePlayerInGame( pinfo, pack );
break;
case 03:
ph_ReceivePlayerLogOut( pinfo, pack );
break;
case 04:
ph_ReceiveChangeAccessLevel( pinfo, pack );
break;
case 05:
ph_ReceivePlayerAuthRequest( pinfo, pack );
break;
case 06:
ph_ReceiveServerStatus( pinfo, pack );
break;
default:
LogWarning( L"Unknown Opcode (0x%02X) from GameServer, closing connection.", (unsigned int)opcode );
forceClose( pinfo, NOT_AUTHED );
pinfo->flagBreak = true; // close con
break;
}
}
free( packbuffer );
if( pinfo->gsi )
{
// log
std::wstring wsname;
pinfo->gst->getServerNameById( pinfo->gsi->getId(), wsname );
Log( L"Connection with gameserver %d (%s) lost: set as disconnected",
pinfo->gsi->getId(), wsname.c_str() );
// set as disconnected
pinfo->gsi->setDown();
}
L2PNet_shutdown( pinfo->sock );
L2PNet_closesocket( pinfo->sock );
delete pinfo;
return 0;
}
void GameServerTable::ph_ReceiveBlowfishKey( GS_ConnectionInfo *pinfo, L2LoginPacket *pack )
{
pack->getPacketType();
unsigned char encoded_block[128];
unsigned char decoded_block[128];
memset( encoded_block, 0, sizeof(encoded_block) );
memset( decoded_block, 0, sizeof(decoded_block) );
// read size of RSA-encrypted block with BF key
unsigned int block_size = pack->readUInt();
if( block_size > 128 )
{
LogError( L"GS: protocol error (block_size)" );
forceClose( pinfo, NOT_AUTHED );
return;
}
// read block
pack->readBytes( encoded_block, block_size );
// decrypt RSA
int r = RSA_private_decrypt( block_size, encoded_block, decoded_block, pinfo->rsaKey, RSA_NO_PADDING );
if( r == -1 )
{
LogError( L"GameServerTable::ph_ReceiveBlowfishKey(): RSA_private_decrypt() error!" );
forceClose( pinfo, NOT_AUTHED );
return;
}
// there are nulls before the key we must remove them
unsigned char new_bf_key[128];
memset( new_bf_key, 0, sizeof(new_bf_key) );
unsigned int new_bf_key_len = 0;
unsigned int zero_count = 0;
while( (decoded_block[zero_count] == 0) && (zero_count < block_size) ) zero_count++;
new_bf_key_len = block_size - zero_count;
if( new_bf_key_len == 0 )
{
LogError( L"GS: protocol error (new_bf_key_len)" );
forceClose( pinfo, NOT_AUTHED );
return;
}
// set new key
memcpy( pinfo->bfKey, decoded_block + zero_count, new_bf_key_len );
pinfo->bfKeyLen = new_bf_key_len;
//
if( pinfo->gst->getConfig()->Debug ) LogDebug( L"GS: new BF key rcvd (len %u)", new_bf_key_len );
}
void GameServerTable::ph_ReceiveGameServerAuth( GS_ConnectionInfo *pinfo, L2LoginPacket *pack )
{
unsigned char hexid[32];
char s_hexid[64];
memset( hexid, 0, sizeof(hexid) );
memset( s_hexid, 0, sizeof(s_hexid) );
//
pack->getPacketType();
unsigned char desiredId = pack->readUChar();
bool acceptAlternativeId = (pack->readC() == 0 ? false : true);
bool hostReserved = (pack->readC() == 0 ? false : true);
wchar_t *externalHost = pack->readS();
wchar_t *internalHost = pack->readS();
int port = (int)pack->readH();
int maxPlayers = pack->readD();
int hexid_size = pack->readD();
if( hexid_size > 32 ) hexid_size = 32;
pack->readBytes( hexid, hexid_size );
int i;
for( i=0; i<hexid_size; i++ )
{
sprintf( s_hexid + i*2, "%02x", hexid[i] );
}
if( hostReserved ) LogWarning( L"GS %d auth: host is reserved. WTF is this?", (int)desiredId );
if( pinfo->gst->getConfig()->Debug )
LogDebug( L"Auth request for GS #%d (%S)", (int)desiredId, s_hexid );
// -3c9043fed79c281b9c9daaeb027bd8f0
//Returns a byte array containing the two's-complement representation of this BigInteger.
//The byte array will be in big-endian byte-order: the most significant byte is in the zeroth
//element. The array will contain the minimum number of bytes required to represent this BigInteger,
//including at least one sign bit, which is (ceil((this.bitLength() + 1)/8)).
//(This representation is compatible with the (byte[]) constructor.)
// ... big-endian byte-order: the most significant byte is in the zeroth element.
// handle auth process
bool auth_success = false;
GameServerTable *gst = pinfo->gst;
GameServerInfo *gsi = gst->getRegisteredGameServerById( desiredId );
// this id is registered?..
if( gsi == NULL ) // ...no
{
// can we register new GS on this id?
if( gst->getConfig()->AcceptNewGameServer )
{
gsi = new GameServerInfo( desiredId, s_hexid );
if( gst->registerServer( desiredId, gsi ) )
{
// successfully registered new game server
attachGameServerInfo( pinfo, gsi, internalHost, externalHost, port, maxPlayers );
gst->registerServerInDb( gsi );
auth_success = true;
}
else // some one took this ID meanwhile
{
delete gsi; gsi = NULL;
forceClose( pinfo, REASON_ID_RESERVED );
}
}
else // cannot register new GS
{
forceClose( pinfo, REASON_WRONG_HEXID );
}
}
else
{
if( strcmp( gsi->getHexId(), s_hexid ) == 0 )
{
gst->Lock();
{
if( gsi->isAuthed() )
{
forceClose( pinfo, REASON_ALREADY_LOGGED_IN );
}
else
{
attachGameServerInfo( pinfo, gsi, internalHost, externalHost, port, maxPlayers );
auth_success = true;
}
}
gst->Unlock();
}
else
{
// there is already a server registered with the desired id and different hex id
// try to register this one with an alternative id
if( gst->getConfig()->AcceptNewGameServer && acceptAlternativeId )
{
gsi = new GameServerInfo( desiredId, s_hexid );
if( gst->registerWithFirstAvaliableId( gsi ) )
{
attachGameServerInfo( pinfo, gsi, internalHost, externalHost, port, maxPlayers );
gst->registerServerInDb( gsi );
auth_success = true;
}
else // failed to register?
{
delete gsi; gsi = NULL;
forceClose( pinfo, REASON_NO_FREE_ID );
}
}
else // server id is already taken, and we cant get a new one for you
{
forceClose( pinfo, REASON_WRONG_HEXID );
}
}
}
free( internalHost );
free( externalHost );
// authorization failed for some reason?
if( !auth_success ) return;
std::wstring wserverName;
gst->getServerNameById( (int)desiredId, wserverName );
Log( L"Updated gameserver %d %s IPs:", (int)desiredId, wserverName.c_str() );
Log( L"Internal IP: %S; External IP: %S", gsi->getInternalIp(), gsi->getExternalIp() );
// send AuthResponse
L2LoginPacket p_ar;
p_ar.setPacketType( 0x02 );
p_ar.writeUChar( desiredId );
p_ar.writeS( wserverName.c_str() );
sendGSPacket( pinfo, &p_ar );
}
void GameServerTable::ph_ReceivePlayerInGame( GS_ConnectionInfo *pinfo, L2LoginPacket *pack )
{
pack->getPacketType();
int acc_count = pack->readH();
int i;
for( i=0; i<acc_count; i++ )
{
const wchar_t *acc = pack->readUnicodeStringPtr();
if( !acc ) continue;
char s_acc[256];
memset( s_acc, 0, sizeof(s_acc) );
l2c_unicode_to_ansi( acc, s_acc, 256 );
// add account to game server list of accounts
pinfo->gsi->addAccountOnGS( s_acc );
if( pinfo->gst->getConfig()->Debug )
LogDebug( L"Account %s logged into gameserver %d", acc, pinfo->gsi->getId() );
}
}
void GameServerTable::ph_ReceivePlayerLogOut( GS_ConnectionInfo *pinfo, L2LoginPacket *pack )
{
pack->getPacketType();
const wchar_t *acc = pack->readUnicodeStringPtr();
if( !acc ) return;
char s_acc[256];
memset( s_acc, 0, sizeof(s_acc) );
l2c_unicode_to_ansi( acc, s_acc, 256 );
// remove account from gs list of accounts
pinfo->gsi->removeAccountFromGS( s_acc );
if( pinfo->gst->getConfig()->Debug )
LogDebug( L"Account %s logged out from gameserver %d", acc, pinfo->gsi->getId() );
}
void GameServerTable::ph_ReceiveChangeAccessLevel( GS_ConnectionInfo *pinfo, L2LoginPacket *pack )
{
pack->getPacketType();
int acc_level = pack->readD();
const wchar_t *account = pack->readUnicodeStringPtr();
//
pinfo->gst->m_loginServer->getLoginController()->setAccountAccessLevel( account, acc_level );
if( pinfo->gst->getConfig()->Debug )
LogDebug( L"Changed %s access level to %d", account, acc_level );
}
void GameServerTable::ph_ReceivePlayerAuthRequest( GS_ConnectionInfo *pinfo, L2LoginPacket *pack )
{
pack->getPacketType();
const wchar_t *waccount = pack->readUnicodeStringPtr();
if( !waccount )
{
LogError( L"GS: ReceivePlayerAuthRequest: account NULL?" );
return;
}
// convert to ansi
char account[256] = {0};
memset( account, 0, sizeof(account) );
l2c_unicode_to_ansi( waccount, account, 256 );
// read keys
unsigned char playKey[8];
unsigned char loginKey[8];
pack->readBytes( playKey, 8 );
pack->readBytes( loginKey, 8 );
//
// get keys for account
unsigned char player_playKey[8];
unsigned char player_loginKey[8];
bool ok = false;
if( pinfo->gst->m_loginServer->getLoginController()->getSessionKeysForAccount( account, player_loginKey, player_playKey ) )
{
bool key1_ok = (memcmp( loginKey, player_loginKey, 8 ) == 0);
bool key2_ok = (memcmp( playKey, player_playKey, 8 ) == 0);
ok = key1_ok && key2_ok;
}
else LogError( L"getSessionKeysForAccount( %s ) failed!", account );
if( ok )
{
pinfo->gst->m_loginServer->getLoginController()->removeAuthedGSClient( account );
if( pinfo->gst->getConfig()->Debug )
LogDebug( L"PlayerAuthRequest for account %s OK", waccount );
GameServerTable::send_PlayerAuthResponse( pinfo, waccount, true );
}
else
{
if( pinfo->gst->getConfig()->Debug )
LogWarning( L"PlayerAuthRequest for account %s failed!", waccount );
GameServerTable::send_PlayerAuthResponse( pinfo, waccount, false );
}
}
void GameServerTable::ph_ReceiveServerStatus( GS_ConnectionInfo *pinfo, L2LoginPacket *pack )
{
if( pinfo->gsi && pinfo->gsi->isAuthed() )
{
if( pinfo->gst->getConfig()->Debug )
LogDebug( L"ServerStatus received (id %d)", pinfo->gsi->getId() );
// parse ServerStatus
pack->getPacketType();
int paramCount = pack->readD();
int i;
for( i=0; i<paramCount; i++ )
{
int valueType = pack->readD();
int valueValue = pack->readD();
switch( valueType )
{
case 0x01: // SERVER_LIST_STATUS
pinfo->gsi->setStatus( (GameServerInfo::ServerStatus)valueValue );
if( pinfo->gst->getConfig()->Debug )
{
std::wstring ws;
GameServerInfo::getStatusStr( pinfo->gsi->getStatus(), ws );
LogDebug( L"SERVER_LIST_STATUS: %s", ws.c_str() );
}
break;
case 0x02: // SERVER_LIST_CLOCK
pinfo->gsi->setShowingClock( valueValue != 0x00 );
if( pinfo->gst->getConfig()->Debug )
LogDebug( L"SERVER_LIST_CLOCK: %S", (valueValue == 0x00 ? "false" : "true") );
break;
case 0x03: // SERVER_LIST_SQUARE_BRACKET
pinfo->gsi->setShowingBrackets( valueValue != 0x00 );
if( pinfo->gst->getConfig()->Debug )
LogDebug( L"SERVER_LIST_SQUARE_BRACKET: %S", (valueValue == 0x00 ? "false" : "true") );
break;
case 0x04: // SERVER_LIST_MAX_PLAYERS
pinfo->gsi->setMaxPlayers( valueValue );
if( pinfo->gst->getConfig()->Debug )
LogDebug( L"SERVER_LIST_MAX_PLAYERS: %d", valueValue );
break;
case 0x05: // SERVER_LIST_TEST_SERVER
pinfo->gsi->setTestServer( valueValue != 0x00 );
if( pinfo->gst->getConfig()->Debug )
LogDebug( L"SERVER_LIST_TEST_SERVER: %S", (valueValue == 0x00 ? "false" : "true") );
break;
}
}
}
else
{
forceClose( pinfo, NOT_AUTHED );
}
}
bool GameServerTable::sendGSPacket( GS_ConnectionInfo *pinfo, L2LoginPacket *pack )
{
// pad packet to pad_step-byte border
const int pad_step = 8;
int dsize = pack->getDataSize();
int rest = pad_step - (dsize % pad_step);
if( rest > 0 && rest < pad_step )
{
//LogDebug( L"Need padding %d bytes to %d byte-border", rest, pad_step );
int i;
for( i=0; i<rest; i++ ) pack->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
pack->writeD( 0x00000000 );
// finally, checksum (4 bytes)
pack->appendChecksum( false );
//
//FILE *f = fopen( "out_packets.log", "at" );
//pack->dumpToFile( f );
//fclose( f );
//
pack->setDynamicBFKey( pinfo->bfKey, pinfo->bfKeyLen );
pack->encodeBlowfish( false );
unsigned int sentLen = 0;
int r = L2PacketSend( pinfo->sock, pack, 5000, &sentLen );
if( r <= 0 || (sentLen != pack->getPacketSize()) ) return false;
return true;
}
void GameServerTable::forceClose( GS_ConnectionInfo *pinfo, GameServerTable::LoginFailReason reason )
{
send_LoginServerFail( pinfo, reason );
pinfo->flagBreak = true;
}
void GameServerTable::attachGameServerInfo( GS_ConnectionInfo *pinfo, GameServerInfo *gsi, const wchar_t *internalHost, const wchar_t *externalHost, int port, int maxPlayers )
{
pinfo->gsi = gsi;
gsi->setGameServerInfo( pinfo->ip, internalHost, externalHost, port, maxPlayers );
gsi->setAuthed( true );
// GameServerInfo should know about game server thread
gsi->setGameServerThread( pinfo );
}
void GameServerTable::send_LoginServerFail( GS_ConnectionInfo *pinfo, GameServerTable::LoginFailReason reason )
{
L2LoginPacket p;
p.setPacketType( 0x01 );
p.writeUChar( (unsigned char)reason & 0xFF );
sendGSPacket( pinfo, &p );
}
void GameServerTable::send_KickPlayer( GS_ConnectionInfo *pinfo, const wchar_t *account )
{
if( !account ) return;
L2LoginPacket p;
p.setPacketType( 0x04 );
p.writeS( account );
sendGSPacket( pinfo, &p );
}
void GameServerTable::send_PlayerAuthResponse( GS_ConnectionInfo *pinfo, const wchar_t *account, bool ok )
{
if( !account ) return;
L2LoginPacket p;
p.setPacketType( 0x03 );
p.writeS( account );
p.writeC( ok ? 0x01 : 0x00 );
sendGSPacket( pinfo, &p );
}
/**
Received gameserver connection from: 127.0.0.1
Sending InitLS...
blowfishKey == null
Length of bf key could be 22
publickey.length: 65
[S] InitLS:
0000: 00 02 01 00 00 41 00 00 00 00 bc 6a b5 47 3b 14 .....A.....j.G;.
0010: 9b 03 3f 68 3a 0d d1 80 af ac 19 0e d7 a7 03 d8 ..?h:...........
0020: c4 c9 2a a0 c9 71 3b fb 3d 96 0b 21 05 6a 20 86 ..*..q;.=..!.j .
0030: d6 59 54 65 52 74 42 d8 49 86 69 54 29 16 0d a3 .YTeRtB.I.iT)...
0040: 40 8e 93 90 65 0e 38 43 62 35 00 00 fa 4d 91 25 @...e.8Cb5...M.%
Sending packet len 82...
[C]
0000: 00 40 00 00 00 30 84 19 d8 e9 c5 96 31 ff ce 9f .@...0......1...
0010: a2 90 97 a2 bc c8 2b 49 9e e0 84 08 bd f1 bd d3 ......+I........
0020: 6b 55 bd 14 8e df 91 24 23 34 3a 2b d1 26 d0 2b kU.....$#4:+.&.+
0030: 17 2c d8 70 03 fd b2 eb a1 68 64 1b 30 90 aa c7 .,.p.....hd.0...
0040: 02 54 94 7c de 00 00 00 00 00 00 00 9a ca fc 2b .T.|...........+
New BlowFish key received, Blowfih Engine initialized:
onReceiveBlowfishKey() new bf key len 40
[C]
0000: 01 01 01 00 2a 00 00 00 2a 00 00 00 61 1e 0a 00 ....*...*...a...
0010: 00 00 10 00 00 00 c3 6f bc 01 28 63 d7 e4 63 62 .......o..(c..cb
0020: 55 14 fd 84 27 10 00 00 00 00 00 00 79 fe 6e ea U...'.......y.n.
Auth request received
Updated Gameserver [1] Bartz IP's:
InternalIP: *
ExternalIP: *
[S] AuthResponse:
0000: 02 01 42 00 61 00 72 00 74 00 7a 00 00 00 00 00 ..B.a.r.t.z.....
0010: 00 00 00 00 17 01 4a 00 ......J.
Sending packet len 26...
Authed: id: 1
[C]
0000: 06 04 00 00 00 03 00 00 00 00 00 00 00 02 00 00 ................
0010: 00 00 00 00 00 05 00 00 00 00 00 00 00 01 00 00 ................
0020: 00 00 00 00 00 00 00 00 00 00 00 00 06 01 00 00 ................
ServerStatus received
**/

View File

@ -0,0 +1,88 @@
#pragma once
#include "GameServerInfo.h"
class LoginConfig;
class LoginServer;
struct GS_ConnectionInfo;
class GameServerTable
{
public:
static const int MAX_RSA_KEYS = 10;
static const int RSA_NUM_BITS = 512; // 1024 for L2 login clients, 512 for game servers
static const int RSA_PUBLIC_EXPONENT = 65537;
enum 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
};
public:
GameServerTable( LoginServer *pLogin );
~GameServerTable();
public:
bool init();
void cleanup();
RSA *getRandomRSAKey();
public:
bool getServerNameById( int id, std::wstring& name );
GameServerInfo *getRegisteredGameServerById( int id );
bool hasRegisteredGameServerOnId( int id );
bool registerServer( int id, GameServerInfo *gsinfo );
bool registerServerInDb( GameServerInfo *gsinfo );
bool registerServerInDb( int id, const char *hexid, const char *externalHost );
bool registerWithFirstAvaliableId( GameServerInfo *gsinfo );
public:
const std::map<int, GameServerInfo *>& getRegisteredGameServers() const;
bool isAccountInAnyGameServer( const char *account );
GameServerInfo *getGameServerThreadForAccount( const char *account );
bool isLoginPossible( int serverId, int accesslevel, const char *account, int lastServer );
public:
void beginProcessGameServerClient( const char *ip, unsigned int sock );
protected:
void loadServerNames();
void loadRSAKeys();
void loadRegisteredGameServers();
const LoginConfig *getConfig() const;
protected:
LoginServer *m_loginServer;
std::map<int, std::wstring> m_serverNames;
std::map<int, GameServerInfo *> m_gameServerTable;
RSA *m_rsa_keys[MAX_RSA_KEYS];
CRITICAL_SECTION m_lock;
protected:
void Lock();
void Unlock();
static DWORD WINAPI GSConnThread( LPVOID lpParam );
// GS packets handlers
static void ph_ReceiveBlowfishKey( GS_ConnectionInfo *pinfo, L2LoginPacket *pack );
static void ph_ReceiveGameServerAuth( GS_ConnectionInfo *pinfo, L2LoginPacket *pack );
static void ph_ReceivePlayerInGame( GS_ConnectionInfo *pinfo, L2LoginPacket *pack );
static void ph_ReceivePlayerLogOut( GS_ConnectionInfo *pinfo, L2LoginPacket *pack );
static void ph_ReceiveChangeAccessLevel( GS_ConnectionInfo *pinfo, L2LoginPacket *pack );
static void ph_ReceivePlayerAuthRequest( GS_ConnectionInfo *pinfo, L2LoginPacket *pack );
static void ph_ReceiveServerStatus( GS_ConnectionInfo *pinfo, L2LoginPacket *pack );
// for internal use
static void attachGameServerInfo( GS_ConnectionInfo *pinfo, GameServerInfo *gsi, const wchar_t *internalHost, const wchar_t *externalHost, int port, int maxPlayers );
static bool sendGSPacket( GS_ConnectionInfo *pinfo, L2LoginPacket *pack );
public:
static void forceClose( GS_ConnectionInfo *pinfo, GameServerTable::LoginFailReason reason );
static void send_LoginServerFail( GS_ConnectionInfo *pinfo, GameServerTable::LoginFailReason reason );
static void send_KickPlayer( GS_ConnectionInfo *pinfo, const wchar_t *account );
static void send_PlayerAuthResponse( GS_ConnectionInfo *pinfo, const wchar_t *account, bool ok );
};

BIN
L2C_Login/L2C.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

159
L2C_Login/L2Logind.cpp Normal file
View File

@ -0,0 +1,159 @@
#include "pch.h"
#include "Resource.h"
#include "Log.h"
#include "LoginServer.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;
LoginServer *g_server = NULL;
void Main_StartServer()
{
if( !g_server )
{
g_server = new LoginServer();
}
g_server->start();
}
void Main_StopServer()
{
if( g_server ) g_server->stop();
Log( L"Login server stopped." );
}
void Main_OnInitDialog( HWND hDlg )
{
#ifdef _DEBUG
SetWindowText( hDlg, TEXT("L2 Login 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:
{
if( g_server )
if( !g_server->start() ) LogError( L"Start failed." );
} break;
case 2:
{
if( g_server )
if( !g_server->stop() ) LogError( L"Stop failed." );
} 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 );
// 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;
}
}
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_SIZING: Main_OnSize( hDlg, wParam, lParam ); break; // invalid
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/loginserver.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;
}
// run dialog!
DialogBoxParam( hInstance, MAKEINTRESOURCE(IDD_MAIN), NULL, MainDlgProc, 0 );
// close server
Main_StopServer();
if( g_server ) delete g_server;
g_server = NULL;
// stop logging to file (close file)
Log_EnableLogToFile( false, NULL );
// cleanup winsock
L2PNet_Cleanup();
return 0;
}

43
L2C_Login/L2Logind.rc Normal file
View File

@ -0,0 +1,43 @@
#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", "L2Logind\0"
VALUE "FileVersion", "0, 0, 0, 9\0"
VALUE "InternalName", "L2Logind\0"
VALUE "LegalCopyright", "Public Domain (FreeForAll)\0"
VALUE "OriginalFilename", "L2Logind.exe\0"
VALUE "ProductName", "L2Logind\0"
VALUE "ProductVersion", "0, 0, 0, 1\0"
END
END
BLOCK "VarFileInfo"
BEGIN
VALUE "Translation", 0x0419, 0x04B0
END
END
// dialog
IDD_MAIN DIALOGEX 6,6,365,200
CAPTION "L2 Login 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
END

222
L2C_Login/Log.cpp Normal file
View File

@ -0,0 +1,222 @@
#include "pch.h"
#include "Log.h"
HWND g_hWndLog = NULL;
extern HINSTANCE g_hInst;
CRITICAL_SECTION g_cs_log;
FILE *g_log_file = NULL;
HWND LogWnd_Create( HWND hWndParent )
{
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
//
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 );
}
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 );
}

17
L2C_Login/Log.h Normal file
View File

@ -0,0 +1,17 @@
#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();

601
L2C_Login/LoginClient.cpp Normal file
View File

@ -0,0 +1,601 @@
#include "pch.h"
#include "LoginClient.h"
#include "Log.h"
#include "LoginServer.h"
const wchar_t *LoginClient::toString() const
{
return m_clientDesc;
}
const char *LoginClient::getIpAddress() const
{
return m_client_ip;
}
const char *LoginClient::getAccount() const
{
return m_account;
}
int LoginClient::getAccessLevel() const
{
return m_accessLevel;
}
void LoginClient::setAccessLevel( int a )
{
m_accessLevel = a;
}
int LoginClient::getLastServer() const
{
return m_lastServer;
}
void LoginClient::setLastServer( int s )
{
m_lastServer = s;
}
void LoginClient::setAccount( const char *account )
{
if( account ) strcpy( m_account, account );
else m_account[0] = 0;
}
bool LoginClient::isUsingInternalIp() const
{
return m_isUsingInternalIp;
}
bool LoginClient::hasJoinedGS( int *gs_id )
{
if( m_joinedGS )
{
if( gs_id ) (*gs_id) = m_joinedGSID;
return true;
}
return false;
}
void LoginClient::getSessionKeys( unsigned char *sessionKey1, unsigned char *sessionKey2 )
{
memcpy( sessionKey1, p_sessionKey1, 8 );
memcpy( sessionKey2, p_sessionKey2, 8 );
}
LoginClient::LoginClient()
{
m_loginServer = NULL;
m_sock = 0xFFFFFFFF;
m_client_ip[0] = 0;
m_client_port = 0;
m_running = false;
m_clientDesc[0] = 0;
m_state = NONE;
m_flagBreak = false;
p_rsaKey = NULL;
m_accessLevel = 0;
m_lastServer = 0;
m_account[0] = 0;
m_isUsingInternalIp = false;
m_joinedGS = false;
m_joinedGSID = 0;
}
LoginClient::LoginClient( const LoginClient& other )
{
UNREFERENCED_PARAMETER(other);
}
LoginClient::~LoginClient()
{
disconnect( true );
m_running = false;
m_clientDesc[0] = 0;
m_flagBreak = false;
}
LoginClient::LoginClient( LoginServer *pLoginServer, unsigned int sock, const char *clientIp, int clientPort )
{
m_loginServer = pLoginServer;
m_sock = sock;
strcpy( m_client_ip, clientIp );
m_client_port = clientPort;
m_running = false;
m_clientDesc[0] = 0;
swprintf( m_clientDesc, 127, L"[IP: %S:%d]", m_client_ip, m_client_port );
m_state = CONNECTED;
m_flagBreak = false;
m_accessLevel = 0;
m_lastServer = 0;
m_account[0] = 0;
// tests for internal IP
m_isUsingInternalIp = false;
if( (strstr( clientIp, "127." ) == clientIp) ||
(strstr( clientIp, "10.70." ) == clientIp) ||
(strstr( clientIp, "192.168." ) == clientIp) ||
(strstr( clientIp, "172.27." ) == clientIp) ) m_isUsingInternalIp = true;
}
void LoginClient::disconnect( bool force )
{
m_flagBreak = true;
if( !force )
{
while( m_running ) Sleep( 100 );
}
if( m_sock != INVALID_SOCKET )
{
L2PNet_shutdown( m_sock );
L2PNet_closesocket( m_sock );
m_sock = INVALID_SOCKET;
}
m_client_ip[0] = 0;
m_client_port = 0;
m_state = NONE;
m_account[0] = 0;
}
void LoginClient::beginProcessing()
{
unsigned int threadId = 0;
HANDLE hThread = (HANDLE)_beginthreadex( NULL, 0,
(unsigned int (__stdcall *)(void *))LoginClientThread, (void *)this,
0, &threadId );
if( hThread ) CloseHandle( hThread );
}
unsigned int __stdcall LoginClient::LoginClientThread( LoginClient *pcls )
{
pcls->m_running = true;
pcls->m_flagBreak = false;
unsigned int sock = pcls->m_sock;
unsigned int sentLen = 0, rcvdLen = 0;
int r;
// create RSA & BF keys
pcls->p_rsaKey = pcls->m_loginServer->getLoginController()->getRandomRSAKeyPair();
BN_bn2bin( pcls->p_rsaKey->n, pcls->p_rsa_pubkey_mod );
memcpy( pcls->p_bf_dyn_key, pcls->m_loginServer->getLoginController()->getRandomBlowfishKey(), 16 );
// generate sessionId :)
pcls->p_sessionId[0] = (unsigned char)( rand() & 0xFF );
pcls->p_sessionId[1] = (unsigned char)( rand() & 0xFF );
pcls->p_sessionId[2] = (unsigned char)( rand() & 0xFF );
pcls->p_sessionId[3] = (unsigned char)( rand() & 0xFF );
// cleanup sessionKeys
memset( pcls->p_sessionKey1, 0, sizeof(pcls->p_sessionKey1) );
memset( pcls->p_sessionKey2, 0, sizeof(pcls->p_sessionKey2) );
// send Init
L2Login_Init *pInit = new L2Login_Init();
memcpy( pInit->p_BF_dyn_key, pcls->p_bf_dyn_key, 16 );
memcpy( pInit->p_RSA_pubKeyMod, pcls->p_rsa_pubkey_mod, 128 );
pInit->p_protoVer = 0x0c621;
memcpy( pInit->p_sessionId, pcls->p_sessionId, 4 );
pInit->create( L2_VERSION_T23 );
pInit->encodeBlowfish( true );
r = L2PacketSend( sock, pInit, 5000, &sentLen );
//LogDebug( L"Client %s: sent %u bytes Init", pcls->toString(), sentLen );
unsigned char *packbuffer = (unsigned char *)malloc( 16384 ); // more than enough...
pcls->m_flagBreak = false;
while( !pcls->m_flagBreak )
{
// receive timeout - 15 seconds (should be enough)
r = L2PacketReceive_buffer( sock, LoginController::LOGIN_TIMEOUT, &rcvdLen, packbuffer );
if( r < 0 )
{
LogError( L"LoginClient %s recv error (%d)...", pcls->toString(), r );
pcls->m_flagBreak = true;
break;
}
if( r == 0 )
{
if( pcls->m_loginServer->getConfig()->Debug )
LogDebug( L"LoginClient %s: client closed connection.", pcls->toString() );
pcls->m_flagBreak = true;
break;
}
L2LoginPacket *pack = new L2LoginPacket( packbuffer, rcvdLen );
pack->decodeBlowfish( pcls->p_bf_dyn_key );
// packet received and decoded, now verify checksum
if( !pack->verifyChecksum() )
{
LogError( L"Incorrect checksum from client %s", pcls->toString() );
pcls->m_flagBreak = true;
continue;
}
//
unsigned int opcode = (unsigned int)pack->getByteAt( 2 );
#ifdef _DEBUG
LogDebug( L"Client: 0x%02X", opcode );
#endif
// parse opcode
switch( pcls->m_state )
{
case LoginClient::CONNECTED:
{
if( opcode == 0x07 ) // RequestGGAuth
pcls->ph_RequestGGAuth( pack );
else pcls->invalidPacket( pack );
} break;
case LoginClient::AUTHED_GG:
{
if( opcode == 0x00 ) // RequestAuthLogin
pcls->ph_RequestAuthLogin( pack );
else pcls->invalidPacket( pack );
} break;
case LoginClient::AUTHED_LOGIN:
{
if( opcode == 0x05 ) // RequestServerList
pcls->ph_RequestServerList( pack );
else if( opcode == 0x02 ) // RequestServerLogin
pcls->ph_RequestServerLogin( pack );
else pcls->invalidPacket( pack );
} break;
}
delete pack;
pack = NULL;
}
free( packbuffer );
packbuffer = NULL;
// remove self from login controller clients list
pcls->m_loginServer->getLoginController()->removeClient( pcls );
// cleanup
if( pcls->p_rsaKey ) RSA_free( pcls->p_rsaKey );
pcls->p_rsaKey = NULL;
L2PNet_shutdown( sock );
L2PNet_closesocket( sock );
pcls->m_sock = INVALID_SOCKET;
pcls->m_running = false;
pcls->m_flagBreak = false;
return 0;
}
void LoginClient::invalidPacket( L2LoginPacket *pack )
{
const unsigned char *bytes = pack->getBytesPtr();
unsigned int opcode = (unsigned int)bytes[2];
std::wstring stateName = L"NONE";
switch( m_state )
{
case CONNECTED: stateName = L"CONNECTED"; break;
case AUTHED_GG: stateName = L"AUTHED_GG"; break;
case AUTHED_LOGIN: stateName = L"AUTHED_LOGIN"; break;
}
LogError( L"Client %s: invalid packet %02X (len %u) in state %s!", toString(),
opcode, pack->getPacketSize(), stateName.c_str() );
m_flagBreak = true; // end client processing
}
void LoginClient::ph_RequestGGAuth( L2LoginPacket *pack )
{
pack->getPacketType();
if( pack->canReadBytes( 20 ) )
{
unsigned char sid[4];
sid[0] = pack->readUChar();
sid[1] = pack->readUChar();
sid[2] = pack->readUChar();
sid[3] = pack->readUChar();
// more 16 bytes follow, ignore them
// compare sid
if( sid[0] == p_sessionId[0] && sid[1] == p_sessionId[1] &&
sid[2] == p_sessionId[2] && sid[3] == p_sessionId[3] )
{
m_state = AUTHED_GG;
// reply GGAuthResponse
L2Login_GGAuthResponse p_ggar;
p_ggar.p_ggAuthResponse = sid[0] | sid[1]<<8 | sid[2]<<16 | sid[3]<<24;
p_ggar.create( L2_VERSION_T23 );
encodeAndSendPacket( &p_ggar );
}
else
{
LogError( L"Client %s: RequestGGAuth invalid sessionId", toString() );
// send login fail
L2Login_LoginFail p_lfail;
p_lfail.p_reasonCode = L2Login_LoginFail::REASON_ACCESS_FAILED;
p_lfail.create( L2_VERSION_T23 );
m_flagBreak = true;
encodeAndSendPacket( &p_lfail );
}
}
else
{
LogError( L"Client %s: RequestGGAuth too short packet, protocol error", toString() );
m_flagBreak = true;
}
}
void LoginClient::ph_RequestAuthLogin( L2LoginPacket *pack )
{
pack->getPacketType();
if( !pack->canReadBytes( 128 ) ) // we need at least 128, ignore rest
{
LogError( L"Client %s: RequestAuthLogin too short packet, protocol error", toString() );
m_flagBreak = true;
return;
}
unsigned char block128[128];
unsigned char loginBlock[128];
memset( block128, 0, sizeof(block128) );
memset( loginBlock, 0, sizeof(loginBlock) );
// read block with encrypted login/pass
pack->readBytes( block128, 128 );
// decode this block with client's RSA key
int r = RSA_private_decrypt( 128, block128, loginBlock, p_rsaKey, RSA_NO_PADDING );
if( r == -1 )
{
LogError( L"Client %s: RSA_private_decrypt failed!", toString() );
m_flagBreak = true;
return;
}
// request auth login constants
char l2login[L2_LOGIN_MAXLEN+1];
char l2pass[L2_PASSWORD_MAXLEN+1];
const int l2login_offset = 94;
const int l2pass_offset = 108;
// memsets
memset( l2login, 0, sizeof(l2login) );
memset( l2pass, 0, sizeof(l2pass) );
memcpy( l2login, loginBlock + l2login_offset, L2_LOGIN_MAXLEN );
memcpy( l2pass, loginBlock + l2pass_offset, L2_PASSWORD_MAXLEN );
// ncotp wtf?
//unsigned int _ncotp = loginBlock[0x7c];
//_ncotp |= loginBlock[0x7d] << 8;
//_ncotp |= loginBlock[0x7e] << 16;
//_ncotp |= loginBlock[0x7f] << 24;
try
{
// validate login
LoginController::AuthLoginResult result =
m_loginServer->getLoginController()->tryAuthLogin( l2login, l2pass, this );
switch( result )
{
case LoginController::AUTH_SUCCESS:
{
m_state = LoginClient::AUTHED_LOGIN;
// if login OK, don't forget to set account
// and update desc to allow toString() return also account
// account is set by LoginController in synchronized code block
swprintf( m_clientDesc, 127, L"Acc: %S [IP: %S:%d]", m_account, m_client_ip, m_client_port );
// generate session keys
int i = 0;
for( i=0; i<8; i++ )
{
p_sessionKey1[i] = (unsigned char)(rand() % 256) & 0xFF;
p_sessionKey2[i] = (unsigned char)(rand() % 256) & 0xFF;
}
// send LoginOK
// L2J can directly send ServerList not sending LoginOK, if Config.SHOW_LICENSE == false
// but this is not true L2 protcol and half of sessionKey is unknown to client
/*if (Config.SHOW_LICENCE)
{
client.sendPacket(new LoginOk(getClient().getSessionKey()));
}
else
{
getClient().sendPacket(new ServerList(getClient()));
}*/
// we always send LoginOK
L2Login_LoginOK p_lok;
memcpy( p_lok.p_sessionKey1, p_sessionKey1, 8 );
p_lok.create( L2_VERSION_T23 );
encodeAndSendPacket( &p_lok );
} break;
case LoginController::INVALID_PASSWORD:
{
// send LoginFail
L2Login_LoginFail p_lfail;
p_lfail.p_reasonCode = L2Login_LoginFail::REASON_USER_OR_PASS_WRONG;
p_lfail.create( L2_VERSION_T23 );
encodeAndSendPacket( &p_lfail );
m_flagBreak = true;
} break;
case LoginController::ACCOUNT_BANNED:
{
// send AccountKicked
L2Login_AccountKicked p_acc;
p_acc.p_reasonCode = L2Login_AccountKicked::REASON_PERMANENTLY_BANNED;
p_acc.create( L2_VERSION_T23 );
encodeAndSendPacket( &p_acc );
m_flagBreak = true;
} break;
case LoginController::ALREADY_ON_LS:
{
LogWarning( L"Client %s: Account %S is already on login server", toString(), l2login );
// L2J kicks other client, we will not do so
//L2LoginClient oldClient;
//if ((oldClient = lc.getAuthedClient(_user)) != null)
//{
// // kick the other client
// oldClient.close(LoginFailReason.REASON_ACCOUNT_IN_USE);
// lc.removeAuthedLoginClient(_user);
//}
// kick current client
//client.close(LoginFailReason.REASON_ACCOUNT_IN_USE);
// send LoginFail
L2Login_LoginFail p_lfail;
p_lfail.p_reasonCode = L2Login_LoginFail::REASON_ACCOUNT_IN_USE;
p_lfail.create( L2_VERSION_T23 );
encodeAndSendPacket( &p_lfail );
m_flagBreak = true;
} break;
case LoginController::ALREADY_ON_GS:
{
GameServerInfo *gsi = m_loginServer->getGameServerTable()->getGameServerThreadForAccount( l2login );
if( gsi != NULL )
{
// send LoginFail
L2Login_LoginFail p_lfail;
p_lfail.p_reasonCode = L2Login_LoginFail::REASON_ACCOUNT_IN_USE;
p_lfail.create( L2_VERSION_T23 );
encodeAndSendPacket( &p_lfail );
// kick from here
m_flagBreak = true;
// kick from there
if( gsi->isAuthed() ) gsi->kickPlayer( l2login );
}
} break;
} // switch( result)
}
catch( LoginController::HackingException& e )
{
int sec_to_ban = m_loginServer->getConfig()->LoginBlockAfterBan*1000;
m_loginServer->getLoginController()->addBanForAddress( m_client_ip, sec_to_ban );
LogWarning( L"Banned %S (%S) for %d seconds, due to %d incorrect login attempts.",
l2login, m_client_ip, sec_to_ban, e.getConnects() );
}
}
void LoginClient::ph_RequestServerList( L2LoginPacket *pack )
{
pack->getPacketType();
if( !pack->canReadBytes( 8 ) )
{
LogError( L"Client %s: RequestServerList packet too short, protocol error!", toString() );
m_flagBreak = true;
return;
}
unsigned char read_sessionKey1[8];
pack->readBytes( read_sessionKey1, 8 );
bool equal = true;
/*int i;
for( i=0; i<8; i++ )
{
if( read_sessionKey1[i] != p_sessionKey1[i] )
{
equal = false;
break;
}
}*/
equal = (memcmp( this->p_sessionKey1, read_sessionKey1, 8 ) == 0);
if( !equal )
{
LogError( L"Client %s: RequestServerList: invalid sessionKey1!", toString() );
// send LoginFail
L2Login_LoginFail p_lfail;
p_lfail.p_reasonCode = L2Login_LoginFail::REASON_ACCESS_FAILED;
p_lfail.create( L2_VERSION_T23 );
encodeAndSendPacket( &p_lfail );
// kick
m_flagBreak = true;
return;
}
// send ServerList...
GameServerTable *gst = m_loginServer->getGameServerTable();
const std::map<int, GameServerInfo *> gameServers = gst->getRegisteredGameServers();
L2Login_ServerList p_sl;
p_sl.setPacketType( 0x04 );
p_sl.writeUChar( (unsigned char)gameServers.size() );
p_sl.writeUChar( (unsigned char)m_lastServer );
// for each server write its info in packet
std::map<int, GameServerInfo *>::const_iterator iter;
for( iter = gameServers.begin(); iter != gameServers.end(); iter++ )
{
const GameServerInfo *gsi = iter->second;
if( !gsi ) continue;
// server id
p_sl.writeUChar( (unsigned char)gsi->getId() );
// server ip
const char *sip = NULL;
if( isUsingInternalIp() ) sip = gsi->getInternalIp(); else sip = gsi->getExternalIp();
in_addr inaddr;
inaddr.s_addr = L2PNet_inet_addr( sip );
if( inaddr.s_addr == INADDR_NONE ) inaddr.s_addr = INADDR_ANY; // 255.255.255.255 => 0.0.0.0
p_sl.writeUChar( inaddr.S_un.S_un_b.s_b1 );
p_sl.writeUChar( inaddr.S_un.S_un_b.s_b2 );
p_sl.writeUChar( inaddr.S_un.S_un_b.s_b3 );
p_sl.writeUChar( inaddr.S_un.S_un_b.s_b4 );
// server port
p_sl.writeD( gsi->getPort() );
p_sl.writeC( 0x00 ); // age limit?
p_sl.writeC( gsi->isPvP() ? 0x01 : 0x00 );
p_sl.writeUShort( (unsigned short)gsi->getPlayerCount() );
p_sl.writeUShort( (unsigned short)gsi->getMaxPlayers() );
p_sl.writeUChar( gsi->getStatus() == GameServerInfo::STATUS_DOWN ? 0x00 : 0x01 );
unsigned int bits = 0x00;
if( gsi->isTestServer() ) bits |= 0x04;
if( gsi->isShowingClock() ) bits |= 0x02;
p_sl.writeUInt( bits );
p_sl.writeUChar( gsi->isShowingBrackets() ? 0x01 : 0x00 );
}
encodeAndSendPacket( &p_sl );
}
void LoginClient::ph_RequestServerLogin( L2LoginPacket *pack )
{
pack->getPacketType();
// create parser :)
L2Login_RequestServerLogin *p_rsl = new L2Login_RequestServerLogin(
pack->getBytesPtr(), pack->getPacketSize() );
if( !p_rsl->parse( L2_VERSION_T23 ) )
{
LogError( L"Client %s: error parsing RequestServerLogin!", toString() );
m_flagBreak = true;
delete p_rsl;
return;
}
int serverId = (int)p_rsl->p_gameServerId;
// compare sessionKey
bool equal = (memcmp( this->p_sessionKey1, p_rsl->p_sessionKey1, 8 ) == 0);
if( !equal )
{
// send LoginFail:ACCESS_FAILED
L2Login_LoginFail p_lfail;
p_lfail.p_reasonCode = L2Login_LoginFail::REASON_ACCESS_FAILED;
p_lfail.create( L2_VERSION_T23 );
encodeAndSendPacket( &p_lfail );
LogError( L"Client %s: RequestServerLogin: invalid session key!", toString() );
m_flagBreak = true;
delete p_rsl;
return;
}
//
delete p_rsl;
p_rsl = NULL;
// check maxPlayers
if( !m_loginServer->getGameServerTable()->isLoginPossible( serverId, m_accessLevel, m_account, m_lastServer ) )
{
L2Login_PlayFail p_pfail;
p_pfail.p_reasonCode = L2Login_PlayFail::REASON_ACCESS_FAILED;
p_pfail.create( L2_VERSION_T23 );
encodeAndSendPacket( &p_pfail );
LogError( L"Client %s: RequestServerLogin: impossible to play on server %d", toString(), serverId );
m_flagBreak = true;
return;
}
// mark as joined GS
m_joinedGS = true;
m_joinedGSID = serverId;
m_loginServer->getLoginController()->addClientOnGS( m_account, p_sessionKey1, p_sessionKey2 );
// send PlayOK
L2Login_PlayOK p_ok;
memcpy( p_ok.p_sessionKey2, this->p_sessionKey2, 8 );
p_ok.create( L2_VERSION_T23 );
encodeAndSendPacket( &p_ok );
}
void LoginClient::encodeAndSendPacket( L2LoginPacket *pack )
{
// pad packet to 8 bytes len, append checksum, encode packet with client's dynamic BF key
pack->encodeAndPrepareToSend( p_bf_dyn_key );
unsigned int sentLen = 0;
int r = L2PacketSend( m_sock, pack, 10000, &sentLen );
if( r<=0 || (sentLen != pack->getPacketSize()) )
{
LogError( L"Client %s: error sending packet, %u instead of %u bytes sent (result %d)",
toString(), sentLen, pack->getPacketSize(), r );
m_flagBreak = true;
}
}

67
L2C_Login/LoginClient.h Normal file
View File

@ -0,0 +1,67 @@
#pragma once
class LoginServer;
class LoginClient
{
public:
enum State { NONE, CONNECTED, AUTHED_GG, AUTHED_LOGIN };
public:
LoginClient( LoginServer *pLoginServer, unsigned int sock, const char *clientIp, int clientPort );
~LoginClient();
private:
LoginClient();
LoginClient( const LoginClient& other );
public:
void disconnect( bool force );
void beginProcessing();
public:
const char *getIpAddress() const;
const char *getAccount() const;
const wchar_t *toString() const;
int getAccessLevel() const;
void setAccessLevel( int a );
int getLastServer() const;
void setLastServer( int s );
void setAccount( const char *account );
bool isUsingInternalIp() const;
bool hasJoinedGS( int *gs_id );
void getSessionKeys( unsigned char *sessionKey1, unsigned char *sessionKey2 );
protected:
LoginServer *m_loginServer;
unsigned int m_sock;
char m_client_ip[16];
int m_client_port;
bool m_running;
wchar_t m_clientDesc[128];
State m_state;
bool m_flagBreak;
char m_account[64];
int m_accessLevel;
int m_lastServer;
bool m_isUsingInternalIp;
bool m_joinedGS;
int m_joinedGSID;
protected: // protocol state vars
unsigned char p_sessionId[4];
unsigned char p_rsa_pubkey_mod[128];
unsigned char p_bf_dyn_key[16];
RSA *p_rsaKey;
unsigned char p_sessionKey1[8];
unsigned char p_sessionKey2[8];
protected:
static unsigned int __stdcall LoginClientThread( LoginClient *pcls );
void invalidPacket( L2LoginPacket *pack );
void ph_RequestGGAuth( L2LoginPacket *pack );
void ph_RequestAuthLogin( L2LoginPacket *pack );
void ph_RequestServerList( L2LoginPacket *pack );
void ph_RequestServerLogin( L2LoginPacket *pack );
void encodeAndSendPacket( L2LoginPacket *pack );
};

107
L2C_Login/LoginConfig.cpp Normal file
View File

@ -0,0 +1,107 @@
#include "pch.h"
#include "Log.h"
#include "LoginConfig.h"
LoginConfig::LoginConfig()
{
// objects
cfg_login = cfg_telnet = NULL;
// network
ExternalHostname = InternalHostname = LoginserverHostname = LoginHostname = NULL;
LoginserverPort = LoginPort = IpUpdateTime = 0;
// security
LogLoginController = AcceptNewGameServer = ForceGGAuth = EnableFloodProtection = false;
LoginTryBeforeBan = LoginBlockAfterBan = FastConnectionLimit = NormalConnectionTime =
FastConnectionTime = MaxConnectionPerIP = 0;
// db
Driver = URL = Login = Password = NULL;
MaximumDbConnections = MaximumDbIdleTime = 0;
// misc
ShowLicence = AutoCreateAccounts = Debug = Assert = Developer = false;
// telnet
EnableTelnet = false;
StatusPort = 0;
StatusPW = ListOfHosts = NULL;
}
LoginConfig::~LoginConfig()
{
// cleanup
if( cfg_login )
{
delete cfg_login;
cfg_login = NULL;
}
if( cfg_telnet )
{
delete cfg_telnet;
cfg_telnet = NULL;
}
}
bool LoginConfig::load()
{
bool were_errors = false;
if( cfg_login ) delete cfg_login;
cfg_login = new L2C_ConfigFile();
// load main loginserver properties
if( !cfg_login->load( L".\\config_login\\loginserver.properties" ) )
{
LogError( L"Failed to load .\\config_login\\loginserver.properties!" );
were_errors = true;
}
else LogDebug( L".\\config_login\\loginserver.properties: loaded %d vars", cfg_login->getVarsCount() );
// load telnet properties
if( cfg_telnet ) delete cfg_telnet;
cfg_telnet = new L2C_ConfigFile();
if( !cfg_telnet->load( L".\\config_login\\telnet.properties" ) )
{
LogError( L"Failed to load .\\config_login\\telnet.properties!" );
were_errors = true;
}
else LogDebug( L".\\config_login\\telnet.properties: loaded %d vars", cfg_telnet->getVarsCount() );
// initialize all config
// network
ExternalHostname = (wchar_t *)cfg_login->getValueStrW( L"ExternalHostname", L"127.0.0.1" );
InternalHostname = (wchar_t *)cfg_login->getValueStrW( L"InternalHostname", L"127.0.0.1" );
LoginserverHostname = (wchar_t *)cfg_login->getValueStrW( L"LoginserverHostname", L"*" );
LoginserverPort = cfg_login->getValueInt( L"LoginserverPort", 2106 );
LoginHostname = (wchar_t *)cfg_login->getValueStrW( L"LoginHostname", L"*" );
LoginPort = cfg_login->getValueInt( L"LoginPort", 9014 );
IpUpdateTime = cfg_login->getValueInt( L"IpUpdateTime", 15 );
// security
LogLoginController = cfg_login->getValueBool( L"LogLoginController", true );
LoginTryBeforeBan = cfg_login->getValueInt( L"LoginTryBeforeBan", 10 );
LoginBlockAfterBan = cfg_login->getValueInt( L"LoginBlockAfterBan", 600 );
AcceptNewGameServer = cfg_login->getValueBool( L"AcceptNewGameServer", false );
ForceGGAuth = cfg_login->getValueBool( L"ForceGGAuth", true );
EnableFloodProtection = cfg_login->getValueBool( L"EnableFloodProtection", true );
FastConnectionLimit = cfg_login->getValueInt( L"FastConnectionLimit", 15 );
NormalConnectionTime = cfg_login->getValueInt( L"NormalConnectionTime", 700 );
FastConnectionTime = cfg_login->getValueInt( L"FastConnectionTime", 350 );
MaxConnectionPerIP = cfg_login->getValueInt( L"MaxConnectionPerIP", 50 );
// db
Driver = (wchar_t *)cfg_login->getValueStrW( L"Driver", L"com.mysql.jdbc.Driver" );
URL = (wchar_t *)cfg_login->getValueStrW( L"URL", L"jdbc:mysql://localhost/l2jdb" );
Login = (wchar_t *)cfg_login->getValueStrW( L"Login", L"root" );
Password = (wchar_t *)cfg_login->getValueStrW( L"Password", L"" );
MaximumDbConnections = cfg_login->getValueInt( L"MaximumDbConnections", 10 );
MaximumDbIdleTime = cfg_login->getValueInt( L"MaximumDbIdleTime", 0 );
// misc
ShowLicence = cfg_login->getValueBool( L"ShowLicence", true );
AutoCreateAccounts = cfg_login->getValueBool( L"AutoCreateAccounts", false );
Debug = cfg_login->getValueBool( L"Debug", false );
Assert = cfg_login->getValueBool( L"Assert", false );
Developer = cfg_login->getValueBool( L"Developer", false );
// telnet
EnableTelnet = cfg_telnet->getValueBool( L"EnableTelnet", false );
StatusPort = cfg_telnet->getValueInt( L"StatusPort", 12345 );
StatusPW = (wchar_t *)cfg_telnet->getValueStrW( L"StatusPW", NULL );
EnableTelnet = cfg_telnet->getValueBool( L"EnableTelnet", false );
return !were_errors;
}

53
L2C_Login/LoginConfig.h Normal file
View File

@ -0,0 +1,53 @@
#pragma once
#include "l2c_configFile.h"
class LoginConfig
{
public:
LoginConfig();
~LoginConfig();
bool load();
protected:
L2C_ConfigFile *cfg_login;
L2C_ConfigFile *cfg_telnet;
public:
// network
wchar_t *ExternalHostname;
wchar_t *InternalHostname;
wchar_t *LoginserverHostname;
int LoginserverPort;
wchar_t *LoginHostname;
int LoginPort;
int IpUpdateTime;
// security
bool LogLoginController;
int LoginTryBeforeBan;
int LoginBlockAfterBan;
bool AcceptNewGameServer;
bool ForceGGAuth;
bool EnableFloodProtection;
int FastConnectionLimit;
int NormalConnectionTime;
int FastConnectionTime;
int MaxConnectionPerIP;
// db
wchar_t *Driver;
wchar_t *URL;
wchar_t *Login;
wchar_t *Password;
int MaximumDbConnections;
int MaximumDbIdleTime;
// misc
bool ShowLicence;
bool AutoCreateAccounts;
bool Debug;
bool Assert;
bool Developer;
// telnet
bool EnableTelnet;
int StatusPort;
wchar_t *StatusPW;
wchar_t *ListOfHosts;
};

View File

@ -0,0 +1,584 @@
#include "pch.h"
#include "Log.h"
#include "LoginController.h"
#include "LoginServer.h"
LoginController::LoginClientOnGS::LoginClientOnGS( const char *account, unsigned char *sessionKey1, unsigned char *sessionKey2 )
{
strcpy( m_account, account );
memcpy( m_sessionKey1, sessionKey1, 8 );
memcpy( m_sessionKey2, sessionKey2, 8 );
}
/*bool LoginController::LoginClientOnGS::areSessionKeysEqual( unsigned char *sessionKey1, unsigned char *sessionKey2 )
{
bool key1_ok = (memcmp( sessionKey1, m_sessionKey1, 8 ) == 0);
bool key2_ok = (memcmp( sessionKey2, m_sessionKey2, 8 ) == 0);
return key1_ok && key2_ok;
}*/
void LoginController::LoginClientOnGS::getSessionKeys( unsigned char *sessionKey1, unsigned char *sessionKey2 )
{
memcpy( sessionKey1, m_sessionKey1, 8 );
memcpy( sessionKey2, m_sessionKey2, 8 );
}
LoginController::LoginController( LoginServer *pServer )
{
InitializeCriticalSectionAndSpinCount( &m_lock, 10 );
m_loginServer = pServer;
int i;
for( i=0; i<MAX_RSA_KEYS; i++ ) m_rsa_keyPairs[i] = NULL;
//for( i=0; i<MAX_BLOWFISH_KEYS; i++ ) m_bf_keys[i][0] = NULL; // not needed really
m_clients.clear();
m_clientsOnGS.clear();
}
LoginController::~LoginController()
{
cleanup();
DeleteCriticalSection( &m_lock );
}
void LoginController::cleanup()
{
closeAllClients( true );
// remove all clietns on gs
LoginClientOnGSList::iterator iter;
int n = 0;
for( iter = m_clientsOnGS.begin(); iter != m_clientsOnGS.end(); iter++ )
{
LoginClientOnGS *ptr = (*iter);
if( ptr ) { delete ptr; n++; }
}
m_clientsOnGS.clear();
if( n>0 ) LogDebug( L"Deleted %d \"clients on GS\"", n );
// clear hack protection/IP bans
m_hackProtection.clear();
m_bannedIps.clear();
//
freeRSAKeyPairs();
freeBFKeys();
}
bool LoginController::init()
{
Log( L"Loading LoginController..." );
generateRSAKeyPairs();
generateBFKeys();
loadBannedIps();
return true;
}
void LoginController::Lock()
{
EnterCriticalSection( &m_lock );
}
void LoginController::Unlock()
{
LeaveCriticalSection( &m_lock );
}
void LoginController::generateRSAKeyPairs()
{
wchar_t mes[256];
wsprintfW( mes, L"Generating %d RSA keys for communication... [", MAX_RSA_KEYS );
LogFull( true, false, RGB(0,0,0), RGB(255,255,255), mes );
int i;
for( i=0; i<MAX_RSA_KEYS; i++ )
{
m_rsa_keyPairs[i] = RSA_generate_key( RSA_NUM_BITS, RSA_PUBLIC_EXPONENT, NULL, NULL );
Log_np( L"|" );
if( m_rsa_keyPairs[i] == NULL )
{
Log_np( L"\n" );
LogError( L"ERROR: RSA_generate_key() returned NULL!\n" );
//ERR_print_errors_fp( stderr );
}
}
Log_np( L"] OK\n" );
}
void LoginController::freeRSAKeyPairs()
{
int i;
for( i=0; i<MAX_RSA_KEYS; i++ )
{
if( m_rsa_keyPairs[i] ) RSA_free( m_rsa_keyPairs[i] );
m_rsa_keyPairs[i] = NULL;
}
}
RSA *LoginController::getRandomRSAKeyPair()
{
srand( (unsigned)time(NULL) );
int idx = rand() % MAX_RSA_KEYS;
RSA *ret_rsa_key = RSAPrivateKey_dup( m_rsa_keyPairs[idx] );
//RSA *ret_rsa_key = RSAPublicKey_dup( m_rsa_keyPairs[idx] ); // cannot be used for decryption :)
return ret_rsa_key;
}
const unsigned char *LoginController::getRandomBlowfishKey()
{
srand( (unsigned)time(NULL) );
int idx = rand() % MAX_BLOWFISH_KEYS;
return (const unsigned char *)m_bf_keys[ idx ];
}
void LoginController::generateBFKeys()
{
int i = 0, j = 0;
for( i=0; i<MAX_BLOWFISH_KEYS; i++ )
{
for( j=0; j<16; j++ ) m_bf_keys[i][j] = (unsigned char)( (rand() % 256) & 0xFF );
}
Log( L"Generated %d blowfish keys.", MAX_BLOWFISH_KEYS );
}
void LoginController::freeBFKeys()
{
// nothing
}
void LoginController::loadBannedIps()
{
FILE *f = fopen( ".\\config_login\\banned_ip.cfg", "rt" );
if( !f )
{
LogWarning( L"Failed to load banned IPs config. (config_login\\banned_ip.cfg)" );
return;
}
int nLoaded = 0;
char line[1024];
while( !feof( f ) )
{
line[0] = 0;
fgets( line, sizeof(line)-1, f );
if( line[0] == 0 ) continue;
if( line[0] == '#' ) continue;
//
char delim[] = " \r\n\t";
char *token1 = strtok( line, delim );
char *token2 = strtok( NULL, delim );
//
if( !token1 ) continue;
//
time_t ban_expire = 0;
char sban_expire[64];
strcpy( sban_expire, "NEVER" );
if( token2 )
{
sscanf( token2, "%I64d", &ban_expire );
struct tm *gt = localtime( &ban_expire );
sprintf( sban_expire, "%s", asctime( gt ) );
}
//
LogDebug( L"IP banned: %S; expires: %S", token1, sban_expire );
//
std::string sip = token1;
BanInfo b( token1, ban_expire );
m_bannedIps[sip] = b;
nLoaded++;
}
fclose( f );
Log( L"Loaded %d IP bans.", nLoaded );
}
void LoginController::closeAllClients( bool force )
{
Lock();
LoginClientList::iterator iter = m_clients.begin();
while( iter != m_clients.end() )
{
LoginClient *cl = (*iter);
cl->disconnect( force );
iter++;
}
m_clients.clear();
Unlock();
}
bool LoginController::addLoginClient( LoginClient *cl )
{
Lock();
m_clients.push_back( cl );
Unlock();
cl->beginProcessing();
return true;
}
bool LoginController::removeClient( LoginClient *cl )
{
bool ret = false;
Lock();
{
LoginClientList::iterator iter = m_clients.begin();
while( iter != m_clients.end() )
{
LoginClient *vcl = (*iter);
if( vcl == cl )
{
if( getConfig()->Debug ) LogDebug( L"LoginController: removing client %s", cl->toString() );
m_clients.erase( iter );
ret = true;
break;
}
iter++;
}
}
Unlock();
if( !ret ) LogError( L"LoginController: cannot find client %s to remove", cl->toString() );
return false;
}
const LoginConfig *LoginController::getConfig() const
{
if( m_loginServer ) return m_loginServer->getConfig();
throw std::exception( "LoginController::getConfig(): m_loginServer null pointer" );
}
LoginController::AuthLoginResult LoginController::tryAuthLogin(
const char *account, const char *password, LoginClient *client ) throw(HackingException)
{
AuthLoginResult ret = LoginController::INVALID_PASSWORD;
if( isLoginValid( account, password, client ) )
{
// login was successful, verify presence on Gameservers
ret = LoginController::ALREADY_ON_GS;
if( !m_loginServer->getGameServerTable()->isAccountInAnyGameServer( account ) )
{
// account isnt on any GS verify LS itself
ret = LoginController::ALREADY_ON_LS;
// dont allow 2 simultaneous login
Lock();
{
bool alreadyOnLs = false;
LoginClientList::const_iterator iter = m_clients.begin();
while( iter != m_clients.end() )
{
LoginClient *cl = (*iter);
if( strcmp( cl->getAccount(), account ) == 0 )
{
alreadyOnLs = true;
break;
}
iter++;
}
// change return value and set account to client, while this code section is locked
if( !alreadyOnLs )
{
ret = LoginController::AUTH_SUCCESS;
client->setAccount( account );
}
}
Unlock();
}
}
else
{
if( client->getAccessLevel() < 0 ) ret = LoginController::ACCOUNT_BANNED;
}
return ret;
}
bool LoginController::isLoginValid( const char *user, const char *password, LoginClient *client )
{
bool ok = false;
// log it anyway
if( getConfig()->LogLoginController )
m_loginServer->LogFile( "logins_try", "'%s' %s\n", user, client->getIpAddress() );
MysqlConnection *con = NULL;
con = m_loginServer->getDBConnection();
try
{
unsigned char hash[128];
char hash_base64[128];
memset( hash, 0, sizeof(hash) );
memset( hash_base64, 0, sizeof(hash_base64) );
SHA1( (const unsigned char *)password, strlen(password), hash ); // SHA1 or SHA?
base64_encode_string( (const char *)hash, 20, hash_base64 );
char expected[256] = {0};
int access = 0;
int lastServer = 1;
MysqlQuery q;
char *e_user = con->escapeStringMalloc( user );
q.create( L"SELECT password, accessLevel, lastServer FROM accounts WHERE login='%S'", e_user );
free( e_user );
if( !con->executeQuery( q ) ) throw std::exception( "execute query (check login)" );
if( q.getNextRow() )
{
// account exists
memset( expected, 0, sizeof(expected) );
strcpy( expected, q.getFieldStr( "password" ) );
access = q.getFieldInt( "accessLevel" );
lastServer = q.getFieldInt( "lastServer" );
if( lastServer <= 0 ) lastServer = 1; // minServerId is 1 in Interlude
if( getConfig()->Debug ) LogDebug( L"LoginController: account %S exists", user );
}
else // if account doesnt exist
{
LogWarning( L"LoginController: account missing for user %S", user );
if( getConfig()->AutoCreateAccounts )
{
size_t usrlen = strlen( user );
if( (usrlen >= 2) && (usrlen <= 14) )
{
MysqlQuery q2;
char *e_user = con->escapeStringMalloc( user );
long long int t = time(NULL) * 1000;
q2.create( L"INSERT INTO accounts (login, password, lastactive, accessLevel, lastIP) "
L"VALUES ('%S','%S', '%I64d', '0', '%S')", e_user, hash_base64, t, client->getIpAddress() );
free( e_user );
if( con->executeQuery( q2 ) ) Log( L"Created new account for %S", user );
else throw std::exception( "create account" );
return true;
}
LogWarning( L"Invalid username creation/use attempt: %S (%S)", user, client->getIpAddress() );
return false;
}
return false;
}
// is this account banned?
if( access < 0 )
{
client->setAccessLevel( access );
return false;
}
// check password hash
ok = true;
int i;
for( i = 0; i < (int)strlen(expected); i++ )
{
if( hash_base64[i] != expected[i] )
{
ok = false;
break;
}
}
// update last access time & ip
if( ok )
{
client->setAccessLevel( access );
client->setLastServer( lastServer );
char *e_user = con->escapeStringMalloc( user );
long long int t = time(NULL) * 1000;
q.create( L"UPDATE accounts SET lastactive='%I64d', lastIP='%S' WHERE login='%S'",
t, client->getIpAddress(), user );
free( e_user );
if( !con->executeQuery( q ) ) throw std::exception( "update last access time & ip" );
}
}
catch( std::exception e )
{
LogWarning( L"LoginController: Client %s: Could not check password: %S", client->toString(), e.what() );
LogWarning( L"last MySQL error: %s", con->getErrorStr() );
ok = false;
}
m_loginServer->releaseDBConnection( con );
if( !ok )
{
LogWarning( L"Failed auth: %S (%S)", user, client->getIpAddress() );
if( getConfig()->LogLoginController )
m_loginServer->LogFile( "logins_ip_fails", "'%s' %s\n", user, client->getIpAddress() );
std::string clientAddress( client->getIpAddress() );
FailedLoginAttempt *failedAttempt = getFailedLoginAttemptForAddress( clientAddress );
int failedCount;
if( failedAttempt == NULL )
{
FailedLoginAttempt new_attempt( password );
m_hackProtection[clientAddress] = new_attempt;
failedCount = 1;
}
else
{
failedAttempt->increaseCounter( password );
failedCount = failedAttempt->getCount();
}
if( failedCount >= getConfig()->LoginTryBeforeBan )
{
LogWarning( L"Banning '%S' (acc %S) for %d seconds due to %d invalid user/pass attempts",
client->getIpAddress(), user, getConfig()->LoginBlockAfterBan, failedCount );
addBanForAddress( client->getIpAddress(), getConfig()->LoginBlockAfterBan );
}
}
else
{
std::string clientAddress( client->getIpAddress() );
removeHackProtectionForAddress( clientAddress );
if( getConfig()->LogLoginController )
m_loginServer->LogFile( "logins_ok", "'%s' %s\n", user, client->getIpAddress() );
}
return ok;
}
bool LoginController::getSessionKeysForAccount( const char *account, unsigned char *player_loginKey, unsigned char *player_playKey )
{
bool ret = false;
Lock();
{
LoginClientOnGSList::const_iterator iter = m_clientsOnGS.begin();
while( iter != m_clientsOnGS.end() )
{
LoginClientOnGS *clientInfo = (*iter);
if( clientInfo )
{
if( strcmp( account, clientInfo->getAccount() ) == 0 )
{
clientInfo->getSessionKeys( player_loginKey, player_playKey );
ret = true;
break;
}
}
iter++;
}
}
Unlock();
return ret;
}
void LoginController::addClientOnGS( const char *account, unsigned char *player_loginKey, unsigned char *player_playKey )
{
LoginClientOnGS *c = new LoginClientOnGS( account, player_loginKey, player_playKey );
Lock();
m_clientsOnGS.push_back( c );
Unlock();
#ifdef _DEBUG
LogDebug( L"LoginController::addClientOnGS(): added account %S", account );
#endif
}
void LoginController::removeAuthedGSClient( const char *account )
{
if( !account ) return;
Lock();
LoginClientOnGSList::iterator iter;
for( iter = m_clientsOnGS.begin(); iter != m_clientsOnGS.end(); iter++ )
{
LoginClientOnGS *cl = (*iter);
if( cl )
{
if( strcmp( cl->getAccount(), account ) == 0 )
{
m_clientsOnGS.erase( iter );
#ifdef _DEBUG
LogDebug( L"LoginController::removeAuthedGSClient(): removed account %S", account );
#endif
break;
}
}
}
Unlock();
}
void LoginController::setAccountAccessLevel( const wchar_t *account, int level )
{
MysqlQuery q;
q.create( L"UPDATE accounts SET accessLevel=%d WHERE login='%s'", level, account );
MysqlConnection *con = m_loginServer->getDBConnection();
if( !con->executeQuery( q ) )
{
LogError( L"Cannot update account %s accesslevel to %d: %s",
account, level, con->getErrorStr() );
}
m_loginServer->releaseDBConnection( con );
}
FailedLoginAttempt *LoginController::getFailedLoginAttemptForAddress( std::string& address )
{
HackProtection::iterator iter = m_hackProtection.find( address );
if( iter == m_hackProtection.end() ) return NULL;
return &(iter->second);
}
void LoginController::removeHackProtectionForAddress( std::string& address )
{
HackProtection::iterator iter = m_hackProtection.find( address );
if( iter == m_hackProtection.end() ) return;
m_hackProtection.erase( iter );
}
void LoginController::addBanForAddress( const char *ip, time_t seconds )
{
if( !ip ) return;
std::string sip = ip;
BannedIpList::const_iterator iter = m_bannedIps.find( sip );
if( iter == m_bannedIps.end() )
{
// calc expiration time as now time + ban seconds
time_t expirationTime = time(NULL) + (time_t)seconds;
BanInfo b( ip, expirationTime );
m_bannedIps[sip] = b;
}
}
void LoginController::removeBanForAddress( const char *ip )
{
if( !ip ) return;
std::string sip = ip;
BannedIpList::const_iterator iter = m_bannedIps.find( sip );
if( iter != m_bannedIps.end() ) m_bannedIps.erase( iter );
}
bool LoginController::isIpBanned( const char *ip )
{
if( !ip ) return false;
in_addr addr;
addr.s_addr = L2PNet_inet_addr( ip );
//
char cc[32] = {0};
std::string sip = ip;
BannedIpList::const_iterator iter = m_bannedIps.find( sip );
if( iter == m_bannedIps.end() )
{
// try to find ip "%d.%d.%d.0"
sprintf( cc, "%d.%d.%d.0", (int)addr.S_un.S_un_b.s_b1, (int)addr.S_un.S_un_b.s_b2, (int)addr.S_un.S_un_b.s_b3 );
sip.assign( cc );
iter = m_bannedIps.find( sip );
}
if( iter == m_bannedIps.end() )
{
// try to find ip "%d.%d.0.0"
sprintf( cc, "%d.%d.0.0", (int)addr.S_un.S_un_b.s_b1, (int)addr.S_un.S_un_b.s_b2 );
sip.assign( cc );
iter = m_bannedIps.find( sip );
}
if( iter == m_bannedIps.end() )
{
// try to find ip "%d.0.0.0"
sprintf( cc, "%d.0.0.0", (int)addr.S_un.S_un_b.s_b1 );
sip.assign( cc );
iter = m_bannedIps.find( sip );
}
// still not found? not banned :)
if( iter == m_bannedIps.end() ) return false;
// found, but maybe ban expired?
if( iter->second.hasExpired() )
{
LogWarning( L"LoginController: ban for ip %S expired.", sip.c_str() );
m_bannedIps.erase( iter );
return false;
}
// not expired, still banned
return true;
}

112
L2C_Login/LoginController.h Normal file
View File

@ -0,0 +1,112 @@
#pragma once
class LoginConfig;
#include "LoginClient.h"
#include "FailedLoginAttempt.h"
#include "BanInfo.h"
// warning C4290: C++ exception specification ignored except to indicate a function is not __declspec(nothrow)
#pragma warning( disable: 4290 )
class LoginController
{
public:
static const int MAX_RSA_KEYS = 10;
static const int MAX_BLOWFISH_KEYS = 20;
static const int RSA_NUM_BITS = 1024;
static const int RSA_PUBLIC_EXPONENT = 65537;
enum AuthLoginResult
{
INVALID_PASSWORD,
ACCOUNT_BANNED,
ALREADY_ON_LS,
ALREADY_ON_GS,
AUTH_SUCCESS
};
static const int LOGIN_TIMEOUT = 60000; // msec
public:
class HackingException: public std::exception
{
public:
HackingException( int login_attempts ): std::exception(), m_attempts(login_attempts) {}
inline int getConnects() const { return m_attempts; }
protected:
int m_attempts;
};
public:
class LoginClientOnGS
{
public:
LoginClientOnGS( const char *account, unsigned char *sessionKey1, unsigned char *sessionKey2 );
void getSessionKeys( unsigned char *sessionKey1, unsigned char *sessionKey2 );
const char *getAccount() const { return m_account; }
protected:
char m_account[128];
unsigned char m_sessionKey1[8];
unsigned char m_sessionKey2[8];
};
public:
typedef std::list<LoginClient *> LoginClientList;
typedef std::list<LoginClientOnGS *> LoginClientOnGSList;
typedef std::map<std::string, FailedLoginAttempt> HackProtection;
typedef std::map<std::string, BanInfo> BannedIpList;
public:
LoginController( LoginServer *pServer );
~LoginController();
public:
bool init();
void cleanup();
RSA *getRandomRSAKeyPair();
const unsigned char *getRandomBlowfishKey();
public:
bool addLoginClient( LoginClient *cl );
bool removeClient( LoginClient *cl );
void closeAllClients( bool force );
public:
AuthLoginResult tryAuthLogin( const char *account, const char *password, LoginClient *client ) throw(HackingException);
void addBanForAddress( const char *ip, time_t seconds );
void removeBanForAddress( const char *ip );
bool isIpBanned( const char *ip );
bool getSessionKeysForAccount( const char *account, unsigned char *player_loginKey, unsigned char *player_playKey );
void addClientOnGS( const char *account, unsigned char *player_loginKey, unsigned char *player_playKey );
void removeAuthedGSClient( const char *account );
void setAccountAccessLevel( const wchar_t *account, int level );
protected:
bool isLoginValid( const char *user, const char *password, LoginClient *client );
protected:
void generateRSAKeyPairs();
void freeRSAKeyPairs();
void generateBFKeys();
void freeBFKeys();
void loadBannedIps();
const LoginConfig *getConfig() const;
void Lock();
void Unlock();
protected:
FailedLoginAttempt *getFailedLoginAttemptForAddress( std::string& address );
void removeHackProtectionForAddress( std::string& address );
protected:
LoginServer *m_loginServer;
RSA *m_rsa_keyPairs[MAX_RSA_KEYS];
unsigned char m_bf_keys[MAX_BLOWFISH_KEYS][16];
LoginClientList m_clients; // currently connected login server clients
LoginClientOnGSList m_clientsOnGS; // authorized nd went on game server clients
CRITICAL_SECTION m_lock;
HackProtection m_hackProtection;
BannedIpList m_bannedIps;
};

344
L2C_Login/LoginServer.cpp Normal file
View File

@ -0,0 +1,344 @@
#include "pch.h"
#include "Log.h"
#include "LoginServer.h"
LoginServer::LoginServer()
{
m_config = NULL;
m_mysql = NULL;
m_mysql_server[0] = m_mysql_db[0] = 0;
m_loginController = NULL;
m_gameServerTable = NULL;
//
m_isRunning = false;
m_hMainThread = NULL;
m_dwMainThreadId = 0;
m_mainThreadShouldStop = false;
}
LoginServer::~LoginServer()
{
stop();
}
bool LoginServer::start()
{
// already running?
if( m_hMainThread ) return false;
// createthread
m_isRunning = false; // explicitly
m_mainThreadShouldStop = false; // explicitly
m_dwMainThreadId = 0;
m_hMainThread = (HANDLE)_beginthreadex( NULL, 0,
(unsigned int (__stdcall *)(void *))MainThread, (void *)this,
0, (unsigned int *)&m_dwMainThreadId );
if( !m_hMainThread )
{
LogError( L"LoginServer::start(): _beginthreadex() failed!" );
return false;
}
#ifdef _DEBUG
LogDebug( L"LoginServer::start(): created thread %u", (unsigned int)m_dwMainThreadId );
#endif
return true;
}
bool LoginServer::stop()
{
// not running?
if( !m_hMainThread ) return false;
// signal thread to stop
LogDebug( L"Stopping login server..." );
Log_FlushFile();
m_mainThreadShouldStop = true;
// wait until thread is stopped
do { Sleep( 200 ); } while( m_isRunning );
#ifdef _DEBUG
LogDebug( L"LoginServer MainThread stopped." );
#endif
// call cleanup
resourceCleanup();
return true;
}
void LoginServer::resourceCleanup()
{
// cleanup
if( m_hMainThread )
{
CloseHandle( m_hMainThread );
m_hMainThread = NULL;
}
m_dwMainThreadId = 0;
if( m_gameServerTable )
{
m_gameServerTable->cleanup();
delete m_gameServerTable;
m_gameServerTable = NULL;
}
if( m_loginController )
{
m_loginController->cleanup();
delete m_loginController;
m_loginController = NULL;
}
m_mysql_server[0] = 0;
m_mysql_db[0] = 0;
if( m_mysql )
{
delete m_mysql;
m_mysql = NULL;
}
if( m_config )
{
delete m_config;
m_config = NULL;
}
#ifdef _DEBUG
LogDebug( L"LoginServer cleanup complete." );
#endif
}
MysqlConnection *LoginServer::getDBConnection()
{
MysqlConnection *ret = m_mysql->getConnection();
if( ret ) return ret;
LogError( L"LoginServer::getDBConnection(): not enough free connections, %d/%d busy",
m_mysql->getCountActiveConnections(), m_mysql->getCountMaxConnections() );
throw std::exception( "LoginServer::getDBConnection(): no free connections!" );
//return NULL;
}
bool LoginServer::releaseDBConnection( MysqlConnection *con )
{
if( !con ) return false;
return m_mysql->releaseConnection( con );
}
LoginController *LoginServer::getLoginController()
{
return m_loginController;
}
const LoginConfig *LoginServer::getConfig() const
{
return m_config;
}
GameServerTable *LoginServer::getGameServerTable()
{
return m_gameServerTable;
}
void LoginServer::LogFile( const char *fn, const char *_Format, ... )
{
char ffn[256] = {0};
sprintf( ffn, ".\\log\\login_%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 );
}
// returns true if ip is banned and closes sockets
bool LoginServer::checkIsBannedIp( unsigned int sock, const char *ip, int port )
{
if( !ip ) return false;
if( m_loginController->isIpBanned( ip ) )
{
LogWarning( L"Connected IP %S:%d is banned!", ip, port );
L2PNet_shutdown( sock );
L2PNet_closesocket( sock );
return true;
}
return false;
}
unsigned int __stdcall LoginServer::MainThread( LoginServer *pcls )
{
pcls->m_isRunning = true; // mark self as running
DWORD startTick = GetTickCount();
LogFull( false, true, RGB(0,200,0), RGB(255,255,255), L"===================================" );
Log( L"Starting login server..." );
//
// ensure log dir exists
if( CreateDirectory( TEXT(".\\log"), NULL ) ) Log( L"Created logs directory." );
//
// load config
Log( L"Loading config..." );
pcls->m_config = new LoginConfig();
if( !pcls->m_config->load() ) LogError( L"Failed to load config!" );
//else Log( L"Config loaded." );
//
//
// initialize mysql conn manager
pcls->m_mysql = new MysqlConnectionManager();
// parse jdbc url... [jdbc:mysql://localhost/l2jdb]
wchar_t delim[] = L":/";
wchar_t *_url_copy = _wcsdup( pcls->m_config->URL );
wchar_t *token = wcstok( _url_copy, delim );
int i = 0;
while( token )
{
if( i == 2 ) wcscpy( pcls->m_mysql_server, token );
if( i == 3 ) wcscpy( pcls->m_mysql_db, token );
i++;
token = wcstok( NULL, delim );
}
free( _url_copy );
_url_copy = NULL;
// init
Log( L"Initializing MySQL... server %s; db %s; maximum connections %d",
pcls->m_mysql_server, pcls->m_mysql_db, pcls->m_config->MaximumDbConnections );
if( !pcls->m_mysql->initialize( pcls->m_config->MaximumDbConnections,
pcls->m_mysql_server, pcls->m_config->Login, pcls->m_config->Password, pcls->m_mysql_db ) )
LogError( L"Cannot init mysql db!" );
//else Log( L"MySQL init OK" );
//
//
// init LoginController
pcls->m_loginController = new LoginController( pcls );
pcls->m_loginController->init();
//
// init game server table
pcls->m_gameServerTable = new GameServerTable( pcls );
pcls->m_gameServerTable->init();
// create listening sockets
SOCKET s_clients = L2PNet_TCPsocket_create( true );
SOCKET s_servers = L2PNet_TCPsocket_create( true );
// calc bind addresses
char cl_bind_ip[16];
char srv_bind_ip[16];
strcpy( cl_bind_ip, "0.0.0.0" );
strcpy( srv_bind_ip, "0.0.0.0" );
if( wcscmp( pcls->m_config->LoginserverHostname, L"*" ) )
{
char aip[64] = {0};
l2c_unicode_to_ansi( pcls->m_config->LoginserverHostname, aip, sizeof(aip) );
in_addr inaddr;
if( L2PNet_resolveHostname( aip, &inaddr ) )
{
strcpy( cl_bind_ip, L2PNet_inet_ntoa( inaddr ) );
LogDebug( L"Login server will listen to clients on IP %S", cl_bind_ip );
}
else
{
LogError( L"Invalid hostname: %s, login server will bind client listener to all available IPs",
pcls->m_config->LoginserverHostname );
}
}
if( wcscmp( pcls->m_config->LoginHostname, L"*" ) )
{
char aip[64] = {0};
l2c_unicode_to_ansi( pcls->m_config->LoginHostname, aip, sizeof(aip) );
in_addr inaddr;
if( L2PNet_resolveHostname( aip, &inaddr ) )
{
strcpy( srv_bind_ip, L2PNet_inet_ntoa( inaddr ) );
LogDebug( L"Login server will listen to gameservers on IP %S", srv_bind_ip );
}
else
{
LogError( L"Invalid hostname: %s, login server will bind GS listener to all available IPs",
pcls->m_config->LoginHostname );
}
}
int r = 0;
DWORD endTick = 0;
int secs_load = 0;
unsigned int socks_array[2] = { s_clients, s_servers };
int bReadyRead[2] = {0,0}, bReadyWrite[2] = {0,0};
try
{
// bind listening sockets
// bind clients listener
r = L2PNet_bind( s_clients, cl_bind_ip, (unsigned short)pcls->m_config->LoginserverPort );
if( r != 0 ) throw std::exception( "Bind client listener error: L2 clients port busy?" );
// bind servers listener
r = L2PNet_bind( s_servers, srv_bind_ip, (unsigned short)pcls->m_config->LoginPort );
if( r != 0 ) throw std::exception( "Bind GS servers listener error: GS listen port busy?" );
// start listening
// start client listener
r = L2PNet_listen( s_clients );
if( r != 0 ) throw std::exception( "Listen client listener error" );
// start server listener
r = L2PNet_listen( s_servers );
if( r != 0 ) throw std::exception( "Listen GS servers listener error" );
Log( L"Listening to gameservers on %s:%d", pcls->m_config->LoginHostname, pcls->m_config->LoginPort );
Log( L"Login server ready on %s:%d", pcls->m_config->LoginserverHostname, pcls->m_config->LoginserverPort );
// measure load time
endTick = GetTickCount();
secs_load = (endTick - startTick) / 1000;
Log( L"Login server loaded in %d seconds (%d ms).", secs_load, (int)(endTick - startTick) );
LogFull( false, true, RGB(0,200,0), RGB(255,255,255), L"===================================" );
// run main loop
while( pcls->m_mainThreadShouldStop == false )
{
bReadyRead[0] = bReadyRead[1] = 0;
bReadyWrite[0] = bReadyWrite[1] = 0;
socks_array[0] = s_clients;
socks_array[1] = s_servers;
r = L2PNet_select_multi( socks_array, 2, L2PNET_SELECT_READ, 200, bReadyRead, bReadyWrite );
if( pcls->m_mainThreadShouldStop ) break;
if( r == 0 ) continue;
if( r == -1 ) throw std::exception( "L2PNet_select_multi(): return value -1!" );
// accept login client?
if( bReadyRead[0] )
{
char acceptedIP[16] = {0};
unsigned short acceptedPort = 0;
unsigned int accepted_client_socket = L2PNet_accept( s_clients, acceptedIP, &acceptedPort );
if( accepted_client_socket != INVALID_SOCKET )
{
if( pcls->getConfig()->Debug )
LogDebug( L"Accepted L2 client %S:%d... ", acceptedIP, (int)acceptedPort );
if( !pcls->checkIsBannedIp( accepted_client_socket, acceptedIP, acceptedPort ) )
{
LoginClient *cl = new LoginClient( pcls, accepted_client_socket, acceptedIP, (int)acceptedPort );
pcls->m_loginController->addLoginClient( cl );
}
}
}
// accept game server client?
if( bReadyRead[1] )
{
char acceptedIP[16] = {0};
unsigned short acceptedPort = 0;
unsigned int accepted_socket = L2PNet_accept( s_servers, acceptedIP, &acceptedPort );
if( pcls->getConfig()->Debug )
LogDebug( L"Accepted gameserver %S:%d... ", acceptedIP, (int)acceptedPort );
if( !pcls->checkIsBannedIp( accepted_socket, acceptedIP, acceptedPort ) )
{
pcls->m_gameServerTable->beginProcessGameServerClient( acceptedIP, accepted_socket );
}
}
}
}
catch( std::exception& e )
{
LogError( L"LoginServer MainThread C++ std::exception: %S", e.what() );
LogError( L"LoginServer will stop." );
}
// close sockets
L2PNet_shutdown( s_clients );
L2PNet_closesocket( s_clients );
L2PNet_shutdown( s_servers );
L2PNet_closesocket( s_servers );
// cleanup self (mark as thread end)
pcls->m_isRunning = false;
LogDebug( L"Login server stopping." );
return 0;
}

48
L2C_Login/LoginServer.h Normal file
View File

@ -0,0 +1,48 @@
#pragma once
#include "LoginConfig.h"
#include "l2c_db.h"
#include "LoginController.h"
#include "GameServerTable.h"
class LoginServer
{
public:
static const unsigned int LOGIN_PROTOCOL_REV = 0x102;
public:
LoginServer();
~LoginServer();
public:
bool start();
bool stop();
public:
MysqlConnection *getDBConnection();
bool releaseDBConnection( MysqlConnection *con );
public:
LoginController *getLoginController();
const LoginConfig *getConfig() const;
GameServerTable *getGameServerTable();
public:
void LogFile( const char *fn, const char *_Format, ... );
protected:
LoginConfig *m_config;
MysqlConnectionManager *m_mysql;
wchar_t m_mysql_server[128];
wchar_t m_mysql_db[128];
LoginController *m_loginController;
GameServerTable *m_gameServerTable;
protected:
bool m_isRunning;
HANDLE m_hMainThread;
DWORD m_dwMainThreadId;
bool m_mainThreadShouldStop;
static unsigned int __stdcall MainThread( LoginServer *pcls );
bool checkIsBannedIp( unsigned int sock, const char *ip, int port );
void resourceCleanup();
};

View File

@ -0,0 +1,15 @@
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
rem Copy EXE
copy /y %2\%1.exe %2\%out%
rem Copy PDB
copy /y %2\%1.pdb %2\%out

14
L2C_Login/Resource.h Normal file
View File

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

1
L2C_Login/pch.cpp Normal file
View File

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

41
L2C_Login/pch.h Normal file
View File

@ -0,0 +1,41 @@
#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>
// STL
#include <string>
#include <list>
#include <map>
#include <set>
#include <vector>
// Win32 API
#include <winsock2.h>
#include <windows.h>
#include <windowsx.h>
#include <wchar.h>
#include <tchar.h>
#include <process.h>
#include <commctrl.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>

578
L2C_Server.vcproj Normal file
View File

@ -0,0 +1,578 @@
<?xml version="1.0" encoding="windows-1251"?>
<VisualStudioProject
ProjectType="Visual C++"
Version="9,00"
Name="L2C_Server"
ProjectGUID="{72DC0189-0628-48FC-AA67-A7A279A2A171}"
RootNamespace="L2C_Server"
Keyword="Win32Proj"
TargetFrameworkVersion="196613"
>
<Platforms>
<Platform
Name="Win32"
/>
</Platforms>
<ToolFiles>
</ToolFiles>
<Configurations>
<Configuration
Name="Debug|Win32"
OutputDirectory="_build\L2C_Server_Debug"
IntermediateDirectory="_build\L2C_Server_Debug"
ConfigurationType="1"
CharacterSet="1"
>
<Tool
Name="VCPreBuildEventTool"
/>
<Tool
Name="VCCustomBuildTool"
/>
<Tool
Name="VCXMLDataGeneratorTool"
/>
<Tool
Name="VCWebServiceProxyGeneratorTool"
/>
<Tool
Name="VCMIDLTool"
/>
<Tool
Name="VCCLCompilerTool"
Optimization="0"
AdditionalIncludeDirectories="L2C_Server;l2packets;libl2c_utils;lib"
PreprocessorDefinitions="WIN32;_DEBUG;_WINDOWS"
MinimalRebuild="true"
BasicRuntimeChecks="3"
RuntimeLibrary="1"
UsePrecompiledHeader="2"
PrecompiledHeaderThrough="pch.h"
ProgramDataBaseFileName="$(IntDir)\$(TargetName).pdb"
WarningLevel="4"
DebugInformationFormat="4"
/>
<Tool
Name="VCManagedResourceCompilerTool"
/>
<Tool
Name="VCResourceCompilerTool"
/>
<Tool
Name="VCPreLinkEventTool"
/>
<Tool
Name="VCLinkerTool"
AdditionalDependencies="ws2_32.lib comctl32.lib libeay32_st_MTd.lib ssleay32_st_MTd.lib dbghelp.lib"
OutputFile="$(OutDir)\$(ProjectName)_d.exe"
LinkIncremental="2"
AdditionalLibraryDirectories="lib\openssl"
GenerateDebugInformation="true"
SubSystem="2"
TargetMachine="1"
/>
<Tool
Name="VCALinkTool"
/>
<Tool
Name="VCManifestTool"
/>
<Tool
Name="VCXDCMakeTool"
/>
<Tool
Name="VCBscMakeTool"
/>
<Tool
Name="VCFxCopTool"
/>
<Tool
Name="VCAppVerifierTool"
/>
<Tool
Name="VCPostBuildEventTool"
Description="Copy files to dist folder..."
CommandLine="L2C_Server\PostBuildEvent.cmd $(TargetName) $(TargetDir)"
/>
</Configuration>
<Configuration
Name="Release|Win32"
OutputDirectory="_build\L2C_Server_Release"
IntermediateDirectory="_build\L2C_Server_Release"
ConfigurationType="1"
CharacterSet="1"
WholeProgramOptimization="1"
>
<Tool
Name="VCPreBuildEventTool"
/>
<Tool
Name="VCCustomBuildTool"
/>
<Tool
Name="VCXMLDataGeneratorTool"
/>
<Tool
Name="VCWebServiceProxyGeneratorTool"
/>
<Tool
Name="VCMIDLTool"
/>
<Tool
Name="VCCLCompilerTool"
Optimization="2"
EnableIntrinsicFunctions="true"
AdditionalIncludeDirectories="L2C_Server;l2packets;libl2c_utils;lib"
PreprocessorDefinitions="WIN32;NDEBUG;_WINDOWS"
RuntimeLibrary="0"
EnableFunctionLevelLinking="true"
UsePrecompiledHeader="2"
PrecompiledHeaderThrough="pch.h"
ProgramDataBaseFileName="$(IntDir)\$(TargetName).pdb"
WarningLevel="3"
DebugInformationFormat="3"
/>
<Tool
Name="VCManagedResourceCompilerTool"
/>
<Tool
Name="VCResourceCompilerTool"
/>
<Tool
Name="VCPreLinkEventTool"
/>
<Tool
Name="VCLinkerTool"
AdditionalDependencies="ws2_32.lib comctl32.lib libeay32_st_MT.lib ssleay32_st_MT.lib dbghelp.lib"
LinkIncremental="1"
AdditionalLibraryDirectories="lib\openssl"
GenerateDebugInformation="true"
SubSystem="2"
OptimizeReferences="2"
EnableCOMDATFolding="2"
TargetMachine="1"
/>
<Tool
Name="VCALinkTool"
/>
<Tool
Name="VCManifestTool"
/>
<Tool
Name="VCXDCMakeTool"
/>
<Tool
Name="VCBscMakeTool"
/>
<Tool
Name="VCFxCopTool"
/>
<Tool
Name="VCAppVerifierTool"
/>
<Tool
Name="VCPostBuildEventTool"
Description="Copy files to dist folder..."
CommandLine="L2C_Server\PostBuildEvent.cmd $(TargetName) $(TargetDir)"
/>
</Configuration>
</Configurations>
<References>
</References>
<Files>
<Filter
Name="main"
>
<File
RelativePath=".\L2C_Server\L2Serverd.cpp"
>
</File>
<File
RelativePath=".\L2C_Server\L2Serverd.rc"
>
</File>
<File
RelativePath=".\L2C_Server\Log.cpp"
>
</File>
<File
RelativePath=".\L2C_Server\Log.h"
>
</File>
<File
RelativePath=".\L2C_Server\pch.cpp"
>
<FileConfiguration
Name="Debug|Win32"
>
<Tool
Name="VCCLCompilerTool"
UsePrecompiledHeader="1"
/>
</FileConfiguration>
<FileConfiguration
Name="Release|Win32"
>
<Tool
Name="VCCLCompilerTool"
UsePrecompiledHeader="1"
/>
</FileConfiguration>
</File>
<File
RelativePath=".\L2C_Server\pch.h"
>
</File>
<File
RelativePath=".\L2C_Server\Resource.h"
>
</File>
</Filter>
<Filter
Name="GS"
>
<File
RelativePath=".\L2C_Server\enums.h"
>
</File>
<File
RelativePath=".\L2C_Server\GS.cpp"
>
</File>
<File
RelativePath=".\L2C_Server\GS.h"
>
</File>
<File
RelativePath=".\L2C_Server\GS_Cfg.cpp"
>
</File>
<File
RelativePath=".\L2C_Server\GS_Cfg.h"
>
</File>
<File
RelativePath=".\L2C_Server\LS_Connection.cpp"
>
</File>
<File
RelativePath=".\L2C_Server\LS_Connection.h"
>
</File>
<Filter
Name="datatables"
>
<File
RelativePath=".\L2C_Server\datatables\CharNameTable.cpp"
>
</File>
<File
RelativePath=".\L2C_Server\datatables\CharNameTable.h"
>
</File>
<File
RelativePath=".\L2C_Server\datatables\CharTemplateTable.cpp"
>
</File>
<File
RelativePath=".\L2C_Server\datatables\CharTemplateTable.h"
>
</File>
<File
RelativePath=".\L2C_Server\datatables\ItemTable.cpp"
>
</File>
<File
RelativePath=".\L2C_Server\datatables\ItemTable.h"
>
</File>
</Filter>
<Filter
Name="world"
>
<Filter
Name="model"
>
<File
RelativePath=".\L2C_Server\world\model\GameObject.cpp"
>
</File>
<File
RelativePath=".\L2C_Server\world\model\GameObject.h"
>
</File>
<Filter
Name="base"
>
<File
RelativePath=".\L2C_Server\world\model\base\ClassId.cpp"
>
</File>
<File
RelativePath=".\L2C_Server\world\model\base\ClassId.h"
>
</File>
<File
RelativePath=".\L2C_Server\world\model\base\ClassIdTree.cpp"
>
</File>
<File
RelativePath=".\L2C_Server\world\model\base\ClassIdTree.h"
>
</File>
<File
RelativePath=".\L2C_Server\world\model\base\Race.h"
>
</File>
</Filter>
<Filter
Name="character"
>
<File
RelativePath=".\L2C_Server\world\model\character\GameCharacter.cpp"
>
</File>
<File
RelativePath=".\L2C_Server\world\model\character\GameCharacter.h"
>
</File>
<File
RelativePath=".\L2C_Server\world\model\character\GameNpc.cpp"
>
</File>
<File
RelativePath=".\L2C_Server\world\model\character\GameNpc.h"
>
</File>
<File
RelativePath=".\L2C_Server\world\model\character\GamePlayer.cpp"
>
</File>
<File
RelativePath=".\L2C_Server\world\model\character\GamePlayer.h"
>
</File>
</Filter>
</Filter>
<Filter
Name="templates"
>
<File
RelativePath=".\L2C_Server\world\templates\StatsSet.cpp"
>
</File>
<File
RelativePath=".\L2C_Server\world\templates\StatsSet.h"
>
</File>
<Filter
Name="chars"
>
<File
RelativePath=".\L2C_Server\world\templates\chars\L2CharTemplate.cpp"
>
</File>
<File
RelativePath=".\L2C_Server\world\templates\chars\L2CharTemplate.h"
>
</File>
<File
RelativePath=".\L2C_Server\world\templates\chars\L2PlayerTemplate.cpp"
>
</File>
<File
RelativePath=".\L2C_Server\world\templates\chars\L2PlayerTemplate.h"
>
</File>
</Filter>
<Filter
Name="item"
>
<File
RelativePath=".\L2C_Server\world\templates\item\L2ArmorTemplate.cpp"
>
</File>
<File
RelativePath=".\L2C_Server\world\templates\item\L2ArmorTemplate.h"
>
</File>
<File
RelativePath=".\L2C_Server\world\templates\item\L2EtcItemTemplate.cpp"
>
</File>
<File
RelativePath=".\L2C_Server\world\templates\item\L2EtcItemTemplate.h"
>
</File>
<File
RelativePath=".\L2C_Server\world\templates\item\L2ItemTemplate.cpp"
>
</File>
<File
RelativePath=".\L2C_Server\world\templates\item\L2ItemTemplate.h"
>
</File>
<File
RelativePath=".\L2C_Server\world\templates\item\L2ItemType.h"
>
</File>
<File
RelativePath=".\L2C_Server\world\templates\item\L2WeaponTemplate.cpp"
>
</File>
<File
RelativePath=".\L2C_Server\world\templates\item\L2WeaponTemplate.h"
>
</File>
</Filter>
</Filter>
</Filter>
<Filter
Name="net"
>
<File
RelativePath=".\L2C_Server\net\ClientPacketHandler.cpp"
>
</File>
<File
RelativePath=".\L2C_Server\net\ClientPacketHandler.h"
>
</File>
<File
RelativePath=".\L2C_Server\net\ClientPool.cpp"
>
</File>
<File
RelativePath=".\L2C_Server\net\ClientPool.h"
>
</File>
<File
RelativePath=".\L2C_Server\net\ServerPackets.h"
>
</File>
<Filter
Name="clientpackets"
>
<File
RelativePath=".\L2C_Server\net\clientpackets\c_AuthLogin.cpp"
>
</File>
<File
RelativePath=".\L2C_Server\net\clientpackets\c_CharacterCreate.cpp"
>
</File>
<File
RelativePath=".\L2C_Server\net\clientpackets\c_CharacterSelect.cpp"
>
</File>
<File
RelativePath=".\L2C_Server\net\clientpackets\c_Logout.cpp"
>
</File>
<File
RelativePath=".\L2C_Server\net\clientpackets\c_NewCharacter.cpp"
>
</File>
<File
RelativePath=".\L2C_Server\net\clientpackets\c_ProtocolVersion.cpp"
>
</File>
<File
RelativePath=".\L2C_Server\net\clientpackets\c_RequestGotoLobby.cpp"
>
</File>
</Filter>
<Filter
Name="serverpackets"
>
<File
RelativePath=".\L2C_Server\net\serverpackets\s_CharacterSelectionInfo.cpp"
>
</File>
<File
RelativePath=".\L2C_Server\net\serverpackets\s_CharCreateFail.cpp"
>
</File>
<File
RelativePath=".\L2C_Server\net\serverpackets\s_CharCreateOK.cpp"
>
</File>
<File
RelativePath=".\L2C_Server\net\serverpackets\s_KeyPacket.cpp"
>
</File>
<File
RelativePath=".\L2C_Server\net\serverpackets\s_NewCharacterSuccess.cpp"
>
</File>
</Filter>
<Filter
Name="GameClient"
>
<File
RelativePath=".\L2C_Server\net\GameClient\GameClient.cpp"
>
</File>
<File
RelativePath=".\L2C_Server\net\GameClient\GameClient.h"
>
</File>
<File
RelativePath=".\L2C_Server\net\GameClient\GameClientNet.cpp"
>
</File>
<File
RelativePath=".\L2C_Server\net\GameClient\GameClientNetStats.cpp"
>
</File>
<File
RelativePath=".\L2C_Server\net\GameClient\GameClientNetStats.h"
>
</File>
<File
RelativePath=".\L2C_Server\net\GameClient\GameClientThread.cpp"
>
</File>
</Filter>
</Filter>
</Filter>
<Filter
Name="utils"
>
<File
RelativePath=".\L2C_Server\utils\Debugging.cpp"
>
</File>
<File
RelativePath=".\L2C_Server\utils\Debugging.h"
>
</File>
<File
RelativePath=".\L2C_Server\utils\Exception.cpp"
>
</File>
<File
RelativePath=".\L2C_Server\utils\Exception.h"
>
</File>
<File
RelativePath=".\L2C_Server\utils\IdFactory.cpp"
>
</File>
<File
RelativePath=".\L2C_Server\utils\IdFactory.h"
>
</File>
<File
RelativePath=".\L2C_Server\utils\Utils.cpp"
>
</File>
<File
RelativePath=".\L2C_Server\utils\Utils.h"
>
</File>
</Filter>
</Files>
<Globals>
</Globals>
</VisualStudioProject>

393
L2C_Server/GS.cpp Normal file
View File

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

58
L2C_Server/GS.h Normal file
View File

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

144
L2C_Server/GS_Cfg.cpp Normal file
View File

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

57
L2C_Server/GS_Cfg.h Normal file
View File

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

BIN
L2C_Server/L2C.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

213
L2C_Server/L2Serverd.cpp Normal file
View File

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

44
L2C_Server/L2Serverd.rc Normal file
View File

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

View File

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

View File

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

244
L2C_Server/Log.cpp Normal file
View File

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

19
L2C_Server/Log.h Normal file
View File

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

View File

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

15
L2C_Server/Resource.h Normal file
View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

39
L2C_Server/enums.h Normal file
View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

1
L2C_Server/pch.cpp Normal file
View File

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

44
L2C_Server/pch.h Normal file
View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

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

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

Some files were not shown because too many files have changed in this diff Show More