Initial MSVC 2008 projects workspace
This commit is contained in:
commit
03de3bdc95
316
L2C_Login.vcproj
Normal file
316
L2C_Login.vcproj
Normal 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
40
L2C_Login/BanInfo.cpp
Normal 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
16
L2C_Login/BanInfo.h
Normal 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;
|
||||
};
|
56
L2C_Login/FailedLoginAttempt.cpp
Normal file
56
L2C_Login/FailedLoginAttempt.cpp
Normal 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;
|
||||
}
|
||||
|
19
L2C_Login/FailedLoginAttempt.h
Normal file
19
L2C_Login/FailedLoginAttempt.h
Normal 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;
|
||||
};
|
13
L2C_Login/GS_ConnectionInfo.h
Normal file
13
L2C_Login/GS_ConnectionInfo.h
Normal 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;
|
||||
};
|
241
L2C_Login/GameServerInfo.cpp
Normal file
241
L2C_Login/GameServerInfo.cpp
Normal 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 );
|
||||
}
|
||||
|
71
L2C_Login/GameServerInfo.h
Normal file
71
L2C_Login/GameServerInfo.h
Normal 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;
|
||||
};
|
904
L2C_Login/GameServerTable.cpp
Normal file
904
L2C_Login/GameServerTable.cpp
Normal 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
|
||||
**/
|
||||
|
88
L2C_Login/GameServerTable.h
Normal file
88
L2C_Login/GameServerTable.h
Normal 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
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
159
L2C_Login/L2Logind.cpp
Normal 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
43
L2C_Login/L2Logind.rc
Normal 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
222
L2C_Login/Log.cpp
Normal 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
17
L2C_Login/Log.h
Normal 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
601
L2C_Login/LoginClient.cpp
Normal 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
67
L2C_Login/LoginClient.h
Normal 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
107
L2C_Login/LoginConfig.cpp
Normal 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
53
L2C_Login/LoginConfig.h
Normal 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;
|
||||
};
|
||||
|
584
L2C_Login/LoginController.cpp
Normal file
584
L2C_Login/LoginController.cpp
Normal 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
112
L2C_Login/LoginController.h
Normal 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
344
L2C_Login/LoginServer.cpp
Normal 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
48
L2C_Login/LoginServer.h
Normal 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();
|
||||
};
|
15
L2C_Login/PostBuildEvent.cmd
Normal file
15
L2C_Login/PostBuildEvent.cmd
Normal 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
14
L2C_Login/Resource.h
Normal 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
1
L2C_Login/pch.cpp
Normal file
@ -0,0 +1 @@
|
||||
#include "pch.h"
|
41
L2C_Login/pch.h
Normal file
41
L2C_Login/pch.h
Normal 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
578
L2C_Server.vcproj
Normal 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
393
L2C_Server/GS.cpp
Normal file
@ -0,0 +1,393 @@
|
||||
#include "pch.h"
|
||||
#include "Log.h"
|
||||
#include "GS.h"
|
||||
#include "utils/Debugging.h"
|
||||
#include "utils/Exception.h"
|
||||
#include "world/model/base/ClassIdTree.h"
|
||||
#include "datatables/CharNameTable.h"
|
||||
#include "datatables/CharTemplateTable.h"
|
||||
#include "datatables/ItemTable.h"
|
||||
|
||||
// init class static vars
|
||||
GameServer *GameServer::_self = NULL;
|
||||
int GameServer::_referenceCount = 0;
|
||||
|
||||
GameServer *GameServer::getInstance()
|
||||
{
|
||||
if( !_self )
|
||||
{
|
||||
_self = new GameServer();
|
||||
_referenceCount++;
|
||||
}
|
||||
return _self;
|
||||
}
|
||||
|
||||
void GameServer::freeInstance()
|
||||
{
|
||||
if( _self )
|
||||
{
|
||||
_referenceCount--;
|
||||
if( _referenceCount == 0 )
|
||||
{
|
||||
delete _self;
|
||||
_self = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
GameServer::GameServer()
|
||||
{
|
||||
m_serverStatus = SERVER_STATUS_DOWN;
|
||||
m_cfg = new GameConfig();
|
||||
m_mysql = new MysqlConnectionManager();
|
||||
m_loginConnection = new LoginConnection();
|
||||
m_isRunning = m_flagStop = false;
|
||||
m_clientPool = new ClientPool();
|
||||
m_idf = new IdFactory();
|
||||
}
|
||||
|
||||
GameServer::~GameServer()
|
||||
{
|
||||
stop();
|
||||
if( m_cfg ) { delete m_cfg; m_cfg = NULL; }
|
||||
if( m_mysql ) { delete m_mysql; m_mysql = NULL; }
|
||||
if( m_loginConnection ) { delete m_loginConnection; m_loginConnection = NULL; }
|
||||
if( m_clientPool ) { delete m_clientPool; m_clientPool = NULL; }
|
||||
if( m_idf ) { delete m_idf; m_idf = NULL; }
|
||||
}
|
||||
|
||||
bool GameServer::start()
|
||||
{
|
||||
if( m_isRunning ) return false;
|
||||
HANDLE hThread = NULL;
|
||||
unsigned int threadId = 0;
|
||||
m_gsLock.Lock();
|
||||
{
|
||||
m_flagStop = false;
|
||||
m_isRunning = false;
|
||||
hThread = (HANDLE)_beginthreadex( NULL, 0,
|
||||
(unsigned int (__stdcall *)(void *))(GameServer::GS_Thread), (void *)this,
|
||||
0, &threadId );
|
||||
if( hThread )
|
||||
{
|
||||
m_isRunning = true;
|
||||
CloseHandle( hThread );
|
||||
}
|
||||
else
|
||||
{
|
||||
m_isRunning = false;
|
||||
}
|
||||
}
|
||||
m_gsLock.Unlock();
|
||||
return (hThread != NULL);
|
||||
}
|
||||
|
||||
bool GameServer::stop()
|
||||
{
|
||||
if( !m_isRunning ) return false;
|
||||
m_gsLock.Lock();
|
||||
{
|
||||
this->m_flagStop = true;
|
||||
while( this->m_isRunning ) Sleep( 20 );
|
||||
this->m_flagStop = false;
|
||||
}
|
||||
m_gsLock.Unlock();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool GameServer::isRunning() const
|
||||
{
|
||||
return m_isRunning;
|
||||
}
|
||||
|
||||
GameConfig *GameServer::getConfig() const
|
||||
{
|
||||
return m_cfg;
|
||||
}
|
||||
|
||||
MysqlConnection *GameServer::getDBConnection()
|
||||
{
|
||||
MysqlConnection *ret = m_mysql->getConnection();
|
||||
if( ret ) return ret;
|
||||
LogError( L"GameServer::getDBConnection(): not enough free connections, %d/%d busy",
|
||||
m_mysql->getCountActiveConnections(), m_mysql->getCountMaxConnections() );
|
||||
throw Exception( "GameServer::getDBConnection(): no free connections!" );
|
||||
}
|
||||
|
||||
bool GameServer::releaseDBConnection( MysqlConnection *con )
|
||||
{
|
||||
if( !con ) return false;
|
||||
return m_mysql->releaseConnection( con );
|
||||
}
|
||||
|
||||
void GameServer::LogFile( const char *fn, const char *_Format, ... )
|
||||
{
|
||||
char ffn[256] = {0};
|
||||
sprintf( ffn, ".\\log\\game_%s.log", fn );
|
||||
FILE *f = fopen( ffn, "at" );
|
||||
if( !f ) return;
|
||||
SYSTEMTIME st;
|
||||
GetLocalTime( &st );
|
||||
fprintf( f, "%02d.%02d.%04d %02d:%02d:%03d: ", (int)st.wDay, (int)st.wMonth, (int)st.wYear,
|
||||
(int)st.wHour, (int)st.wMinute, (int)st.wMilliseconds );
|
||||
va_list _ArgList;
|
||||
va_start( _ArgList, _Format );
|
||||
vfprintf( f, _Format, _ArgList );
|
||||
fclose( f );
|
||||
}
|
||||
|
||||
const wchar_t *GameServer::getServerName() const
|
||||
{
|
||||
if( !m_loginConnection ) return L"NULL";
|
||||
return m_loginConnection->getRegisteredServerName();
|
||||
}
|
||||
|
||||
int GameServer::getServerId() const
|
||||
{
|
||||
if( !m_loginConnection ) return 0;
|
||||
return m_loginConnection->getRegisteredServerId();
|
||||
}
|
||||
|
||||
/* Server will be considered as Good when the number of online players
|
||||
* is less than half the maximum. as Normal between half and 4/5
|
||||
* and Full when there's more than 4/5 of the maximum number of players */
|
||||
ServerStatus GameServer::getServerStatus()
|
||||
{
|
||||
// TODO: ServerStatus may also be GM_ONLY
|
||||
// SERVER_STATUS_DOWN is set when server is shut down
|
||||
if( m_serverStatus != SERVER_STATUS_DOWN )
|
||||
{
|
||||
int curOnline = getCurrentOnline();
|
||||
int maxOnline = getMaxPlayers();
|
||||
float ratio = 100.0f * ((float)curOnline) / ((float)maxOnline);
|
||||
if( ratio <= 0.5f ) m_serverStatus = SERVER_STATUS_GOOD;
|
||||
if( ratio > 0.5f && ratio <= 0.8f ) m_serverStatus = SERVER_STATUS_NORMAL;
|
||||
if( ratio > 0.8f ) m_serverStatus = SERVER_STATUS_FULL;
|
||||
}
|
||||
return m_serverStatus;
|
||||
}
|
||||
|
||||
int GameServer::getMaxPlayers() const
|
||||
{
|
||||
return m_cfg->max_players;
|
||||
}
|
||||
|
||||
int GameServer::getCurrentOnline()
|
||||
{
|
||||
if( !m_clientPool ) return 0;
|
||||
return m_clientPool->getCurrentOnline();
|
||||
}
|
||||
|
||||
bool GameServer::checkFreePort( const wchar_t *bind_addr, int bind_port )
|
||||
{
|
||||
char a_addr[128] = {0};
|
||||
l2c_unicode_to_ansi( bind_addr, a_addr, 128 );
|
||||
unsigned short s_port = (unsigned short)( bind_port & 0xFFFF );
|
||||
SOCKET s = L2PNet_TCPsocket_create( true );
|
||||
if( s == INVALID_SOCKET ) return false;
|
||||
int r = L2PNet_bind( s, a_addr, s_port );
|
||||
L2PNet_shutdown( s );
|
||||
L2PNet_closesocket( s );
|
||||
return (r==0);
|
||||
}
|
||||
|
||||
ClientPool *GameServer::getClientPool() const
|
||||
{
|
||||
return m_clientPool;
|
||||
}
|
||||
|
||||
IdFactory *GameServer::getIdFactory()
|
||||
{
|
||||
return m_idf;
|
||||
}
|
||||
|
||||
bool GameServer::kickPlayerByAccount( const wchar_t *accountName, bool reasonDualLogin )
|
||||
{
|
||||
if( !accountName ) return false;
|
||||
if( !m_clientPool ) return false;
|
||||
bool ret = m_clientPool->disconnectPlayerByAccount( accountName, reasonDualLogin );
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
bool GameServer::logoutAccountFromLoginServer( const wchar_t *accountName )
|
||||
{
|
||||
if( !m_loginConnection || !accountName ) return false;
|
||||
m_loginConnection->send_PlayerLogOut( accountName );
|
||||
return true;
|
||||
}
|
||||
|
||||
void GameServer::addWaitingClientAndSendPlayerAuthRequest( const wchar_t *accountName, unsigned char *loginKey, unsigned char *playKey )
|
||||
{
|
||||
if( !m_loginConnection ) return;
|
||||
m_loginConnection->send_PlayerAuthRequest( accountName, loginKey, playKey );
|
||||
}
|
||||
|
||||
void GameServer::notifyLoginPlayerAuth( const wchar_t *account, int ok )
|
||||
{
|
||||
if( !m_clientPool ) return;
|
||||
m_clientPool->notifyClientAuthResponse( account, ok ? true : false );
|
||||
}
|
||||
|
||||
|
||||
DWORD WINAPI GameServer::GS_Thread( LPVOID lpvParam )
|
||||
{
|
||||
GameServer *gs = (GameServer *)lpvParam;
|
||||
// remember load time start
|
||||
DWORD startTick = GetTickCount();
|
||||
DWORD endTick = 0;
|
||||
int secs_load = 0;
|
||||
LogFull( false, true, RGB(0,200,0), RGB(255,255,255), L"===================================" );
|
||||
Log( L"Starting game server..." );
|
||||
|
||||
SOCKET gs_acceptor = INVALID_SOCKET;
|
||||
|
||||
try
|
||||
{
|
||||
|
||||
///////////////////////////////////
|
||||
// begin server initialization
|
||||
///////////////////////////////////
|
||||
// ensure log dir exists
|
||||
if( CreateDirectory( TEXT(".\\log"), NULL ) ) Log( L"Created logs directory." );
|
||||
// Load config
|
||||
if( !gs->m_cfg->load() ) throw Exception( "GS_Thread: error loading config!" );
|
||||
LogDebug( L"Config loaded." );
|
||||
|
||||
// check binding port is free
|
||||
if( !checkFreePort( gs->m_cfg->game_server_bind_address, gs->m_cfg->game_server_bind_port ) )
|
||||
{
|
||||
LogError( L"Error: bind address %s:%d already in use (another GS is running?)",
|
||||
gs->m_cfg->game_server_bind_address, gs->m_cfg->game_server_bind_port );
|
||||
throw Exception( "Check free bind port failed" );
|
||||
}
|
||||
|
||||
// Initialize MySQL
|
||||
if( !gs->m_mysql->initialize( gs->m_cfg->mysql_max_connections, gs->m_cfg->mysql_host,
|
||||
gs->m_cfg->mysql_user, gs->m_cfg->mysql_pass, gs->m_cfg->mysql_db ) )
|
||||
throw Exception( "MySQL init error!" );
|
||||
MysqlConnection *test_con = gs->m_mysql->getConnection();
|
||||
if( !test_con ) throw Exception( "MySQL init error (no free connections???)" );
|
||||
if( !test_con->isConnected() )
|
||||
{
|
||||
char comment[256] = {0};
|
||||
sprintf( comment, "MySQL test connection error: %S", test_con->getErrorStr() );
|
||||
gs->m_mysql->releaseConnection( test_con );
|
||||
throw Exception( comment );
|
||||
}
|
||||
LogDebug( L"MySQL connection test OK" );
|
||||
|
||||
// init IdFactory
|
||||
gs->m_idf->init();
|
||||
Log( L"IdFactory: used %u object IDs, %I64u bytes", gs->m_idf->getUsedCount(), gs->m_idf->getUsedMemoryBytes() );
|
||||
|
||||
// init & load datatables
|
||||
ItemTable::getInstance()->load();
|
||||
ClassIdTree::getInstance();
|
||||
CharTemplateTable::getInstance()->load();
|
||||
CharNameTable::getInstance();
|
||||
|
||||
// TODO: load skills...
|
||||
// TODO: load scripts/quests... TODO
|
||||
|
||||
// create listening socket
|
||||
gs_acceptor = L2PNet_TCPsocket_create( true );
|
||||
char a_bind_ip[128] = {0};
|
||||
l2c_unicode_to_ansi( gs->m_cfg->game_server_bind_address, a_bind_ip, 128 );
|
||||
if( L2PNet_bind( gs_acceptor, a_bind_ip, (unsigned short)gs->m_cfg->game_server_bind_port ) != 0 )
|
||||
{
|
||||
LogError( L"GS acceptor bind error at %s:%d!", gs->m_cfg->game_server_bind_address,
|
||||
gs->m_cfg->game_server_bind_port );
|
||||
throw Exception( "GS acceptor bind error!" );
|
||||
}
|
||||
L2PNet_listen( gs_acceptor );
|
||||
|
||||
// measure load time
|
||||
endTick = GetTickCount();
|
||||
secs_load = (endTick - startTick) / 1000;
|
||||
Log( L"Game server loaded in %d seconds (%d ms).", secs_load, (int)(endTick - startTick) );
|
||||
LogFull( false, true, RGB(0,200,0), RGB(255,255,255), L"===================================" );
|
||||
|
||||
// mark server as running
|
||||
gs->m_serverStatus = SERVER_STATUS_AUTO;
|
||||
|
||||
// Initiaite login server connection thread
|
||||
gs->m_loginConnection->start( gs->m_cfg->login_server_address,
|
||||
gs->m_cfg->login_server_auth_port, gs->m_cfg->login_protocol_version );
|
||||
|
||||
////////////////////////////////////////////
|
||||
// initiate main server loop
|
||||
////////////////////////////////////////////
|
||||
while( !gs->m_flagStop )
|
||||
{
|
||||
int r = 0, bReadyRead = 0, bReadyWrite = 0;
|
||||
r = L2PNet_select( gs_acceptor, L2PNET_SELECT_READ, 200, &bReadyRead, &bReadyWrite );
|
||||
if( r == 0 ) continue; // timeout
|
||||
if( r == -1 ) // error
|
||||
throw Exception( "GS acceptor select() error!" );
|
||||
// accepted client?
|
||||
if( bReadyRead )
|
||||
{
|
||||
char clientIp[32] = {0};
|
||||
unsigned short clientPort = 0;
|
||||
SOCKET s_cl = L2PNet_accept( gs_acceptor, clientIp, &clientPort );
|
||||
if( s_cl != INVALID_SOCKET )
|
||||
{
|
||||
LogDebug( L"Accepted client %S:%d", clientIp, (int)clientPort );
|
||||
if( !gs->m_clientPool->addClient( s_cl, clientIp, (int)clientPort ) )
|
||||
{
|
||||
LogError( L"Failed to add client %S:%d to clients list!", clientIp, (int)clientPort );
|
||||
}
|
||||
}
|
||||
else LogWarning( L"gs acceptor: invalid_socket accepted :(" );
|
||||
}
|
||||
}
|
||||
///////////////////////////////////////////
|
||||
// end main server loop
|
||||
///////////////////////////////////////////
|
||||
}
|
||||
catch( std::exception& e )
|
||||
{
|
||||
LogError( L"GS_Thread: std::exception: %S\n", e.what() );
|
||||
}
|
||||
catch( Exception& e )
|
||||
{
|
||||
LogError( L"GS_Thread: Exception: %S", e.what() );
|
||||
e.logStackTrace();
|
||||
}
|
||||
|
||||
////////////////////////////////////////////
|
||||
// initiate shutdown procedures
|
||||
////////////////////////////////////////////
|
||||
// notify login server that server will now be offline
|
||||
gs->m_serverStatus = SERVER_STATUS_DOWN;
|
||||
gs->m_loginConnection->send_ServerStatus();
|
||||
|
||||
// disconnect all players
|
||||
gs->m_clientPool->disconnectAllPlayers();
|
||||
|
||||
// TODO: save all data
|
||||
// close GS acceptor socket
|
||||
if( gs_acceptor != INVALID_SOCKET )
|
||||
{
|
||||
L2PNet_shutdown( gs_acceptor );
|
||||
L2PNet_closesocket( gs_acceptor );
|
||||
gs_acceptor = INVALID_SOCKET;
|
||||
}
|
||||
|
||||
// cleanup datatables
|
||||
CharNameTable::freeInstance();
|
||||
CharTemplateTable::freeInstance();
|
||||
ClassIdTree::freeInstance();
|
||||
ItemTable::freeInstance();
|
||||
|
||||
// close id factory
|
||||
gs->m_idf->clear();
|
||||
|
||||
// stop login conn thread
|
||||
gs->m_loginConnection->stop();
|
||||
|
||||
// mark as not running
|
||||
gs->m_isRunning = false;
|
||||
return 0;
|
||||
}
|
58
L2C_Server/GS.h
Normal file
58
L2C_Server/GS.h
Normal file
@ -0,0 +1,58 @@
|
||||
#pragma once
|
||||
#include "l2c_utils.h"
|
||||
#include "enums.h"
|
||||
#include "LS_Connection.h"
|
||||
#include "GS_Cfg.h"
|
||||
#include "net/ClientPool.h"
|
||||
#include "utils/IdFactory.h"
|
||||
|
||||
class GameServer
|
||||
{
|
||||
protected:
|
||||
GameServer(); // cannot be created - only by getInstance()
|
||||
~GameServer(); // canot be deleted - only by freeInstnce()
|
||||
public:
|
||||
static GameServer *getInstance();
|
||||
static void freeInstance();
|
||||
protected:
|
||||
static GameServer *_self;
|
||||
static int _referenceCount;
|
||||
|
||||
public:
|
||||
bool start();
|
||||
bool stop();
|
||||
bool isRunning() const;
|
||||
public:
|
||||
GameConfig *getConfig() const;
|
||||
MysqlConnection *getDBConnection();
|
||||
bool releaseDBConnection( MysqlConnection *con );
|
||||
void LogFile( const char *fn, const char *_Format, ... );
|
||||
const wchar_t *getServerName() const;
|
||||
int getServerId() const;
|
||||
ServerStatus getServerStatus();
|
||||
int getMaxPlayers() const;
|
||||
int getCurrentOnline();
|
||||
ClientPool *getClientPool() const;
|
||||
IdFactory *getIdFactory();
|
||||
|
||||
public:
|
||||
bool kickPlayerByAccount( const wchar_t *accountName, bool reasonDualLogin );
|
||||
bool logoutAccountFromLoginServer( const wchar_t *accountName );
|
||||
void addWaitingClientAndSendPlayerAuthRequest( const wchar_t *accountName, unsigned char *loginKey, unsigned char *playKey );
|
||||
void notifyLoginPlayerAuth( const wchar_t *account, int ok );
|
||||
|
||||
protected:
|
||||
CriticalSection m_gsLock;
|
||||
GameConfig *m_cfg;
|
||||
MysqlConnectionManager *m_mysql;
|
||||
LoginConnection *m_loginConnection;
|
||||
ServerStatus m_serverStatus;
|
||||
ClientPool *m_clientPool;
|
||||
IdFactory *m_idf;
|
||||
|
||||
protected:
|
||||
bool m_isRunning;
|
||||
bool m_flagStop;
|
||||
static DWORD WINAPI GS_Thread( LPVOID lpvParam );
|
||||
static bool checkFreePort( const wchar_t *bind_addr, int bind_port );
|
||||
};
|
144
L2C_Server/GS_Cfg.cpp
Normal file
144
L2C_Server/GS_Cfg.cpp
Normal file
@ -0,0 +1,144 @@
|
||||
#include "pch.h"
|
||||
#include "Log.h"
|
||||
#include "GS_Cfg.h"
|
||||
|
||||
GameConfig::GameConfig()
|
||||
{
|
||||
m_cfg_mysql = m_cfg_network = m_cfg_rates = m_cfg_hexid = m_cfg_dev = NULL;
|
||||
clear();
|
||||
}
|
||||
|
||||
GameConfig::~GameConfig()
|
||||
{
|
||||
clear();
|
||||
if( m_cfg_mysql ) { delete m_cfg_mysql; m_cfg_mysql = NULL; }
|
||||
if( m_cfg_network ) { delete m_cfg_network; m_cfg_network = NULL; }
|
||||
if( m_cfg_rates ) { delete m_cfg_rates; m_cfg_rates = NULL; }
|
||||
if( m_cfg_hexid ) { delete m_cfg_hexid; m_cfg_hexid = NULL; }
|
||||
if( m_cfg_dev ) { delete m_cfg_dev; m_cfg_dev = NULL; }
|
||||
}
|
||||
|
||||
void GameConfig::clear()
|
||||
{
|
||||
// clear MySQL settings
|
||||
mysql_host = mysql_user = mysql_pass = mysql_db = NULL;
|
||||
mysql_max_connections = 0;
|
||||
// hexid
|
||||
server_hexid = NULL;
|
||||
server_id = 0;
|
||||
accept_alternate_server_id = list_as_test_server = server_list_clock =
|
||||
server_list_brackets = false;
|
||||
// network
|
||||
login_server_address = NULL;
|
||||
login_server_auth_port = login_protocol_version = 0;
|
||||
game_server_bind_address = NULL;
|
||||
game_server_bind_port = 0;
|
||||
report_internal_hostname = report_external_hostname = NULL;
|
||||
min_game_protocol_version = max_game_protocol_version = 0;
|
||||
max_players = 0;
|
||||
// developer settings
|
||||
EverybodyHasAdminRights = Debug = Assert = false;
|
||||
}
|
||||
|
||||
bool GameConfig::load()
|
||||
{
|
||||
bool load_ok = true;
|
||||
load_ok &= load_mysql();
|
||||
load_ok &= load_hexid();
|
||||
load_ok &= load_network();
|
||||
load_ok &= load_rates();
|
||||
load_ok &= load_developer();
|
||||
return load_ok;
|
||||
}
|
||||
|
||||
bool GameConfig::load_mysql()
|
||||
{
|
||||
if( m_cfg_mysql ) delete m_cfg_mysql;
|
||||
m_cfg_mysql = new L2C_ConfigFile();
|
||||
if( !m_cfg_mysql->load( L"./config_game/mysql.conf" ) )
|
||||
{
|
||||
LogError( L"Cannot open file: ./config_game/mysql.conf!" );
|
||||
return false;
|
||||
}
|
||||
// init
|
||||
mysql_host = (wchar_t *)m_cfg_mysql->getValueStrW( L"mysql_host", L"localhost" );
|
||||
mysql_user = (wchar_t *)m_cfg_mysql->getValueStrW( L"mysql_user", L"root" );
|
||||
mysql_pass = (wchar_t *)m_cfg_mysql->getValueStrW( L"mysql_pass", L"" );
|
||||
mysql_db = (wchar_t *)m_cfg_mysql->getValueStrW( L"mysql_db", L"l2jdb" );
|
||||
mysql_max_connections = m_cfg_mysql->getValueInt( L"mysql_max_connections", 10 );
|
||||
//
|
||||
return true;
|
||||
}
|
||||
|
||||
bool GameConfig::load_hexid()
|
||||
{
|
||||
if( m_cfg_hexid ) delete m_cfg_hexid;
|
||||
m_cfg_hexid = new L2C_ConfigFile();
|
||||
if( !m_cfg_hexid->load( L"./config_game/hexid.txt" ) )
|
||||
{
|
||||
LogError( L"Cannot open file: ./config_game/hexid.txt!" );
|
||||
return false;
|
||||
}
|
||||
// init
|
||||
server_hexid = (wchar_t *)m_cfg_hexid->getValueStrW( L"HexID", L"" );
|
||||
server_id = m_cfg_hexid->getValueInt( L"ServerID", 1 );
|
||||
//
|
||||
return true;
|
||||
}
|
||||
|
||||
bool GameConfig::load_network()
|
||||
{
|
||||
if( m_cfg_network ) delete m_cfg_network;
|
||||
m_cfg_network = new L2C_ConfigFile();
|
||||
if( !m_cfg_network->load( L"./config_game/network.conf" ) )
|
||||
{
|
||||
LogError( L"Cannot open file: ./config_game/network.conf!" );
|
||||
return false;
|
||||
}
|
||||
// init
|
||||
login_server_address = (wchar_t *)m_cfg_network->getValueStrW( L"login_server_address", L"127.0.0.1" );
|
||||
login_server_auth_port = m_cfg_network->getValueInt( L"login_server_auth_port", 9014 );
|
||||
login_protocol_version = m_cfg_network->getValueInt( L"login_protocol_version", 258 );
|
||||
game_server_bind_address = (wchar_t *)m_cfg_network->getValueStrW( L"game_server_bind_address", L"0.0.0.0" );
|
||||
game_server_bind_port = m_cfg_network->getValueInt( L"game_server_bind_port", 7777 );
|
||||
report_internal_hostname = (wchar_t *)m_cfg_network->getValueStrW( L"report_internal_hostname", L"127.0.0.1" );
|
||||
report_external_hostname = (wchar_t *)m_cfg_network->getValueStrW( L"report_external_hostname", L"127.0.0.1" );
|
||||
min_game_protocol_version = m_cfg_network->getValueInt( L"min_game_protocol_version", 12 );
|
||||
max_game_protocol_version = m_cfg_network->getValueInt( L"max_game_protocol_version", 17 );
|
||||
max_players = m_cfg_network->getValueInt( L"max_players", 10 );
|
||||
// misc net
|
||||
accept_alternate_server_id = m_cfg_network->getValueBool( L"accept_alternate_server_id", false );
|
||||
list_as_test_server = m_cfg_network->getValueBool( L"list_as_test_server", false );
|
||||
server_list_clock = m_cfg_network->getValueBool( L"server_list_clock", false );
|
||||
server_list_brackets = m_cfg_network->getValueBool( L"server_list_brackets", false );
|
||||
//
|
||||
return true;
|
||||
}
|
||||
|
||||
bool GameConfig::load_developer()
|
||||
{
|
||||
if( m_cfg_dev ) delete m_cfg_dev;
|
||||
m_cfg_dev = new L2C_ConfigFile();
|
||||
if( !m_cfg_dev->load( L"./config_game/developer.conf" ) )
|
||||
{
|
||||
LogError( L"Cannot open file: ./config_game/developer.conf!" );
|
||||
return false;
|
||||
}
|
||||
// init
|
||||
EverybodyHasAdminRights = m_cfg_dev->getValueBool( L"EverybodyHasAdminRights", false );
|
||||
Assert = m_cfg_dev->getValueBool( L"Assert", false );
|
||||
Debug = m_cfg_dev->getValueBool( L"Debug", false );
|
||||
//
|
||||
return true;
|
||||
}
|
||||
|
||||
bool GameConfig::load_rates()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool GameConfig::save()
|
||||
{
|
||||
return true;
|
||||
}
|
57
L2C_Server/GS_Cfg.h
Normal file
57
L2C_Server/GS_Cfg.h
Normal file
@ -0,0 +1,57 @@
|
||||
#pragma once
|
||||
#include "l2c_utils.h"
|
||||
|
||||
class GameConfig
|
||||
{
|
||||
public:
|
||||
GameConfig();
|
||||
~GameConfig();
|
||||
public:
|
||||
void clear();
|
||||
bool load();
|
||||
bool save();
|
||||
|
||||
public:
|
||||
// MySQL settings
|
||||
wchar_t *mysql_host;
|
||||
wchar_t *mysql_user;
|
||||
wchar_t *mysql_pass;
|
||||
wchar_t *mysql_db;
|
||||
int mysql_max_connections;
|
||||
// Server auth settings
|
||||
wchar_t *server_hexid;
|
||||
int server_id;
|
||||
bool accept_alternate_server_id;
|
||||
bool list_as_test_server;
|
||||
bool server_list_clock;
|
||||
bool server_list_brackets;
|
||||
// Network settings
|
||||
wchar_t *login_server_address;
|
||||
int login_server_auth_port;
|
||||
int login_protocol_version;
|
||||
wchar_t *game_server_bind_address;
|
||||
int game_server_bind_port;
|
||||
wchar_t *report_internal_hostname;
|
||||
wchar_t *report_external_hostname;
|
||||
int min_game_protocol_version;
|
||||
int max_game_protocol_version;
|
||||
int max_players;
|
||||
// developer settings
|
||||
bool EverybodyHasAdminRights;
|
||||
bool Debug;
|
||||
bool Assert;
|
||||
|
||||
protected:
|
||||
L2C_ConfigFile *m_cfg_mysql;
|
||||
L2C_ConfigFile *m_cfg_hexid;
|
||||
L2C_ConfigFile *m_cfg_network;
|
||||
L2C_ConfigFile *m_cfg_dev;
|
||||
L2C_ConfigFile *m_cfg_rates;
|
||||
|
||||
protected:
|
||||
bool load_mysql();
|
||||
bool load_hexid();
|
||||
bool load_network();
|
||||
bool load_developer();
|
||||
bool load_rates();
|
||||
};
|
BIN
L2C_Server/L2C.ico
Normal file
BIN
L2C_Server/L2C.ico
Normal file
Binary file not shown.
After Width: | Height: | Size: 3.2 KiB |
213
L2C_Server/L2Serverd.cpp
Normal file
213
L2C_Server/L2Serverd.cpp
Normal file
@ -0,0 +1,213 @@
|
||||
#include "pch.h"
|
||||
#include "Resource.h"
|
||||
#include "Log.h"
|
||||
#include "GS_Cfg.h"
|
||||
#include "LS_Connection.h"
|
||||
#include "GS.h"
|
||||
#include "utils/Debugging.h"
|
||||
#include "utils/Exception.h"
|
||||
|
||||
#pragma comment(linker,"/manifestdependency:\"type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='*' publicKeyToken='6595b64144ccf1df' language='*'\"")
|
||||
|
||||
HINSTANCE g_hInst = NULL;
|
||||
|
||||
void Main_StartServer()
|
||||
{
|
||||
GameServer *gs = GameServer::getInstance();
|
||||
if( !gs->start() ) LogError( L"GS Start failed!" );
|
||||
}
|
||||
|
||||
void Main_StopServer()
|
||||
{
|
||||
GameServer *gs = GameServer::getInstance();
|
||||
if( !gs->isRunning() ) return;
|
||||
if( !gs->stop() ) LogError( L"GS Stop failed!" );
|
||||
else Log( L"GS Stopped." );
|
||||
}
|
||||
|
||||
void Main_OnTestExceptions()
|
||||
{
|
||||
LogFull( true, true, RGB(255,128,128), RGB(255,255,255), L"===== Throwing an exception... =====" );
|
||||
try
|
||||
{
|
||||
throw Exception( "test exception: max players: %d", GameServer::getInstance()->getMaxPlayers() );
|
||||
}
|
||||
catch( Exception& e )
|
||||
{
|
||||
LogError( L"Got exception: %S", e.what() );
|
||||
e.logStackTrace();
|
||||
}
|
||||
LogFull( true, true, RGB(255,128,128), RGB(255,255,255), L"===== Exceptions test complete =====" );
|
||||
}
|
||||
|
||||
|
||||
void Main_OnInitDialog( HWND hDlg )
|
||||
{
|
||||
#ifdef _DEBUG
|
||||
SetWindowText( hDlg, TEXT("L2 Game Server (DEBUG)" ) );
|
||||
#endif
|
||||
// rect
|
||||
RECT rc;
|
||||
GetClientRect( hDlg, &rc );
|
||||
HICON hIcon = LoadIcon( g_hInst, MAKEINTRESOURCE(IDI_MAIN) );
|
||||
SendMessage( hDlg, WM_SETICON, ICON_BIG, (LPARAM)hIcon );
|
||||
SendMessage( hDlg, WM_SETICON, ICON_SMALL, (LPARAM)hIcon );
|
||||
// create richedit
|
||||
g_hWndLog = LogWnd_Create( hDlg );
|
||||
// start server
|
||||
Main_StartServer();
|
||||
}
|
||||
|
||||
void Main_OnClose( HWND hDlg )
|
||||
{
|
||||
EndDialog( hDlg, IDOK );
|
||||
}
|
||||
|
||||
void Main_OnButtonServer( HWND hDlg )
|
||||
{
|
||||
POINT pt;
|
||||
GetCursorPos( &pt );
|
||||
HMENU hMenu = CreatePopupMenu();
|
||||
AppendMenu( hMenu, MF_STRING, 1, TEXT("Start") );
|
||||
AppendMenu( hMenu, MF_STRING, 2, TEXT("Stop") );
|
||||
int cmd = TrackPopupMenu( hMenu, TPM_NONOTIFY | TPM_RETURNCMD, pt.x, pt.y, 0, hDlg, NULL );
|
||||
DestroyMenu( hMenu );
|
||||
switch( cmd )
|
||||
{
|
||||
case 1: Main_StartServer(); break;
|
||||
case 2: Main_StopServer(); break;
|
||||
}
|
||||
}
|
||||
|
||||
void Main_OnButtonDebug( HWND hDlg )
|
||||
{
|
||||
POINT pt;
|
||||
GetCursorPos( &pt );
|
||||
HMENU hMenu = CreatePopupMenu();
|
||||
AppendMenu( hMenu, MF_STRING, 1, TEXT("Dump ClientPool") );
|
||||
AppendMenu( hMenu, MF_STRING, 2, TEXT("Test exceptions and stack trace") );
|
||||
int cmd = TrackPopupMenu( hMenu, TPM_NONOTIFY | TPM_RETURNCMD, pt.x, pt.y, 0, hDlg, NULL );
|
||||
DestroyMenu( hMenu );
|
||||
switch( cmd )
|
||||
{
|
||||
case 1:
|
||||
{
|
||||
if( GameServer::getInstance()->getClientPool() )
|
||||
GameServer::getInstance()->getClientPool()->dumpClientsToLog();
|
||||
} break;
|
||||
case 2:
|
||||
{
|
||||
Main_OnTestExceptions();
|
||||
} break;
|
||||
}
|
||||
}
|
||||
|
||||
void Main_OnSize( HWND hDlg, WPARAM wParam, LPARAM lParam )
|
||||
{
|
||||
UNREFERENCED_PARAMETER(wParam);
|
||||
int w = LOWORD(lParam);
|
||||
int h = HIWORD(lParam);
|
||||
const int controlswnd_h = 80;
|
||||
HWND hWnd = GetDlgItem( hDlg, IDC_STATIC_CONTROLS );
|
||||
MoveWindow( hWnd, 10, h-controlswnd_h-10, w-20, controlswnd_h, TRUE );
|
||||
hWnd = GetDlgItem( hDlg, IDC_B_SERVER );
|
||||
MoveWindow( hWnd, 20, h-controlswnd_h-10+20, 50, 25, TRUE );
|
||||
hWnd = GetDlgItem( hDlg, IDC_B_DEBUG );
|
||||
MoveWindow( hWnd, 80, h-controlswnd_h-10+20, 50, 25, TRUE );
|
||||
// log wnd
|
||||
MoveWindow( g_hWndLog, 10, 10, w-20, h-controlswnd_h-10-10-10, TRUE );
|
||||
}
|
||||
|
||||
void Main_OnCommand( HWND hDlg, WPARAM wParam, LPARAM lParam )
|
||||
{
|
||||
UNREFERENCED_PARAMETER(lParam);
|
||||
switch( LOWORD(wParam) )
|
||||
{
|
||||
case IDC_B_SERVER: Main_OnButtonServer( hDlg ); break;
|
||||
case IDC_B_DEBUG: Main_OnButtonDebug( hDlg ); break;
|
||||
}
|
||||
}
|
||||
|
||||
INT_PTR CALLBACK MainDlgProc( HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam )
|
||||
{
|
||||
switch( uMsg )
|
||||
{
|
||||
case WM_INITDIALOG: Main_OnInitDialog( hDlg ); break;
|
||||
case WM_CLOSE: Main_OnClose( hDlg ); break;
|
||||
case WM_SIZE: Main_OnSize( hDlg, wParam, lParam ); break;
|
||||
case WM_COMMAND: Main_OnCommand( hDlg, wParam, lParam ); break;
|
||||
default: return FALSE; break;
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
|
||||
int WINAPI wWinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPWSTR lpszCmdLine, int nShowCmd )
|
||||
{
|
||||
g_hInst = hInstance;
|
||||
hPrevInstance = NULL;
|
||||
lpszCmdLine = NULL; // cmdline ignored...
|
||||
nShowCmd = 0; // ignored
|
||||
|
||||
// WinXP visual styles / common controls
|
||||
INITCOMMONCONTROLSEX iccex;
|
||||
iccex.dwSize = sizeof(INITCOMMONCONTROLSEX);
|
||||
iccex.dwICC = ICC_TAB_CLASSES | ICC_LISTVIEW_CLASSES;
|
||||
InitCommonControlsEx( &iccex );
|
||||
// winsock
|
||||
L2PNet_InitDefault();
|
||||
|
||||
// richedit
|
||||
HINSTANCE hInstRiched = LoadLibrary( TEXT("Riched20") );
|
||||
if( !hInstRiched )
|
||||
{
|
||||
MessageBox( NULL, TEXT("Cannot load Riched20.dll"), TEXT("Load error!"), MB_ICONSTOP );
|
||||
return 1;
|
||||
}
|
||||
// setup logging
|
||||
CreateDirectoryW( L"./log", NULL );
|
||||
FILE *flog = _wfopen( L"log/gameserver.log", L"wt" );
|
||||
Log_EnableLogToFile( true, flog );
|
||||
// init OpenSSL
|
||||
SSL_library_init();
|
||||
SSL_load_error_strings();
|
||||
void *rand_buf = malloc( 1024 );
|
||||
if( rand_buf )
|
||||
{
|
||||
RAND_seed( rand_buf, 1024 );
|
||||
free( rand_buf );
|
||||
rand_buf = NULL;
|
||||
}
|
||||
srand( (unsigned)time( NULL ) );
|
||||
|
||||
// initialize debugging
|
||||
Debug::getInstance();
|
||||
|
||||
// run dialog!
|
||||
try
|
||||
{
|
||||
DialogBoxParam( hInstance, MAKEINTRESOURCE(IDD_MAIN), NULL, MainDlgProc, 0 );
|
||||
}
|
||||
catch( std::exception& stde ) {
|
||||
MessageBoxA( NULL, stde.what(), "WinMain: std::exception:", MB_ICONSTOP ); // warning C4702: unreachable code? WTF???
|
||||
} catch( Exception& e ) {
|
||||
MessageBoxA( NULL, e.what(), "WinMain: Exception:", MB_ICONSTOP );
|
||||
} catch( ... ) {
|
||||
MessageBox( NULL, L"catch( ... )", L"WinMain: unknown exception!", MB_ICONSTOP );
|
||||
}
|
||||
|
||||
// close server
|
||||
Main_StopServer();
|
||||
GameServer::freeInstance();
|
||||
|
||||
// free debugging
|
||||
Debug::freeInstance();
|
||||
|
||||
// stop logging to file (close file)
|
||||
Log_EnableLogToFile( false, NULL );
|
||||
// cleanup winsock
|
||||
L2PNet_Cleanup();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
44
L2C_Server/L2Serverd.rc
Normal file
44
L2C_Server/L2Serverd.rc
Normal file
@ -0,0 +1,44 @@
|
||||
#include <windows.h>
|
||||
#include "Resource.h"
|
||||
#define IDC_STATIC -1
|
||||
|
||||
// icon
|
||||
IDI_MAIN ICON "L2C.ico"
|
||||
|
||||
// version
|
||||
VS_VERSION_INFO VERSIONINFO
|
||||
FILEVERSION 0,0,0,1
|
||||
PRODUCTVERSION 0,0,0,1
|
||||
FILEOS 0x00000004
|
||||
FILETYPE 0x00000001
|
||||
BEGIN
|
||||
BLOCK "StringFileInfo"
|
||||
BEGIN
|
||||
BLOCK "041904B0"
|
||||
BEGIN
|
||||
VALUE "FileDescription", "L2Serverd\0"
|
||||
VALUE "FileVersion", "0, 0, 0, 9\0"
|
||||
VALUE "InternalName", "L2Serverd\0"
|
||||
VALUE "LegalCopyright", "Public Domain (FreeForAll)\0"
|
||||
VALUE "OriginalFilename", "L2Serverd.exe\0"
|
||||
VALUE "ProductName", "L2Serverd\0"
|
||||
VALUE "ProductVersion", "0, 0, 0, 1\0"
|
||||
END
|
||||
END
|
||||
BLOCK "VarFileInfo"
|
||||
BEGIN
|
||||
VALUE "Translation", 0x0419, 0x04B0
|
||||
END
|
||||
END
|
||||
|
||||
// dialog
|
||||
IDD_MAIN DIALOGEX 26,16,365,200
|
||||
CAPTION "L2 Game Server"
|
||||
FONT 8,"MS Shell Dlg",400,0,0
|
||||
STYLE WS_VISIBLE|WS_OVERLAPPEDWINDOW
|
||||
BEGIN
|
||||
CONTROL "Controls",IDC_STATIC_CONTROLS,"Button",WS_CHILD|WS_VISIBLE|BS_GROUPBOX,4,151,354,43
|
||||
CONTROL "Server",IDC_B_SERVER,"Button",WS_CHILD|WS_VISIBLE|WS_TABSTOP,14,166,60,19
|
||||
CONTROL "Debug",IDC_B_DEBUG,"Button",WS_CHILD|WS_VISIBLE|WS_TABSTOP,80,166,60,19
|
||||
END
|
||||
|
494
L2C_Server/LS_Connection.cpp
Normal file
494
L2C_Server/LS_Connection.cpp
Normal file
@ -0,0 +1,494 @@
|
||||
#include "pch.h"
|
||||
#include "Log.h"
|
||||
#include "l2c_utils.h"
|
||||
#include "LS_Connection.h"
|
||||
#include "GS.h"
|
||||
#include "utils/Exception.h"
|
||||
|
||||
LoginConnection::LoginConnection()
|
||||
{
|
||||
m_ls_addr[0] = 0;
|
||||
m_ls_port = m_ls_protover = 0;
|
||||
m_isRunning = m_flagStop = m_isConnected = false;
|
||||
m_sock = INVALID_SOCKET;
|
||||
m_bfKey[0] = 0;
|
||||
m_bfKeyLen = 0;
|
||||
m_rsaKey = NULL;
|
||||
m_registeredServerName[0] = 0;
|
||||
m_registeredServerId = 0;
|
||||
}
|
||||
|
||||
LoginConnection::~LoginConnection()
|
||||
{
|
||||
stop();
|
||||
m_ls_addr[0] = 0;
|
||||
m_ls_port = m_ls_protover = 0;
|
||||
m_isRunning = m_flagStop = m_isConnected = false;
|
||||
if( m_sock != INVALID_SOCKET )
|
||||
{
|
||||
L2PNet_shutdown( m_sock );
|
||||
L2PNet_closesocket( m_sock );
|
||||
}
|
||||
m_sock = INVALID_SOCKET;
|
||||
m_bfKey[0] = 0;
|
||||
m_bfKeyLen = 0;
|
||||
if( m_rsaKey )
|
||||
{
|
||||
RSA_free( m_rsaKey );
|
||||
m_rsaKey = NULL;
|
||||
}
|
||||
m_registeredServerName[0] = 0;
|
||||
m_registeredServerId = 0;
|
||||
}
|
||||
|
||||
int LoginConnection::getRegisteredServerId() const
|
||||
{
|
||||
return m_registeredServerId;
|
||||
}
|
||||
|
||||
const wchar_t *LoginConnection::getRegisteredServerName() const
|
||||
{
|
||||
return (const wchar_t *)m_registeredServerName;
|
||||
}
|
||||
|
||||
void LoginConnection::start( const wchar_t *ls_addr, int ls_port, int ls_protover )
|
||||
{
|
||||
if( m_isRunning ) return;
|
||||
HANDLE hThread = NULL;
|
||||
unsigned int tid = 0;
|
||||
m_flagStop = false;
|
||||
m_lock.Lock();
|
||||
{
|
||||
hThread = (HANDLE)_beginthreadex( NULL, 0,
|
||||
(unsigned int (__stdcall *)(void *))LoginConnection::LS_ConnThread, (void *)this,
|
||||
0, &tid );
|
||||
if( hThread )
|
||||
{
|
||||
wcsncpy( m_ls_addr, ls_addr, 127 ); m_ls_addr[127] = 0;
|
||||
m_ls_port = ls_port;
|
||||
m_ls_protover = ls_protover;
|
||||
m_isRunning = true;
|
||||
CloseHandle( hThread );
|
||||
}
|
||||
}
|
||||
m_lock.Unlock();
|
||||
}
|
||||
|
||||
void LoginConnection::stop()
|
||||
{
|
||||
if( !m_isRunning ) return;
|
||||
m_lock.Lock();
|
||||
m_flagStop = true;
|
||||
while( m_isRunning ) Sleep(50);
|
||||
m_flagStop = false;
|
||||
m_registeredServerName[0] = 0;
|
||||
m_registeredServerId = 0;
|
||||
m_lock.Unlock();
|
||||
//LogDebug( L"LS Connection stopped." ); // deadlock in SendMessage() :(
|
||||
}
|
||||
|
||||
DWORD WINAPI LoginConnection::LS_ConnThread( LPVOID lpvParam )
|
||||
{
|
||||
LoginConnection *lc = (LoginConnection *)lpvParam;
|
||||
//lc->m_isRunning = true; // not here, see line 107
|
||||
unsigned char *packbuffer = NULL;
|
||||
bool should_retry_connect = true;
|
||||
int connect_tries = 0;
|
||||
char a_ls_addr[128] = {0};
|
||||
l2c_unicode_to_ansi( lc->m_ls_addr, a_ls_addr, 128 );
|
||||
int bReadyRead = 0, bReadyWrite = 0, r = 0;
|
||||
unsigned int rcvdLen = 0;
|
||||
// initialize initial BF key
|
||||
char initial_bfKey[] = "_;v.]05-31!|+-%xT!^[$\00";
|
||||
unsigned int opcode = 0;
|
||||
L2LoginPacket *pack = NULL;
|
||||
|
||||
retry_connect:
|
||||
lc->m_isRunning = true; // only now we started work!
|
||||
lc->m_isConnected = false; // not connected
|
||||
connect_tries++;
|
||||
if( connect_tries == 1 )
|
||||
Log( L"Connecting to login at %s:%d...", lc->m_ls_addr, lc->m_ls_port );
|
||||
else
|
||||
Log( L"Connecting to login at %s:%d... (try #%d)", lc->m_ls_addr, lc->m_ls_port, connect_tries );
|
||||
|
||||
try
|
||||
{
|
||||
packbuffer = (unsigned char *)malloc( 64*1024 ); // 64 Kb enough
|
||||
if( !packbuffer ) throw std::bad_alloc( "packbuffer (64 Kb)" );
|
||||
|
||||
lc->m_sock = L2PNet_TCPsocket_create( true );
|
||||
L2PNet_connect( lc->m_sock, a_ls_addr, (unsigned short)lc->m_ls_port );
|
||||
|
||||
bReadyRead = 0, bReadyWrite = 0, r = 0;
|
||||
r = L2PNet_select( lc->m_sock, L2PNET_SELECT_WRITE, 5000, &bReadyRead, &bReadyWrite );
|
||||
if( !bReadyWrite ) throw Exception( "Connect to LS failed!" );
|
||||
if( GameServer::getInstance()->getConfig()->Debug ) LogDebug( L"Connected to login." );
|
||||
lc->m_isConnected = true; // we're connected!
|
||||
|
||||
// suddenly we're signaled to stop?
|
||||
if( lc->m_flagStop ) goto lsconn_cleanup;
|
||||
|
||||
rcvdLen = 0;
|
||||
memcpy( lc->m_bfKey, initial_bfKey, 23 );
|
||||
lc->m_bfKey[22] = '0';
|
||||
lc->m_bfKeyLen = 22;
|
||||
|
||||
while( !lc->m_flagStop )
|
||||
{
|
||||
r = L2PNet_select( lc->m_sock, L2PNET_SELECT_READ, 200, &bReadyRead, &bReadyWrite );
|
||||
if( r == 0 ) continue; // timeout
|
||||
if( r == -1 ) throw Exception( "Network select() error" );
|
||||
r = L2PacketReceive_buffer( lc->m_sock, 2000, &rcvdLen, packbuffer );
|
||||
if( r <= 0 ) throw Exception( "recv packet failed" );
|
||||
// packet received OK
|
||||
// decode blowfish
|
||||
pack = new L2LoginPacket( packbuffer, rcvdLen );
|
||||
pack->setDynamicBFKey( lc->m_bfKey, lc->m_bfKeyLen );
|
||||
pack->decodeBlowfish( false );
|
||||
// validate checksum
|
||||
if( !pack->verifyChecksum() ) throw Exception( "Invalid packet checksum!" );
|
||||
#ifdef _DEBUG
|
||||
LogDebug( L"DBG: LoginConnection rcv: %u bytes (opcode %02X)", rcvdLen, (unsigned int)pack->getByteAt(2) );
|
||||
#endif
|
||||
// handle packet
|
||||
opcode = (unsigned int)pack->getByteAt(2);
|
||||
switch( opcode )
|
||||
{
|
||||
case 0x00: lc->ph_InitLS( pack ); break;
|
||||
case 0x01: lc->ph_LoginServerFail( pack ); break;
|
||||
case 0x02: lc->ph_LoginServerOK( pack ); break;
|
||||
case 0x03: lc->ph_PlayerAuthResponse( pack ); break;
|
||||
case 0x04: lc->ph_KickPlayer( pack ); break;
|
||||
}
|
||||
// free pack mem
|
||||
if( pack ) delete pack;
|
||||
pack = NULL;
|
||||
}
|
||||
}
|
||||
catch( Exception& e )
|
||||
{
|
||||
if( !lc->m_flagStop ) // do not write log if signaled to stop (deadlock in SendMessage)
|
||||
{
|
||||
LogError( L"LoginConnection thread: Exception: %S", e.what() );
|
||||
e.logStackTrace();
|
||||
}
|
||||
}
|
||||
catch( std::bad_alloc& ba )
|
||||
{
|
||||
LogError( L"LoginConnection thread: bad memory allocation: %S", ba.what() );
|
||||
}
|
||||
catch( std::exception& e )
|
||||
{
|
||||
LogError( L"LoginConnection thread: error: %S", e.what() );
|
||||
}
|
||||
|
||||
lsconn_cleanup:
|
||||
// cleanup
|
||||
if( packbuffer ) free( packbuffer );
|
||||
packbuffer = NULL;
|
||||
if( pack ) delete pack;
|
||||
pack = NULL;
|
||||
L2PNet_shutdown( lc->m_sock );
|
||||
L2PNet_closesocket( lc->m_sock );
|
||||
lc->m_sock = INVALID_SOCKET;
|
||||
lc->m_bfKey[0] = 0;
|
||||
lc->m_bfKeyLen = 0;
|
||||
if( lc->m_rsaKey )
|
||||
{
|
||||
RSA_free( lc->m_rsaKey );
|
||||
lc->m_rsaKey = NULL;
|
||||
}
|
||||
lc->m_registeredServerName[0] = 0;
|
||||
lc->m_registeredServerId = 0;
|
||||
lc->m_isRunning = false; // mark as stopped
|
||||
lc->m_isConnected = false; // mark as not connected
|
||||
|
||||
// should we retry and connect again?
|
||||
if( lc->m_flagStop ) should_retry_connect = false;
|
||||
if( should_retry_connect )
|
||||
{
|
||||
if( GameServer::getInstance()->isRunning() )
|
||||
should_retry_connect = true;
|
||||
else
|
||||
should_retry_connect = false;
|
||||
}
|
||||
if( should_retry_connect ) goto retry_connect;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void LoginConnection::ph_InitLS( L2LoginPacket *pack )
|
||||
{
|
||||
// ddc[b64]
|
||||
unsigned char rsa_modulus[128];
|
||||
memset( rsa_modulus, 0, sizeof(rsa_modulus) );
|
||||
//
|
||||
pack->getPacketType(); // 0x00 - InitLS
|
||||
int login_proto_rev = pack->readD(); // login protocol rev
|
||||
unsigned int rsa_keylen = pack->readD() - 1; //
|
||||
pack->readC(); // ?? wtf
|
||||
pack->readBytes( rsa_modulus, rsa_keylen );
|
||||
//
|
||||
GameServer *gs = GameServer::getInstance();
|
||||
if( login_proto_rev != gs->getConfig()->login_protocol_version )
|
||||
LogWarning( L"Warning! login server sent protocol version %d (must be %d)",
|
||||
login_proto_rev, gs->getConfig()->login_protocol_version );
|
||||
if( gs->getConfig()->Debug )
|
||||
LogDebug( L"LoginConnection: got InitLS, login protover = %u", login_proto_rev );
|
||||
//
|
||||
RSA *rsaKey = RSA_new();
|
||||
BN_dec2bn( &(rsaKey->e), "65537" );
|
||||
rsaKey->n = BN_bin2bn( rsa_modulus, rsa_keylen, NULL );
|
||||
m_rsaKey = rsaKey;
|
||||
|
||||
send_BlowfishKey();
|
||||
send_GameServerAuthRequest();
|
||||
}
|
||||
|
||||
void LoginConnection::ph_LoginServerFail( L2LoginPacket *pack )
|
||||
{
|
||||
// c
|
||||
pack->getPacketType();
|
||||
unsigned int reason = (unsigned int)pack->readC();
|
||||
bool mustThrow = true;
|
||||
wchar_t strReason[32] = {0};
|
||||
wcscpy( strReason, L"<unknown>" );
|
||||
switch( reason )
|
||||
{
|
||||
case NOT_AUTHED: wcscpy( strReason, L"NOT_AUTHED" ); break;
|
||||
case REASON_ALREADY_LOGGED_IN: wcscpy( strReason, L"REASON_ALREADY_LOGGED_IN" ); mustThrow = false; break;
|
||||
case REASON_ID_RESERVED: wcscpy( strReason, L"REASON_ID_RESERVED" ); break;
|
||||
case REASON_IP_BANNED: wcscpy( strReason, L"REASON_IP_BANNED" ); break;
|
||||
case REASON_NO_FREE_ID: wcscpy( strReason, L"REASON_NO_FREE_ID" ); break;
|
||||
case REASON_WRONG_HEXID: wcscpy( strReason, L"REASON_WRONG_HEXID" ); break;
|
||||
}
|
||||
if( mustThrow )
|
||||
{
|
||||
LogError( L"Failed to auth on login server! Error code %u (%s)", reason, strReason );
|
||||
throw Exception( "LS auth failed!" );
|
||||
}
|
||||
LogWarning( L"Failed to auth on login server! Error code %u (%s)", reason, strReason );
|
||||
}
|
||||
|
||||
void LoginConnection::ph_LoginServerOK( L2LoginPacket *pack )
|
||||
{
|
||||
// cS
|
||||
pack->getPacketType();
|
||||
m_registeredServerId = (int)pack->readUChar();
|
||||
wcsncpy( m_registeredServerName, pack->readUnicodeStringPtr(), 255 );
|
||||
m_registeredServerName[255] = 0;
|
||||
Log( L"Registered on login as server #%d (%s)", m_registeredServerId, m_registeredServerName );
|
||||
//
|
||||
send_ServerStatus();
|
||||
}
|
||||
|
||||
void LoginConnection::ph_PlayerAuthResponse( L2LoginPacket *pack )
|
||||
{
|
||||
// Sc
|
||||
GameServer *gs = GameServer::getInstance();
|
||||
pack->getPacketType();
|
||||
wchar_t account[256] = {0};
|
||||
wcsncpy( account, pack->readUnicodeStringPtr(), 255 ); account[255] = 0;
|
||||
int ok = (int)pack->readUChar();
|
||||
//
|
||||
if( GameServer::getInstance()->getConfig()->Debug )
|
||||
LogDebug( L"LS PlayerAuthResponse: account [%s] response: %d", account, ok );
|
||||
//
|
||||
gs->notifyLoginPlayerAuth( account, ok );
|
||||
}
|
||||
|
||||
void LoginConnection::ph_KickPlayer( L2LoginPacket *pack )
|
||||
{
|
||||
// S
|
||||
pack->getPacketType();
|
||||
wchar_t account[256] = {0};
|
||||
wcsncpy( account, pack->readUnicodeStringPtr(), 255 ); account[255] = 0;
|
||||
//
|
||||
GameServer *gs = GameServer::getInstance();
|
||||
if( gs->getConfig()->Debug ) Log( L"LS KickPlayer acc [%s]", account );
|
||||
gs->kickPlayerByAccount( account, true );
|
||||
}
|
||||
|
||||
|
||||
|
||||
void LoginConnection::send_BlowfishKey()
|
||||
{
|
||||
// now reply with BlowfishKey
|
||||
// construct array of 64 bytes, first 24 of them are NULLs, next 40 are new BF key.
|
||||
unsigned char block[64];
|
||||
unsigned char block_e[64];
|
||||
memset( block, 0, sizeof(block) );
|
||||
memset( block_e, 0, sizeof(block_e) );
|
||||
srand( (unsigned int)time(NULL) );
|
||||
int i;
|
||||
for( i=0; i<40; i++ ) block[24+i] = (unsigned char)( (rand() % 255) & 0xFF );
|
||||
|
||||
// encode block with RSA key
|
||||
int r = RSA_public_encrypt( 64, block, block_e, m_rsaKey, RSA_NO_PADDING );
|
||||
if( r == -1 )
|
||||
{
|
||||
LogError( L"send BlowfishKey: RSA_public_encrypt() error!" );
|
||||
m_flagStop = true;
|
||||
return;
|
||||
}
|
||||
// construct packet BlowfishKey
|
||||
L2LoginPacket p_bf;
|
||||
p_bf.setPacketType( 0x00 );
|
||||
p_bf.writeUInt( 64 ); // length of block
|
||||
p_bf.writeBytes( block_e, 64 ); // rsa encrypted block with new BF key
|
||||
|
||||
// send it!
|
||||
sendLSPacket( &p_bf );
|
||||
|
||||
// after send! save generated bf key as our new key
|
||||
memcpy( m_bfKey, block+24, 40 );
|
||||
m_bfKeyLen = 40;
|
||||
|
||||
// now we can free RSA key - we dont need it any more?
|
||||
RSA_free( m_rsaKey );
|
||||
m_rsaKey = NULL;
|
||||
|
||||
//
|
||||
if( GameServer::getInstance()->getConfig()->Debug ) LogDebug( L"LoginConnection: sent BlowfishKey" );
|
||||
}
|
||||
|
||||
void LoginConnection::send_GameServerAuthRequest()
|
||||
{
|
||||
GameServer *gs = GameServer::getInstance();
|
||||
L2LoginPacket p;
|
||||
p.setPacketType( 0x01 );
|
||||
p.writeUChar( (unsigned char)(gs->getConfig()->server_id & 0xFF) );
|
||||
p.writeUChar( gs->getConfig()->accept_alternate_server_id ? 0x01 : 0x00 );
|
||||
p.writeUChar( 0x00 ); // host reserved = false
|
||||
p.writeS( gs->getConfig()->report_external_hostname );
|
||||
p.writeS( gs->getConfig()->report_internal_hostname );
|
||||
p.writeH( (short)gs->getConfig()->game_server_bind_port );
|
||||
p.writeD( gs->getConfig()->max_players );
|
||||
// write hexid as byte array
|
||||
wchar_t *server_hexid = gs->getConfig()->server_hexid;
|
||||
int hexid_len = wcslen( server_hexid );
|
||||
int i;
|
||||
p.writeD( hexid_len/2 );
|
||||
unsigned int val = 0;
|
||||
wchar_t temp_ws[3] = {0,0,0};
|
||||
for( i=0; i<hexid_len; i+=2 )
|
||||
{
|
||||
temp_ws[0] = server_hexid[i];
|
||||
temp_ws[1] = server_hexid[i+1];
|
||||
swscanf( temp_ws, L"%x", &val );
|
||||
p.writeUChar( (unsigned char)(val & 0xFF) );
|
||||
}
|
||||
// send!
|
||||
sendLSPacket( &p );
|
||||
//
|
||||
if( gs->getConfig()->Debug ) LogDebug( L"LoginConnection: sent GameServerAuthRequest" );
|
||||
}
|
||||
|
||||
void LoginConnection::send_PlayerInGame( const wchar_t *accountName )
|
||||
{
|
||||
L2LoginPacket pack;
|
||||
pack.setPacketType( 0x02 );
|
||||
pack.writeH( 1 ); // 1 account name
|
||||
pack.writeS( accountName );
|
||||
sendLSPacket( &pack );
|
||||
}
|
||||
|
||||
void LoginConnection::send_PlayerLogOut( const wchar_t *accountName )
|
||||
{
|
||||
L2LoginPacket pack;
|
||||
pack.setPacketType( 0x03 );
|
||||
pack.writeS( accountName );
|
||||
sendLSPacket( &pack );
|
||||
}
|
||||
|
||||
void LoginConnection::send_ChangeAccessLevel( const wchar_t *accountName, int new_access_level )
|
||||
{
|
||||
L2LoginPacket pack;
|
||||
pack.setPacketType( 0x04 );
|
||||
pack.writeD( new_access_level );
|
||||
pack.writeS( accountName );
|
||||
sendLSPacket( &pack );
|
||||
}
|
||||
|
||||
void LoginConnection::send_PlayerAuthRequest( const wchar_t *accountName, unsigned char *loginKey, unsigned char *playKey )
|
||||
{
|
||||
L2LoginPacket pack;
|
||||
pack.setPacketType( 0x05 );
|
||||
pack.writeS( accountName );
|
||||
pack.writeBytes( playKey, 8 );
|
||||
pack.writeBytes( loginKey, 8 );
|
||||
sendLSPacket( &pack );
|
||||
}
|
||||
|
||||
void LoginConnection::send_ServerStatus()
|
||||
{
|
||||
GameServer *gs = GameServer::getInstance();
|
||||
L2LoginPacket p;
|
||||
p.setPacketType( 0x06 ); // opcode
|
||||
//
|
||||
p.writeD( 5 ); // parameters count
|
||||
// first - server status
|
||||
p.writeD( SERVER_LIST_STATUS );
|
||||
p.writeD( gs->getServerStatus() );
|
||||
// 2 - SERVER_LIST_CLOCK
|
||||
p.writeD( SERVER_LIST_CLOCK );
|
||||
p.writeD( gs->getConfig()->server_list_clock ? 0x01 : 0x00 );
|
||||
// 3 - SERVER_LIST_SQUARE_BRACKET
|
||||
p.writeD( SERVER_LIST_SQUARE_BRACKET );
|
||||
p.writeD( gs->getConfig()->server_list_brackets ? 0x01 : 0x00 );
|
||||
// 4 - max players
|
||||
p.writeD( SERVER_LIST_MAX_PLAYERS );
|
||||
p.writeD( gs->getMaxPlayers() );
|
||||
// 5 - list as test server
|
||||
p.writeD( SERVER_LIST_TEST_SERVER );
|
||||
p.writeD( gs->getConfig()->list_as_test_server ? 0x01 : 0x00 );
|
||||
//
|
||||
sendLSPacket( &p );
|
||||
}
|
||||
|
||||
void LoginConnection::sendLSPacket( L2LoginPacket *pack )
|
||||
{
|
||||
// asserts
|
||||
if( !pack ) return;
|
||||
if( m_sock == INVALID_SOCKET ) return; // socket down
|
||||
if( !m_isRunning ) return; // login connection is stopped
|
||||
if( !m_isConnected ) return; // maybe login connection thread is running, trying to connect, but not connected
|
||||
//
|
||||
unsigned int sentLen = 0;
|
||||
L2LoginPacket *packet_copy = new L2LoginPacket( pack->getBytesPtr(), pack->getPacketSize() );
|
||||
// pad packet to pad_step-byte border
|
||||
const int pad_step = 8;
|
||||
int dsize = packet_copy->getDataSize();
|
||||
int rest = pad_step - (dsize % pad_step);
|
||||
if( (rest > 0) && (rest < pad_step) ) // need alignment?
|
||||
{
|
||||
//LogDebug( L"Need padding %d bytes to %d byte-border", rest, pad_step );
|
||||
int i;
|
||||
for( i=0; i<rest; i++ ) packet_copy->writeC( 0x00 );
|
||||
}
|
||||
// now padded to 8-byte border; since we don't need to append more
|
||||
// 4 bytes after checksum (appendChecksum( false )), write 4 bytes before chksum
|
||||
// to keep padding to 8-byte border after checksum is appended
|
||||
packet_copy->writeD( 0x00000000 );
|
||||
// finally, checksum (4 bytes)
|
||||
packet_copy->appendChecksum( false );
|
||||
// now we can encode blowfish
|
||||
packet_copy->setDynamicBFKey( m_bfKey, m_bfKeyLen );
|
||||
packet_copy->encodeBlowfish( false );
|
||||
//
|
||||
m_lock.Lock();
|
||||
int r = L2PacketSend( m_sock, packet_copy, 2000, &sentLen );
|
||||
m_lock.Unlock();
|
||||
if( r < 0 || sentLen != packet_copy->getPacketSize() )
|
||||
{
|
||||
//LogError( L"LoginConnection::sendLSPacket(): sent %u instead of %u bytes! (retval %d)",
|
||||
// sentLen, packet_copy->getPacketSize(), r );
|
||||
delete packet_copy;
|
||||
throw Exception( "LoginConnection::sendLSPacket(): sent %u instead of %u bytes! (retval %d)",
|
||||
sentLen, packet_copy->getPacketSize(), r );
|
||||
}
|
||||
delete packet_copy;
|
||||
}
|
55
L2C_Server/LS_Connection.h
Normal file
55
L2C_Server/LS_Connection.h
Normal file
@ -0,0 +1,55 @@
|
||||
#pragma once
|
||||
#include "enums.h"
|
||||
|
||||
class LoginConnection
|
||||
{
|
||||
public:
|
||||
LoginConnection();
|
||||
~LoginConnection();
|
||||
|
||||
public:
|
||||
void start( const wchar_t *ls_addr, int ls_port, int ls_protover );
|
||||
void stop();
|
||||
|
||||
public:
|
||||
int getRegisteredServerId() const;
|
||||
const wchar_t *getRegisteredServerName() const;
|
||||
|
||||
protected:
|
||||
CriticalSection m_lock;
|
||||
wchar_t m_ls_addr[128];
|
||||
int m_ls_port;
|
||||
int m_ls_protover;
|
||||
int m_registeredServerId;
|
||||
wchar_t m_registeredServerName[256];
|
||||
protected:
|
||||
static DWORD WINAPI LS_ConnThread( LPVOID lpvParam );
|
||||
bool m_isRunning;
|
||||
bool m_isConnected;
|
||||
bool m_flagStop;
|
||||
SOCKET m_sock;
|
||||
unsigned char m_bfKey[64];
|
||||
unsigned int m_bfKeyLen;
|
||||
RSA *m_rsaKey;
|
||||
|
||||
protected:
|
||||
// senders
|
||||
void send_BlowfishKey(); // 0x00
|
||||
void send_GameServerAuthRequest(); // 0x01
|
||||
public: // these are public senders
|
||||
void send_PlayerInGame( const wchar_t *accountName ); // 0x02
|
||||
void send_PlayerLogOut( const wchar_t *accountName ); // 0x03
|
||||
void send_ChangeAccessLevel( const wchar_t *accountName, int new_access_level ); // 0x04
|
||||
void send_PlayerAuthRequest( const wchar_t *accountName, unsigned char *loginKey, unsigned char *playKey ); // 0x05
|
||||
void send_ServerStatus(); // 0x06
|
||||
|
||||
protected:
|
||||
// handlers
|
||||
void ph_InitLS( L2LoginPacket *pack ); // 0x00
|
||||
void ph_LoginServerFail( L2LoginPacket *pack ); // 0x01
|
||||
void ph_LoginServerOK( L2LoginPacket *pack ); // 0x02
|
||||
void ph_PlayerAuthResponse( L2LoginPacket *pack ); // 0x03
|
||||
void ph_KickPlayer( L2LoginPacket *pack ); // 0x04
|
||||
// internal
|
||||
void sendLSPacket( L2LoginPacket *pack );
|
||||
};
|
244
L2C_Server/Log.cpp
Normal file
244
L2C_Server/Log.cpp
Normal file
@ -0,0 +1,244 @@
|
||||
#include "pch.h"
|
||||
#include "Log.h"
|
||||
|
||||
HWND g_hWndLog = NULL;
|
||||
extern HINSTANCE g_hInst;
|
||||
|
||||
CRITICAL_SECTION g_cs_log;
|
||||
FILE *g_log_file = NULL;
|
||||
|
||||
bool g_log_is_sending = false;
|
||||
|
||||
HWND LogWnd_Create( HWND hWndParent )
|
||||
{
|
||||
g_log_is_sending = false;
|
||||
InitializeCriticalSectionAndSpinCount( &g_cs_log, 10 );
|
||||
RECT rc;
|
||||
GetClientRect( hWndParent, &rc );
|
||||
HWND hWndLog = CreateWindowEx( WS_EX_CLIENTEDGE, RICHEDIT_CLASS, TEXT(""),
|
||||
WS_CHILD | WS_VISIBLE | WS_VSCROLL | WS_HSCROLL |
|
||||
// richedit styles
|
||||
ES_LEFT | ES_NOHIDESEL | ES_READONLY | ES_MULTILINE,
|
||||
10, 10, rc.right-rc.left-20, 230, hWndParent, NULL, g_hInst, NULL );
|
||||
SendMessage( hWndLog, WM_SETTEXT, 0, (LPARAM)TEXT("") );
|
||||
SendMessage( hWndLog, EM_SETTEXTMODE, TM_RICHTEXT, 0 );
|
||||
SendMessage( hWndLog, EM_SETBKGNDCOLOR, FALSE, RGB(255,255,255) );
|
||||
SendMessage( hWndLog, EM_AUTOURLDETECT, TRUE, 0 );
|
||||
// setup default char format for control
|
||||
CHARFORMAT2 cf2;
|
||||
memset( &cf2, 0, sizeof(cf2) );
|
||||
cf2.cbSize = sizeof(cf2);
|
||||
cf2.dwMask = CFM_BACKCOLOR | CFM_COLOR | CFM_LINK | CFM_FACE | CFM_SIZE | CFM_WEIGHT;
|
||||
cf2.crBackColor = RGB(255,255,255);
|
||||
cf2.crTextColor = RGB(0,0,0);
|
||||
cf2.dwEffects = 0; //CFE_LINK | CFE_AUTOCOLOR | CFE_AUTOBACKCOLOR;
|
||||
_tcscpy( cf2.szFaceName, _T("Tahoma") );
|
||||
cf2.yHeight = 170;
|
||||
cf2.wWeight = FW_NORMAL;
|
||||
SendMessage( hWndLog, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf2 );
|
||||
SendMessage( hWndLog, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2 );
|
||||
g_hWndLog = hWndLog;
|
||||
return hWndLog;
|
||||
}
|
||||
|
||||
void Log( const wchar_t *_Format, ... )
|
||||
{
|
||||
va_list _ArgList;
|
||||
va_start( _ArgList, _Format );
|
||||
// calc string len
|
||||
int llen = _vscwprintf( _Format, _ArgList );
|
||||
if( llen < 1 ) return;
|
||||
// alloc string
|
||||
wchar_t *message = (wchar_t *)malloc( llen*2 + 2 );
|
||||
if( !message ) return;
|
||||
// sprintf
|
||||
va_start( _ArgList, _Format );
|
||||
_vsnwprintf( message, llen, _Format, _ArgList );
|
||||
message[llen] = 0;
|
||||
//
|
||||
LogFull( true, true, 0, RGB(255,255,255), message );
|
||||
// free string
|
||||
free( message );
|
||||
message = NULL;
|
||||
}
|
||||
|
||||
void Log_np( const wchar_t *_Format, ... )
|
||||
{
|
||||
va_list _ArgList;
|
||||
va_start( _ArgList, _Format );
|
||||
// calc string len
|
||||
int llen = _vscwprintf( _Format, _ArgList );
|
||||
if( llen < 1 ) return;
|
||||
// alloc string
|
||||
wchar_t *message = (wchar_t *)malloc( llen*2 + 2 );
|
||||
if( !message ) return;
|
||||
// sprintf
|
||||
va_start( _ArgList, _Format );
|
||||
_vsnwprintf( message, llen, _Format, _ArgList );
|
||||
message[llen] = 0;
|
||||
//
|
||||
LogFull( false, false, 0, RGB(255,255,255), message );
|
||||
// free string
|
||||
free( message );
|
||||
message = NULL;
|
||||
}
|
||||
|
||||
void LogError( const wchar_t *_Format, ... )
|
||||
{
|
||||
va_list _ArgList;
|
||||
va_start( _ArgList, _Format );
|
||||
// calc string len
|
||||
int llen = _vscwprintf( _Format, _ArgList );
|
||||
if( llen < 1 ) return;
|
||||
// alloc string
|
||||
wchar_t *message = (wchar_t *)malloc( llen*2 + 2 );
|
||||
if( !message ) return;
|
||||
// sprintf
|
||||
va_start( _ArgList, _Format );
|
||||
_vsnwprintf( message, llen, _Format, _ArgList );
|
||||
message[llen] = 0;
|
||||
//
|
||||
LogFull( true, true, RGB(255,0,0), RGB(255,255,255), message );
|
||||
// free string
|
||||
free( message );
|
||||
message = NULL;
|
||||
}
|
||||
|
||||
void LogWarning( const wchar_t *_Format, ... )
|
||||
{
|
||||
va_list _ArgList;
|
||||
va_start( _ArgList, _Format );
|
||||
// calc string len
|
||||
int llen = _vscwprintf( _Format, _ArgList );
|
||||
if( llen < 1 ) return;
|
||||
// alloc string
|
||||
wchar_t *message = (wchar_t *)malloc( llen*2 + 2 );
|
||||
if( !message ) return;
|
||||
// sprintf
|
||||
va_start( _ArgList, _Format );
|
||||
_vsnwprintf( message, llen, _Format, _ArgList );
|
||||
message[llen] = 0;
|
||||
//
|
||||
LogFull( true, true, RGB(196,64,0), RGB(255,255,255), message );
|
||||
// free string
|
||||
free( message );
|
||||
message = NULL;
|
||||
}
|
||||
|
||||
void LogDebug( const wchar_t *_Format, ... )
|
||||
{
|
||||
//#ifdef _DEBUG
|
||||
va_list _ArgList;
|
||||
va_start( _ArgList, _Format );
|
||||
// calc string len
|
||||
int llen = _vscwprintf( _Format, _ArgList );
|
||||
if( llen < 1 ) return;
|
||||
// alloc string
|
||||
wchar_t *message = (wchar_t *)malloc( llen*2 + 2 );
|
||||
if( !message ) return;
|
||||
// sprintf
|
||||
va_start( _ArgList, _Format );
|
||||
_vsnwprintf( message, llen, _Format, _ArgList );
|
||||
message[llen] = 0;
|
||||
//
|
||||
LogFull( true, true, RGB(128,128,128), RGB(255,255,255), message );
|
||||
// free string
|
||||
free( message );
|
||||
message = NULL;
|
||||
//#else
|
||||
// _Format = NULL; // unreferenced parameter
|
||||
//#endif
|
||||
}
|
||||
|
||||
void LogFull( bool prependTime, bool appendNL, COLORREF crTextColor, COLORREF crBackColor, const wchar_t *message )
|
||||
{
|
||||
//EnterCriticalSection( &g_cs_log ); // TODO: LogFull EnterCriticalSection
|
||||
if( g_log_is_sending ) return; // already running here in other thread; cancel SendMessage
|
||||
g_log_is_sending = true;
|
||||
//
|
||||
static wchar_t nl[2] = { L'\r', L'\0' }; // newline line-break
|
||||
// move selection after last char
|
||||
CHARRANGE cr;
|
||||
cr.cpMin = -1;
|
||||
cr.cpMax = -1;
|
||||
SendMessage( g_hWndLog, EM_EXSETSEL, 0, (LPARAM)&cr );
|
||||
// prepend time?
|
||||
if( prependTime )
|
||||
{
|
||||
wchar_t tstr[128];
|
||||
SYSTEMTIME st;
|
||||
GetLocalTime( &st );
|
||||
_snwprintf( tstr, 127, L"%02d:%02d:%02d.%03d ", st.wHour, st.wMinute, st.wSecond, st.wMilliseconds );
|
||||
// log to file?
|
||||
if( g_log_file )
|
||||
fwprintf( g_log_file, L"%02d:%02d:%02d.%03d: ", st.wHour, st.wMinute, st.wSecond, st.wMilliseconds );
|
||||
// out to window
|
||||
CHARFORMAT2 tcf2;
|
||||
memset( &tcf2, 0, sizeof(tcf2) );
|
||||
tcf2.cbSize = sizeof(tcf2);
|
||||
tcf2.dwMask = CFM_BACKCOLOR | CFM_COLOR | CFM_ITALIC | CFM_BOLD;
|
||||
tcf2.crBackColor = RGB(255,255,255);
|
||||
tcf2.crTextColor = RGB(100,100,100);
|
||||
tcf2.dwEffects = 0;
|
||||
SendMessage( g_hWndLog, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&tcf2 );
|
||||
SendMessage( g_hWndLog, EM_REPLACESEL, FALSE, (LPARAM)tstr );
|
||||
}
|
||||
// log to file?
|
||||
if( g_log_file ) fwprintf( g_log_file, L"%s", message );
|
||||
// out to window
|
||||
// set color
|
||||
CHARFORMAT2 cf2;
|
||||
memset( &cf2, 0, sizeof(cf2) );
|
||||
cf2.cbSize = sizeof(cf2);
|
||||
cf2.dwMask = CFM_BACKCOLOR | CFM_COLOR | CFM_ITALIC | CFM_BOLD;
|
||||
cf2.crBackColor = crBackColor;
|
||||
cf2.crTextColor = crTextColor;
|
||||
cf2.dwEffects = CFE_BOLD;
|
||||
SendMessage( g_hWndLog, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2 );
|
||||
// add string
|
||||
SendMessage( g_hWndLog, EM_REPLACESEL, FALSE, (LPARAM)message );
|
||||
// add line-break
|
||||
if( appendNL )
|
||||
{
|
||||
SendMessage( g_hWndLog, EM_REPLACESEL, FALSE, (LPARAM)nl );
|
||||
if( g_log_file ) fwprintf( g_log_file, L"\n" );
|
||||
}
|
||||
//
|
||||
//LeaveCriticalSection( &g_cs_log );
|
||||
g_log_is_sending = false; // mark as not sending messages
|
||||
}
|
||||
|
||||
void Log_EnableLogToFile( bool bEnable, FILE *f /*= NULL*/ )
|
||||
{
|
||||
if( bEnable )
|
||||
{
|
||||
if( g_log_file ) fclose( g_log_file );
|
||||
g_log_file = f;
|
||||
}
|
||||
else
|
||||
{
|
||||
if( g_log_file ) fclose( g_log_file );
|
||||
g_log_file = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
void Log_FlushFile()
|
||||
{
|
||||
if( g_log_file ) fflush( g_log_file );
|
||||
}
|
||||
|
||||
void Log_Win32Error( const wchar_t *comment, DWORD error_code )
|
||||
{
|
||||
wchar_t wbuffer[512] = {0};
|
||||
FormatMessageW( FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
|
||||
NULL, error_code, MAKELANGID( LANG_NEUTRAL, SUBLANG_DEFAULT ), wbuffer, 511, NULL );
|
||||
wbuffer[511] = 0;
|
||||
int ll = (int)wcslen( wbuffer );
|
||||
if( ll >= 2 )
|
||||
{
|
||||
if( wbuffer[ll-1] == L'\r' || wbuffer[ll-1] == L'\n' ) wbuffer[ll-1] = 0;
|
||||
if( wbuffer[ll-2] == L'\r' || wbuffer[ll-2] == L'\n' ) wbuffer[ll-2] = 0;
|
||||
}
|
||||
LogError( L"Win32 error: %s", wbuffer );
|
||||
if( comment ) LogError( L"Error comment: %s", comment );
|
||||
}
|
19
L2C_Server/Log.h
Normal file
19
L2C_Server/Log.h
Normal file
@ -0,0 +1,19 @@
|
||||
#pragma once
|
||||
|
||||
extern HWND g_hWndLog;
|
||||
|
||||
HWND LogWnd_Create( HWND hWndParent );
|
||||
|
||||
void Log( const wchar_t *_Format, ... );
|
||||
void Log_np( const wchar_t *_Format, ... );
|
||||
void LogError( const wchar_t *_Format, ... );
|
||||
void LogWarning( const wchar_t *_Format, ... );
|
||||
void LogDebug( const wchar_t *_Format, ... );
|
||||
|
||||
void LogFull( bool prependTime, bool appendNL, COLORREF crTextColor, COLORREF crBackColor, const wchar_t *message );
|
||||
|
||||
void Log_EnableLogToFile( bool bEnable, FILE *f = NULL );
|
||||
void Log_FlushFile();
|
||||
|
||||
void Log_Win32Error( const wchar_t *comment, DWORD error_code );
|
||||
|
23
L2C_Server/PostBuildEvent.cmd
Normal file
23
L2C_Server/PostBuildEvent.cmd
Normal file
@ -0,0 +1,23 @@
|
||||
rem %1 = L2Serverd_d
|
||||
rem %2 = output exe dir: m:\dev\l2\servers\l2c\_build\L2Serverd_Debug\
|
||||
rem
|
||||
rem ..\..\dist
|
||||
|
||||
rem echo 1 = %1
|
||||
rem echo 2 = %2
|
||||
|
||||
set out=..\..\out\dist_l2c
|
||||
set bin=..\..\bin
|
||||
|
||||
rem Copy EXE
|
||||
copy /y %2\%1.exe %2\%out%
|
||||
|
||||
rem Copy PDB
|
||||
copy /y %2\%1.pdb %2\%out%
|
||||
|
||||
rem Copy DLLs
|
||||
copy /y %2\%bin%\dbghelp.dll %2\%out%
|
||||
copy /y %2\%bin%\libeay32.dll %2\%out%
|
||||
copy /y %2\%bin%\ssleay32.dll %2\%out%
|
||||
copy /y %2\%bin%\libmysql.dll %2\%out%
|
||||
rem copy /y %2\%bin%\*.dll %2\%out%
|
15
L2C_Server/Resource.h
Normal file
15
L2C_Server/Resource.h
Normal file
@ -0,0 +1,15 @@
|
||||
|
||||
// icons
|
||||
#define IDI_MAIN 100
|
||||
|
||||
// dialogs
|
||||
#define IDD_MAIN 100
|
||||
|
||||
// buttons
|
||||
#define IDC_B_SERVER 1000
|
||||
#define IDC_B_DEBUG 10001
|
||||
|
||||
// static
|
||||
#define IDC_STATIC_CONTROLS 2000
|
||||
|
||||
|
94
L2C_Server/datatables/CharNameTable.cpp
Normal file
94
L2C_Server/datatables/CharNameTable.cpp
Normal file
@ -0,0 +1,94 @@
|
||||
#include "pch.h"
|
||||
#include "CharNameTable.h"
|
||||
#include "GS.h"
|
||||
#include "Log.h"
|
||||
|
||||
CharNameTable *CharNameTable::s_instance = NULL;
|
||||
int CharNameTable::s_refCount = 0;
|
||||
|
||||
CharNameTable *CharNameTable::getInstance()
|
||||
{
|
||||
if( !s_instance )
|
||||
{
|
||||
s_instance = new CharNameTable();
|
||||
s_refCount++;
|
||||
}
|
||||
return s_instance;
|
||||
}
|
||||
|
||||
void CharNameTable::freeInstance()
|
||||
{
|
||||
if( s_instance == NULL ) return;
|
||||
s_refCount--;
|
||||
if( s_refCount == 0 )
|
||||
{
|
||||
delete s_instance;
|
||||
s_instance = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
CharNameTable::CharNameTable()
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
CharNameTable::~CharNameTable()
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
void CharNameTable::LockDB()
|
||||
{
|
||||
m_lock.Lock();
|
||||
}
|
||||
|
||||
void CharNameTable::UnlockDB()
|
||||
{
|
||||
m_lock.Unlock();
|
||||
}
|
||||
|
||||
bool CharNameTable::doesCharNameExist( const wchar_t *charName )
|
||||
{
|
||||
bool ret = true;
|
||||
m_lock.Lock();
|
||||
{
|
||||
GameServer *gs = GameServer::getInstance();
|
||||
MysqlConnection *con = gs->getDBConnection();
|
||||
MysqlQuery q;
|
||||
q.create( L"SELECT account_name FROM characters WHERE char_name='%s'", charName );
|
||||
if( con->executeQuery( q ) )
|
||||
{
|
||||
int num_chars = q.getNumRows();
|
||||
assert( num_chars >= 0 );
|
||||
if( num_chars == 0 ) ret = false;
|
||||
}
|
||||
else
|
||||
LogError( L"CharNameTable::doesCharNameExist(): MySQL ERROR: %s\n", con->getErrorStr() );
|
||||
gs->releaseDBConnection( con );
|
||||
}
|
||||
m_lock.Unlock();
|
||||
return ret;
|
||||
}
|
||||
|
||||
int CharNameTable::getAccountCharCount( const wchar_t *accountName )
|
||||
{
|
||||
if( !accountName ) return 0;
|
||||
// SELECT COUNT(char_name) FROM characters WHERE account_name=?
|
||||
int num_chars = 0;
|
||||
m_lock.Lock();
|
||||
{
|
||||
GameServer *gs = GameServer::getInstance();
|
||||
MysqlConnection *con = gs->getDBConnection();
|
||||
MysqlQuery q;
|
||||
q.create( L"SELECT COUNT(char_name) FROM characters WHERE account_name='%s'", accountName );
|
||||
if( con->executeQuery( q ) )
|
||||
{
|
||||
if( q.getNextRow() ) num_chars = q.getFieldInt( 0 );
|
||||
}
|
||||
else
|
||||
LogError( L"CharNameTable::getAccountCharCount(): MySQL ERROR: %s\n", con->getErrorStr() );
|
||||
gs->releaseDBConnection( con );
|
||||
}
|
||||
m_lock.Unlock();
|
||||
return num_chars;
|
||||
}
|
24
L2C_Server/datatables/CharNameTable.h
Normal file
24
L2C_Server/datatables/CharNameTable.h
Normal file
@ -0,0 +1,24 @@
|
||||
#pragma once
|
||||
#include "l2c_utils.h"
|
||||
|
||||
class CharNameTable
|
||||
{
|
||||
protected:
|
||||
CharNameTable();
|
||||
~CharNameTable();
|
||||
static CharNameTable *s_instance;
|
||||
static int s_refCount;
|
||||
public:
|
||||
static CharNameTable *getInstance();
|
||||
static void freeInstance();
|
||||
|
||||
public:
|
||||
bool doesCharNameExist( const wchar_t *charName );
|
||||
int getAccountCharCount( const wchar_t *accountName );
|
||||
|
||||
void LockDB();
|
||||
void UnlockDB();
|
||||
|
||||
protected:
|
||||
CriticalSection m_lock;
|
||||
};
|
198
L2C_Server/datatables/CharTemplateTable.cpp
Normal file
198
L2C_Server/datatables/CharTemplateTable.cpp
Normal file
@ -0,0 +1,198 @@
|
||||
#include "pch.h"
|
||||
#include "Log.h"
|
||||
#include "CharTemplateTable.h"
|
||||
#include "world/model/base/ClassIdTree.h"
|
||||
#include "ItemTable.h"
|
||||
#include "GS.h"
|
||||
|
||||
CharTemplateTable *CharTemplateTable::s_instance = NULL;
|
||||
int CharTemplateTable::s_refCount = 0;
|
||||
|
||||
CharTemplateTable *CharTemplateTable::getInstance()
|
||||
{
|
||||
if( s_instance == NULL )
|
||||
{
|
||||
s_instance = new CharTemplateTable();
|
||||
s_refCount++;
|
||||
}
|
||||
return s_instance;
|
||||
}
|
||||
|
||||
void CharTemplateTable::freeInstance()
|
||||
{
|
||||
if( s_instance )
|
||||
{
|
||||
s_refCount--;
|
||||
if( s_refCount == 0 )
|
||||
{
|
||||
delete s_instance;
|
||||
s_instance = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
CharTemplateTable::CharTemplateTable()
|
||||
{
|
||||
m_templates.clear();
|
||||
}
|
||||
|
||||
CharTemplateTable::~CharTemplateTable()
|
||||
{
|
||||
std::map<int, L2PlayerTemplate *>::const_iterator iter = m_templates.begin();
|
||||
while( iter != m_templates.end() )
|
||||
{
|
||||
L2PlayerTemplate *tmpl = iter->second;
|
||||
if( tmpl ) delete tmpl;
|
||||
iter++;
|
||||
}
|
||||
m_templates.clear();
|
||||
}
|
||||
|
||||
void CharTemplateTable::load()
|
||||
{
|
||||
GameServer *gs = GameServer::getInstance();
|
||||
MysqlConnection *con = gs->getDBConnection();
|
||||
//
|
||||
MysqlQuery q;
|
||||
q.create( L"SELECT * FROM class_list, char_templates, lvlupgain "
|
||||
L"WHERE class_list.id = char_templates.classId AND class_list.id = lvlupgain.classId "
|
||||
L"ORDER BY class_list.id" );
|
||||
if( con->executeQuery( q ) )
|
||||
{
|
||||
while( q.getNextRow() )
|
||||
{
|
||||
StatsSet set;
|
||||
int classId;
|
||||
// l2 player template vars
|
||||
classId = q.getFieldInt( "id" );
|
||||
set.setInt( "classId", classId );
|
||||
set.setInt( "raceId", q.getFieldInt( "raceId" ) );
|
||||
set.setInt( "spawnX", q.getFieldInt( "x" ) );
|
||||
set.setInt( "spawnY", q.getFieldInt( "y" ) );
|
||||
set.setInt( "spawnZ", q.getFieldInt( "z" ) );
|
||||
set.setInt( "classBaseLevel", q.getFieldInt( "class_lvl" ) );
|
||||
set.setDouble( "lvlHpAdd", q.getFieldDouble( "defaultHpAdd" ) );
|
||||
set.setDouble( "lvlHpMod", q.getFieldDouble( "defaultHpMod" ) );
|
||||
set.setDouble( "lvlCpAdd", q.getFieldDouble( "defaultCpAdd" ) );
|
||||
set.setDouble( "lvlCpMod", q.getFieldDouble( "defaultCpMod" ) );
|
||||
set.setDouble( "lvlMpAdd", q.getFieldDouble( "defaultMpAdd" ) );
|
||||
set.setDouble( "lvlMpMod", q.getFieldDouble( "defaultMpMod" ) );
|
||||
set.setDouble( "baseHpMax", q.getFieldDouble( "defaultHpBase" ) );
|
||||
set.setDouble( "baseMpMax", q.getFieldDouble( "defaultMpBase" ) );
|
||||
set.setDouble( "baseCpMax", q.getFieldDouble( "defaultCpBase" ) );
|
||||
set.setDouble( "baseHpReg", 1.5 ); // O_o constants...?
|
||||
set.setDouble( "baseMpReg", 0.9 );
|
||||
// l2 char template vars
|
||||
set.setInt( "baseSTR", q.getFieldInt( "STR" ) );
|
||||
set.setInt( "baseCON", q.getFieldInt( "CON" ) );
|
||||
set.setInt( "baseDEX", q.getFieldInt( "DEX" ) );
|
||||
set.setInt( "baseINT", q.getFieldInt( "_INT" ) );
|
||||
set.setInt( "baseWIT", q.getFieldInt( "WIT" ) );
|
||||
set.setInt( "baseMEN", q.getFieldInt( "MEN" ) );
|
||||
//
|
||||
set.setInt( "basePAtk", q.getFieldInt( "P_ATK" ) );
|
||||
set.setInt( "basePDef", q.getFieldInt( "P_DEF" ) );
|
||||
set.setInt( "baseMAtk", q.getFieldInt( "M_ATK" ) );
|
||||
set.setInt( "baseMDef", q.getFieldInt( "M_DEF" ) );
|
||||
set.setInt( "basePAtkSpd", q.getFieldInt( "P_SPD" ) );
|
||||
set.setInt( "baseMAtkSpd", q.getFieldInt( "M_SPD" ) );
|
||||
set.setInt( "baseCritRate", q.getFieldInt( "CRITICAL" ) );
|
||||
set.setInt( "baseRunSpd", q.getFieldInt( "MOVE_SPD" ) );
|
||||
set.setInt( "baseWalkSpd", 0 );
|
||||
set.setInt( "baseShldDef", 0 );
|
||||
set.setInt( "baseShldRate", 0 );
|
||||
set.setInt( "baseAtkRange", 40 );
|
||||
set.setInt( "collision_radius", (int)q.getFieldDouble( "M_COL_R" ) );
|
||||
set.setInt( "collision_height", (int)q.getFieldDouble( "M_COL_H" ) );
|
||||
// TODO: MISSED char_templates vars
|
||||
set.setInt( "baseAccuracy", q.getFieldInt( "ACC" ) );
|
||||
set.setInt( "baseEvasion", q.getFieldInt( "EVASION" ) );
|
||||
set.setInt( "baseLoad", q.getFieldInt( "_LOAD" ) );
|
||||
set.setBool( "canCraft", q.getFieldBool( "canCraft" ) );
|
||||
//
|
||||
L2PlayerTemplate *tmpl = new L2PlayerTemplate( set );
|
||||
m_templates[classId] = tmpl;
|
||||
}
|
||||
Log( L"CharTemplateTable: loaded %u character templates.", m_templates.size() );
|
||||
}
|
||||
else
|
||||
{
|
||||
LogError( L"CharTemplateTable::load(): Mysql error: %s", con->getErrorStr() );
|
||||
}
|
||||
|
||||
// load starting items
|
||||
ItemTable *itemTable = ItemTable::getInstance();
|
||||
q.create( L"SELECT classId, itemId, amount, equipped FROM char_creation_items" );
|
||||
if( con->executeQuery( q ) )
|
||||
{
|
||||
int classId, itemId, amount;
|
||||
bool equipped;
|
||||
while( q.getNextRow() )
|
||||
{
|
||||
classId = q.getFieldInt( "classId" );
|
||||
itemId = q.getFieldInt( "itemId" );
|
||||
amount = q.getFieldInt( "amount" );
|
||||
equipped = false;
|
||||
if( strcmp( q.getFieldStr( "equipped" ), "true" ) == 0 ) equipped = true;
|
||||
// check for item existance
|
||||
if( itemTable->getTemplate( itemId ) != NULL )
|
||||
{
|
||||
if( classId == -1 )
|
||||
{
|
||||
this->addItemToAllTemplates( itemId, amount, equipped );
|
||||
}
|
||||
else
|
||||
{
|
||||
L2PlayerTemplate *pct = const_cast<L2PlayerTemplate *>(CharTemplateTable::getTemplate( classId ));
|
||||
if( pct != NULL )
|
||||
pct->addItem( itemId, amount, equipped );
|
||||
else
|
||||
LogWarning( L"CharTemplateTable: load: Entry for undefined class, classId: %d", classId );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
const wchar_t * className = L"(all)";
|
||||
if( classId >= 0 ) className = ClassIdTree::getInstance()->getClassId( classId )->getName();
|
||||
LogWarning( L"CharTemplateTable: load: No data for itemId: %d defined for classId %d (%s)",
|
||||
itemId, classId, className );
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
LogError( L"CharTemplateTable::load(): Mysql error: %s", con->getErrorStr() );
|
||||
}
|
||||
|
||||
//
|
||||
gs->releaseDBConnection( con );
|
||||
}
|
||||
|
||||
const wchar_t *CharTemplateTable::getClassNameById( int classId )
|
||||
{
|
||||
ClassIdTree *cidtree = ClassIdTree::getInstance();
|
||||
const ClassId *cls = cidtree->getClassId( classId );
|
||||
if( !cls ) return NULL;
|
||||
return cls->getName();
|
||||
}
|
||||
|
||||
const L2PlayerTemplate *CharTemplateTable::getTemplate( int classId )
|
||||
{
|
||||
CharTemplateTable *ctt = CharTemplateTable::getInstance();
|
||||
std::map<int, L2PlayerTemplate *>::const_iterator iter;
|
||||
iter = ctt->m_templates.find( classId );
|
||||
if( iter == ctt->m_templates.end() ) return NULL;
|
||||
return iter->second;
|
||||
}
|
||||
|
||||
void CharTemplateTable::addItemToAllTemplates( int itemId, int amount, bool equipped )
|
||||
{
|
||||
std::map<int, L2PlayerTemplate *>::const_iterator iter;
|
||||
iter = m_templates.begin();
|
||||
while( iter != m_templates.end() )
|
||||
{
|
||||
L2PlayerTemplate *tmpl = iter->second;
|
||||
if( tmpl ) tmpl->addItem( itemId, amount, equipped );
|
||||
iter++;
|
||||
}
|
||||
}
|
25
L2C_Server/datatables/CharTemplateTable.h
Normal file
25
L2C_Server/datatables/CharTemplateTable.h
Normal file
@ -0,0 +1,25 @@
|
||||
#pragma once
|
||||
#include "world/templates/chars/L2PlayerTemplate.h"
|
||||
|
||||
class CharTemplateTable
|
||||
{
|
||||
protected:
|
||||
static CharTemplateTable *s_instance;
|
||||
static int s_refCount;
|
||||
CharTemplateTable();
|
||||
~CharTemplateTable();
|
||||
public:
|
||||
static CharTemplateTable *getInstance();
|
||||
static void freeInstance();
|
||||
|
||||
public:
|
||||
void load();
|
||||
static const wchar_t *getClassNameById( int classId );
|
||||
static const L2PlayerTemplate *getTemplate( int classId );
|
||||
|
||||
protected:
|
||||
void addItemToAllTemplates( int itemId, int amount, bool equipped );
|
||||
|
||||
protected:
|
||||
std::map<int, L2PlayerTemplate *> m_templates;
|
||||
};
|
525
L2C_Server/datatables/ItemTable.cpp
Normal file
525
L2C_Server/datatables/ItemTable.cpp
Normal file
@ -0,0 +1,525 @@
|
||||
#include "pch.h"
|
||||
#include "ItemTable.h"
|
||||
#include "Log.h"
|
||||
#include "../GS.h"
|
||||
#include "utils/Exception.h"
|
||||
|
||||
ItemTable *ItemTable::s_instance = NULL;
|
||||
int ItemTable::s_refCount = 0;
|
||||
|
||||
ItemTable *ItemTable::getInstance()
|
||||
{
|
||||
if( s_instance == NULL )
|
||||
{
|
||||
s_instance = new ItemTable();
|
||||
s_refCount++;
|
||||
}
|
||||
return s_instance;
|
||||
}
|
||||
|
||||
void ItemTable::freeInstance()
|
||||
{
|
||||
if( !s_instance ) return;
|
||||
s_refCount--;
|
||||
if( s_refCount > 0 ) return;
|
||||
delete s_instance;
|
||||
s_instance = NULL;
|
||||
}
|
||||
|
||||
ItemTable::ItemTable()
|
||||
{
|
||||
m_allTemplates = NULL;
|
||||
//m_armorTemplates = NULL;
|
||||
//m_weaponTemplates = NULL;
|
||||
//m_etcItemTemplates = NULL;
|
||||
m_cnt_all = 0;
|
||||
//m_cnt_armors = m_cnt_weapons = m_cnt_etcItems = 0;
|
||||
m_nTotalBytesAllocated = 0;
|
||||
m_maxItemId = 0;
|
||||
}
|
||||
|
||||
ItemTable::~ItemTable()
|
||||
{
|
||||
m_lock.Lock();
|
||||
unsigned int i;
|
||||
if( m_allTemplates )
|
||||
{
|
||||
if( m_maxItemId > 0 )
|
||||
{
|
||||
for( i=0; i<m_maxItemId; i++ )
|
||||
{
|
||||
if( m_allTemplates[i] ) delete m_allTemplates[i];
|
||||
m_allTemplates[i] = NULL;
|
||||
}
|
||||
}
|
||||
free( m_allTemplates );
|
||||
m_allTemplates = NULL;
|
||||
}
|
||||
m_lock.Unlock();
|
||||
m_nTotalBytesAllocated = 0;
|
||||
m_maxItemId = 0;
|
||||
m_cnt_all = 0;
|
||||
}
|
||||
|
||||
unsigned int ItemTable::select_count( void *vcon, const wchar_t *table )
|
||||
{
|
||||
unsigned int ret = 0;
|
||||
MysqlConnection *con = (MysqlConnection *)vcon;
|
||||
MysqlQuery q;
|
||||
q.create( L"SELECT COUNT(*) FROM `%s`", table );
|
||||
if( con->executeQuery( q ) )
|
||||
{
|
||||
if( q.getNextRow() ) ret = q.getFieldUInt( 0 );
|
||||
else LogError( L"ItemTable::select_count( %s ): q.getNextRow() failed!", table );
|
||||
}
|
||||
else LogError( L"ItemTable::select_count( %s ): Mysql error: %s", table, con->getErrorStr() );
|
||||
return ret;
|
||||
}
|
||||
|
||||
unsigned int ItemTable::select_max( void *vcon, const wchar_t *table )
|
||||
{
|
||||
unsigned int ret = 0;
|
||||
MysqlConnection *con = (MysqlConnection *)vcon;
|
||||
MysqlQuery q;
|
||||
q.create( L"SELECT MAX(item_id) FROM `%s`", table );
|
||||
if( con->executeQuery( q ) )
|
||||
{
|
||||
if( q.getNextRow() ) ret = q.getFieldUInt( 0 );
|
||||
else LogError( L"ItemTable::select_max( %s ): q.getNextRow() failed!", table );
|
||||
}
|
||||
else LogError( L"ItemTable::select_max( %s ): Mysql error: %s", table, con->getErrorStr() );
|
||||
return ret;
|
||||
}
|
||||
|
||||
void ItemTable::load()
|
||||
{
|
||||
m_lock.Lock();
|
||||
try
|
||||
{
|
||||
// get GS DB conn
|
||||
GameServer *gs = GameServer::getInstance();
|
||||
MysqlConnection *con = gs->getDBConnection();
|
||||
// count items, armors, weapons in DB
|
||||
unsigned int etc_count = select_count( con, L"etcitem" );
|
||||
unsigned int arm_count = select_count( con, L"armor" );
|
||||
unsigned int wea_count = select_count( con, L"weapon" );
|
||||
Log( L"ItemTable: loading %u armors, %u weapons, %u etcitems", arm_count, wea_count, etc_count );
|
||||
unsigned int max_itemId = 0;
|
||||
unsigned int max_armor = select_max( con, L"armor" );
|
||||
unsigned int max_etcItem = select_max( con, L"etcitem" );
|
||||
unsigned int max_weapon = select_max( con, L"weapon" );
|
||||
max_itemId = max_armor;
|
||||
if( max_weapon > max_itemId ) max_itemId = max_weapon;
|
||||
if( max_etcItem > max_itemId ) max_itemId = max_etcItem;
|
||||
LogDebug( L"ItemTable: max itemId = %u (armor %u, etc %u, weapon %u)", max_itemId, max_armor, max_etcItem, max_weapon );
|
||||
// allocate m_allTemplates
|
||||
m_allTemplates = (L2ItemTemplate **)malloc( sizeof(void *) * (max_itemId+1) );
|
||||
if( !m_allTemplates )
|
||||
{
|
||||
m_lock.Unlock();
|
||||
LogError( L"ItemTable::load(): cannot allocate %u bytes\n", (unsigned int)(sizeof(void *) * (max_itemId+1)) );
|
||||
return;
|
||||
}
|
||||
memset( m_allTemplates, 0, sizeof(void *) * (max_itemId+1) );
|
||||
m_cnt_all = etc_count + arm_count + wea_count;
|
||||
m_maxItemId = max_itemId;
|
||||
m_nTotalBytesAllocated = sizeof(void *) * (max_itemId+1);
|
||||
//
|
||||
loadEtcItems( con );
|
||||
loadArmors( con );
|
||||
loadWeapons( con );
|
||||
//
|
||||
// release GS DB conn
|
||||
gs->releaseDBConnection( con );
|
||||
}
|
||||
catch( Exception& e )
|
||||
{
|
||||
LogError( L"ItemTable load error: %S", e.what() );
|
||||
e.logStackTrace();
|
||||
}
|
||||
m_lock.Unlock();
|
||||
Log( L"ItemTable: loaded, used %I64u bytes", m_nTotalBytesAllocated );
|
||||
}
|
||||
|
||||
void ItemTable::reload()
|
||||
{
|
||||
// TODO: maybe better way?
|
||||
Log( L"ItemTable: reload starting..." );
|
||||
m_lock.Lock();
|
||||
this->~ItemTable();
|
||||
this->load();
|
||||
m_lock.Unlock();
|
||||
Log( L"ItemTable: reload complete." );
|
||||
}
|
||||
|
||||
void ItemTable::loadEtcItems( void *vcon )
|
||||
{
|
||||
MysqlConnection *con = (MysqlConnection *)vcon;
|
||||
MysqlQuery q;
|
||||
q.create( L"SELECT item_id, name, crystallizable, item_type, weight, consume_type, material, "
|
||||
L" crystal_type, duration, time, price, crystal_count, sellable, dropable, destroyable, "
|
||||
L" tradeable, handler, skill FROM etcitem" );
|
||||
if( con->executeQuery( q ) )
|
||||
{
|
||||
while( q.getNextRow() )
|
||||
{
|
||||
StatsSet set;
|
||||
L2ItemSubType etcItemType = SYBTYPE_INCORRECT;
|
||||
int item_id = 0;
|
||||
char *str = NULL;
|
||||
L2ItemSlot bodypart = SLOT_NONE;
|
||||
int type2 = L2ItemTemplate::TYPE2_OTHER;
|
||||
wchar_t wname[256] = {0};
|
||||
|
||||
// get item ID
|
||||
item_id = q.getFieldInt( "item_id" );
|
||||
set.setInt( "item_id", item_id );
|
||||
|
||||
str = q.getFieldStr( "item_type" );
|
||||
if( _stricmp( str, "none" ) == 0 )
|
||||
etcItemType = ETCITEM_OTHER; // only for default
|
||||
else if ( _stricmp( str, "castle_guard") == 0 )
|
||||
etcItemType = ETCITEM_SCROLL; // dummy
|
||||
else if( _stricmp( str, "material") == 0 )
|
||||
etcItemType = ETCITEM_MATERIAL;
|
||||
else if( _stricmp( str, "pet_collar") == 0 )
|
||||
etcItemType = ETCITEM_PET_COLLAR;
|
||||
else if( _stricmp( str, "potion") == 0 )
|
||||
etcItemType = ETCITEM_POTION;
|
||||
else if( _stricmp( str, "recipe") == 0 )
|
||||
etcItemType = ETCITEM_RECEIPE;
|
||||
else if( _stricmp( str, "scroll") == 0 )
|
||||
etcItemType = ETCITEM_SCROLL;
|
||||
else if( _stricmp( str, "seed") == 0 )
|
||||
etcItemType = ETCITEM_SEED;
|
||||
else if( _stricmp( str, "shot") == 0 )
|
||||
etcItemType = ETCITEM_SHOT;
|
||||
else if( _stricmp( str, "spellbook") == 0 )
|
||||
etcItemType = ETCITEM_SPELLBOOK; // Spellbook, Amulet, Blueprint
|
||||
else if( _stricmp( str, "herb") == 0 )
|
||||
etcItemType = ETCITEM_HERB;
|
||||
else if( _stricmp( str, "arrow") == 0 )
|
||||
{
|
||||
etcItemType = ETCITEM_ARROW;
|
||||
bodypart = SLOT_L_HAND;
|
||||
}
|
||||
else if( _stricmp( str, "bolt") == 0 )
|
||||
{
|
||||
etcItemType = ETCITEM_BOLT;
|
||||
bodypart = SLOT_L_HAND;
|
||||
}
|
||||
else if( _stricmp( str, "quest") == 0 )
|
||||
{
|
||||
etcItemType = ETCITEM_QUEST;
|
||||
type2 = L2ItemTemplate::TYPE2_QUEST;
|
||||
}
|
||||
else if( _stricmp( str, "lure") == 0 )
|
||||
{
|
||||
etcItemType = ETCITEM_OTHER;
|
||||
bodypart = SLOT_L_HAND;
|
||||
}
|
||||
else if( _stricmp( str, "dye") == 0 )
|
||||
etcItemType = ETCITEM_DYE;
|
||||
else if( _stricmp( str, "lotto") == 0 )
|
||||
etcItemType = ETCITEM_OTHER;
|
||||
else if( _stricmp( str, "race_ticket") == 0 )
|
||||
etcItemType = ETCITEM_OTHER;
|
||||
else if( _stricmp( str, "harvest") == 0 )
|
||||
etcItemType = ETCITEM_OTHER;
|
||||
else if( _stricmp( str, "ticket_of_lord") == 0 )
|
||||
etcItemType = ETCITEM_OTHER;
|
||||
else
|
||||
{
|
||||
q.getFieldStrW( "name", wname, 255 ); wname[255] = 0;
|
||||
LogWarning( L"ItemTable::loadEtcItems(): unknown etcitem type: %S (item_id %d [%s])",
|
||||
str, item_id, wname );
|
||||
etcItemType = ETCITEM_OTHER;
|
||||
}
|
||||
|
||||
set.setInt( "type1", L2ItemTemplate::TYPE1_ITEM_QUESTITEM_ADENA );
|
||||
set.setInt( "type2", type2 );
|
||||
set.setInt( "bodypart", (int)bodypart );
|
||||
set.setBool( "crystallizable", q.getFieldBool( "crystallizable" ) );
|
||||
set.setInt( "crystal_count", q.getFieldInt( "crystal_count" ) );
|
||||
set.setBool( "sellable", q.getFieldBool( "sellable" ) );
|
||||
set.setBool( "dropable", q.getFieldBool( "dropable" ) );
|
||||
set.setBool( "destroyable", q.getFieldBool( "destroyable" ) );
|
||||
set.setBool( "tradeable", q.getFieldBool( "tradeable" ) );
|
||||
set.setString( "handler", q.getFieldStr( "handler" ) );
|
||||
// item.set.set("skill", rset.getString("skill")); // TODO: etc item skills
|
||||
|
||||
str = q.getFieldStr( "consume_type" );
|
||||
if( _stricmp( str, "asset" ) == 0 )
|
||||
{
|
||||
etcItemType = ETCITEM_MONEY;
|
||||
set.setBool( "stackable", true );
|
||||
set.setInt( "type2", L2ItemTemplate::TYPE2_MONEY );
|
||||
}
|
||||
else if ( _stricmp( str, "stackable" ) == 0 )
|
||||
{
|
||||
set.setBool( "stackable", true );
|
||||
}
|
||||
else
|
||||
{
|
||||
set.setBool( "stackable", false );
|
||||
}
|
||||
|
||||
L2ItemMaterial material = L2ItemTemplate::getMaterialByName( q.getFieldStr( "material" ) );
|
||||
set.setInt( "material", (int)material );
|
||||
L2ItemCrystal crystal = L2ItemTemplate::getCrystalTypeByName( q.getFieldStr( "crystal_type" ) );
|
||||
set.setInt( "crystal_type", (int)crystal );
|
||||
set.setInt( "weight", q.getFieldInt( "weight" ) );
|
||||
// name
|
||||
q.getFieldStrW( "name", wname, 255 ); wname[255] = 0;
|
||||
set.setWString( "name", wname );
|
||||
//
|
||||
set.setInt( "duration", q.getFieldInt( "duration" ) );
|
||||
set.setInt( "time", q.getFieldInt( "time" ) );
|
||||
set.setInt( "price", q.getFieldInt( "price" ) );
|
||||
//
|
||||
// create and insert template
|
||||
L2EtcItemTemplate *tmpl = new L2EtcItemTemplate( etcItemType, set );
|
||||
m_allTemplates[item_id] = (L2ItemTemplate *)tmpl;
|
||||
|
||||
// count bytes allocated
|
||||
if( tmpl ) m_nTotalBytesAllocated += (sizeof(L2EtcItemTemplate) + 2*wcslen(tmpl->getName()));
|
||||
} // next row
|
||||
}
|
||||
else
|
||||
LogError( L"ItemTable::loadEtcItems(): MySQL error: %s", con->getErrorStr() );
|
||||
}
|
||||
|
||||
void ItemTable::loadArmors( void *vcon )
|
||||
{
|
||||
MysqlConnection *con = (MysqlConnection *)vcon;
|
||||
MysqlQuery q;
|
||||
q.create( L"SELECT item_id, name, bodypart, crystallizable, armor_type, weight,"
|
||||
L" material, crystal_type, avoid_modify, duration, time, p_def, m_def, mp_bonus,"
|
||||
L" price, crystal_count, sellable, dropable, destroyable, tradeable, enchant4_skill, skill FROM armor" );
|
||||
if( con->executeQuery( q ) )
|
||||
{
|
||||
while( q.getNextRow() )
|
||||
{
|
||||
StatsSet set;
|
||||
int item_id;
|
||||
int bodypart_i = 0;
|
||||
L2ItemSlot bodypart = SLOT_NONE;
|
||||
wchar_t wstr[256] = {0};
|
||||
char *str = NULL;
|
||||
//
|
||||
L2ItemSubType armorType = L2ItemTemplate::getArmorTypeByName( q.getFieldStr( "armor_type" ) );
|
||||
item_id = q.getFieldInt( 0 );
|
||||
set.setInt( "item_id", item_id );
|
||||
q.getFieldStrW( "name", wstr, 255 );
|
||||
set.setWString( "name", wstr );
|
||||
bodypart = L2ItemTemplate::getSlotByName( q.getFieldStr( "bodypart" ) );
|
||||
bodypart_i = (int)bodypart;
|
||||
set.setInt( "bodypart", bodypart_i );
|
||||
set.setBool( "crystallizable", q.getFieldBool( "crystallizable" ) );
|
||||
set.setInt( "crystal_count", q.getFieldInt( "crystal_count" ) );
|
||||
set.setBool( "sellable", q.getFieldBool( "sellable" ) );
|
||||
set.setBool( "dropable", q.getFieldBool( "dropable" ) );
|
||||
set.setBool( "destroyable", q.getFieldBool( "destroyable" ) );
|
||||
set.setBool( "tradeable", q.getFieldBool( "tradeable" ) );
|
||||
set.setString( "enchant4_skill", q.getFieldStr( "enchant4_skill" ) );
|
||||
set.setString( "skill", str = q.getFieldStr( "skill" ) );
|
||||
|
||||
// determine type1 & type2
|
||||
if( bodypart == SLOT_NECK || bodypart == SLOT_HAIR
|
||||
|| bodypart == SLOT_HAIR2 || bodypart == SLOT_HAIRALL
|
||||
|| (bodypart & SLOT_L_EAR) != 0 || (bodypart & SLOT_L_FINGER) != 0)
|
||||
{
|
||||
set.setInt( "type1", L2ItemTemplate::TYPE1_WEAPON_RING_EARRING_NECKLACE );
|
||||
set.setInt( "type2", L2ItemTemplate::TYPE2_ACCESSORY );
|
||||
}
|
||||
else
|
||||
{
|
||||
set.setInt( "type1", L2ItemTemplate::TYPE1_SHIELD_ARMOR );
|
||||
set.setInt( "type2", L2ItemTemplate::TYPE2_SHIELD_ARMOR );
|
||||
}
|
||||
|
||||
set.setInt( "weight", q.getFieldInt( "weight" ) );
|
||||
set.setInt( "material", (int)L2ItemTemplate::getMaterialByName( q.getFieldStr( "material" ) ) );
|
||||
set.setInt( "crystal_type", (int)L2ItemTemplate::getCrystalTypeByName( q.getFieldStr( "crystal_type" ) ) );
|
||||
set.setInt( "avoid_modify", q.getFieldInt( "avoid_modify" ) );
|
||||
set.setInt( "duration", q.getFieldInt( "duration" ) );
|
||||
set.setInt( "time", q.getFieldInt( "time" ) );
|
||||
set.setInt( "p_def", q.getFieldInt( "p_def" ) );
|
||||
set.setInt( "m_def", q.getFieldInt( "m_def" ) );
|
||||
set.setInt( "mp_bonus", q.getFieldInt( "mp_bonus" ) );
|
||||
set.setInt( "price", q.getFieldInt( "price" ) );
|
||||
|
||||
// deal with pet items: type1, type2 & bodypart
|
||||
if( armorType == ARMOR_PET )
|
||||
{
|
||||
if( bodypart == SLOT_NECK )
|
||||
{
|
||||
set.setInt( "type1", L2ItemTemplate::TYPE1_WEAPON_RING_EARRING_NECKLACE );
|
||||
set.setInt( "type2", L2ItemTemplate::TYPE2_ACCESSORY );
|
||||
set.setInt( "bodypart", (int)SLOT_NECK );
|
||||
}
|
||||
else
|
||||
{
|
||||
set.setInt( "type1", L2ItemTemplate::TYPE1_SHIELD_ARMOR );
|
||||
L2ItemSlot temp_b = SLOT_NONE;
|
||||
//int temp_b = 0;
|
||||
set.getInt( "bodypart", (int *)&temp_b );
|
||||
switch( temp_b )
|
||||
{
|
||||
case SLOT_WOLF:
|
||||
set.setInt( "type2", L2ItemTemplate::TYPE2_PET_WOLF );
|
||||
break;
|
||||
case SLOT_GREATWOLF:
|
||||
set.setInt( "type2", L2ItemTemplate::TYPE2_PET_EVOLVEDWOLF );
|
||||
break;
|
||||
case SLOT_HATCHLING:
|
||||
set.setInt( "type2", L2ItemTemplate::TYPE2_PET_HATCHLING );
|
||||
break;
|
||||
case SLOT_BABYPET:
|
||||
set.setInt( "type2", L2ItemTemplate::TYPE2_PET_BABY );
|
||||
break;
|
||||
default:
|
||||
set.setInt( "type2", L2ItemTemplate::TYPE2_PET_STRIDER );
|
||||
break;
|
||||
}
|
||||
set.setInt( "bodypart", (int)SLOT_CHEST );
|
||||
}
|
||||
}
|
||||
|
||||
// create and insert template
|
||||
L2ArmorTemplate *tmpl = new L2ArmorTemplate( armorType, set );
|
||||
m_allTemplates[item_id] = (L2ItemTemplate *)tmpl;
|
||||
|
||||
// count bytes allocated
|
||||
if( tmpl ) m_nTotalBytesAllocated += (sizeof(L2ArmorTemplate) + 2*wcslen(tmpl->getName()));
|
||||
//else throw std::bad_alloc( "ItemTable:loadArmors: new L2ArmorTemplate" );
|
||||
}
|
||||
}
|
||||
else
|
||||
LogError( L"ItemTable::loadArmors(): MySQL error: %s", con->getErrorStr() );
|
||||
}
|
||||
|
||||
void ItemTable::loadWeapons( void *vcon )
|
||||
{
|
||||
MysqlConnection *con = (MysqlConnection *)vcon;
|
||||
MysqlQuery q;
|
||||
q.create( L"SELECT item_id, name, bodypart, crystallizable, weight, soulshots, spiritshots,"
|
||||
L" material, crystal_type, p_dam, rnd_dam, weaponType, critical, hit_modify, avoid_modify,"
|
||||
L" shield_def, shield_def_rate, atk_speed, mp_consume, m_dam, duration, time, price, crystal_count,"
|
||||
L" sellable, dropable, destroyable, tradeable, skill,enchant4_skill_id,enchant4_skill_lvl, "
|
||||
L" onCast_skill_id, onCast_skill_lvl, onCast_skill_chance, onCrit_skill_id, "
|
||||
L" onCrit_skill_lvl, onCrit_skill_chance, change_weaponId FROM weapon" );
|
||||
if( con->executeQuery( q ) )
|
||||
{
|
||||
while( q.getNextRow() )
|
||||
{
|
||||
StatsSet set;
|
||||
int item_id;
|
||||
int bodypart_i = 0;
|
||||
L2ItemSlot bodypart = SLOT_NONE;
|
||||
wchar_t wstr[256] = {0};
|
||||
//
|
||||
L2ItemSubType weaponType = L2ItemTemplate::getWeaponTypeByName( q.getFieldStr( "weaponType" ) );
|
||||
item_id = q.getFieldInt( 0 );
|
||||
set.setInt( "item_id", item_id );
|
||||
q.getFieldStrW( "name", wstr, 255 );
|
||||
set.setWString( "name", wstr );
|
||||
|
||||
// lets see if this is a shield
|
||||
if( weaponType == WEAPON_NONE )
|
||||
{
|
||||
set.setInt( "type1", L2ItemTemplate::TYPE1_SHIELD_ARMOR );
|
||||
set.setInt( "type2", L2ItemTemplate::TYPE2_SHIELD_ARMOR );
|
||||
}
|
||||
else
|
||||
{
|
||||
set.setInt( "type1", L2ItemTemplate::TYPE1_WEAPON_RING_EARRING_NECKLACE );
|
||||
set.setInt( "type2", L2ItemTemplate::TYPE2_WEAPON );
|
||||
}
|
||||
|
||||
bodypart = L2ItemTemplate::getSlotByName( q.getFieldStr( "bodypart" ) );
|
||||
bodypart_i = (int)bodypart;
|
||||
set.setInt( "bodypart", bodypart_i );
|
||||
|
||||
set.setInt( "material", (int)L2ItemTemplate::getMaterialByName( q.getFieldStr( "material" ) ) );
|
||||
set.setInt( "crystal_type", (int)L2ItemTemplate::getCrystalTypeByName( q.getFieldStr( "crystal_type" ) ) );
|
||||
|
||||
set.setBool( "crystallizable", q.getFieldBool( "crystallizable" ) );
|
||||
set.setInt( "weight", q.getFieldInt( "weight" ) );
|
||||
set.setInt( "soulshots", q.getFieldInt( "soulshots" ) );
|
||||
set.setInt( "spiritshots", q.getFieldInt( "spiritshots" ) );
|
||||
set.setInt( "p_dam", q.getFieldInt( "p_dam" ) );
|
||||
set.setInt( "rnd_dam", q.getFieldInt( "rnd_dam" ) );
|
||||
set.setInt( "critical", q.getFieldInt( "critical" ) );
|
||||
set.setInt( "hit_modify", q.getFieldInt( "hit_modify" ) );
|
||||
set.setInt( "avoid_modify", q.getFieldInt( "avoid_modify" ) );
|
||||
set.setInt( "shield_def", q.getFieldInt( "shield_def" ) );
|
||||
set.setInt( "shield_def_rate", q.getFieldInt( "shield_def_rate" ) );
|
||||
set.setInt( "atk_speed", q.getFieldInt( "atk_speed" ) );
|
||||
set.setInt( "mp_consume", q.getFieldInt( "mp_consume" ) );
|
||||
set.setInt( "m_dam", q.getFieldInt( "m_dam" ) );
|
||||
set.setInt( "duration", q.getFieldInt( "duration" ) );
|
||||
set.setInt( "time", q.getFieldInt( "time" ) );
|
||||
set.setInt( "price", q.getFieldInt( "price" ) );
|
||||
set.setInt( "crystal_count", q.getFieldInt( "crystal_count" ) );
|
||||
set.setBool( "sellable", q.getFieldBool( "sellable" ) );
|
||||
set.setBool( "dropable", q.getFieldBool( "dropable" ) );
|
||||
set.setBool( "destroyable", q.getFieldBool( "destroyable" ) );
|
||||
set.setBool( "tradeable", q.getFieldBool( "tradeable" ) );
|
||||
set.setString( "skill", q.getFieldStr( "skill" ) );
|
||||
|
||||
set.setInt( "enchant4_skill_id", q.getFieldInt( "enchant4_skill_id" ) );
|
||||
set.setInt( "enchant4_skill_lvl", q.getFieldInt( "enchant4_skill_lvl" ) );
|
||||
|
||||
/*item.set.set("onCast_skill_id", rset.getInt("onCast_skill_id"));
|
||||
item.set.set("onCast_skill_lvl", rset.getInt("onCast_skill_lvl"));
|
||||
item.set.set("onCast_skill_chance", rset.getInt("onCast_skill_chance"));
|
||||
|
||||
item.set.set("onCrit_skill_id", rset.getInt("onCrit_skill_id"));
|
||||
item.set.set("onCrit_skill_lvl", rset.getInt("onCrit_skill_lvl"));
|
||||
item.set.set("onCrit_skill_chance", rset.getInt("onCrit_skill_chance"));*/
|
||||
|
||||
set.setInt( "change_weaponId", q.getFieldInt( "change_weaponId" ) );
|
||||
|
||||
if( weaponType == WEAPON_PET )
|
||||
{
|
||||
set.setInt( "type1", L2ItemTemplate::TYPE1_WEAPON_RING_EARRING_NECKLACE );
|
||||
if( bodypart == SLOT_WOLF )
|
||||
set.setInt( "type2", L2ItemTemplate::TYPE2_PET_WOLF );
|
||||
else if( bodypart == SLOT_GREATWOLF )
|
||||
set.setInt( "type2", L2ItemTemplate::TYPE2_PET_EVOLVEDWOLF );
|
||||
else if( bodypart == SLOT_HATCHLING )
|
||||
set.setInt( "type2", L2ItemTemplate::TYPE2_PET_HATCHLING );
|
||||
else if( bodypart == SLOT_BABYPET )
|
||||
set.setInt( "type2", L2ItemTemplate::TYPE2_PET_BABY );
|
||||
else
|
||||
set.setInt( "type2", L2ItemTemplate::TYPE2_PET_STRIDER );
|
||||
|
||||
bodypart = SLOT_R_HAND;
|
||||
set.setInt( "bodypart", (int)SLOT_R_HAND);
|
||||
}
|
||||
|
||||
// create and insert template
|
||||
L2WeaponTemplate *tmpl = new L2WeaponTemplate( weaponType, set );
|
||||
m_allTemplates[item_id] = (L2ItemTemplate *)tmpl;
|
||||
|
||||
// count bytes allocated
|
||||
if( tmpl ) m_nTotalBytesAllocated += (sizeof(L2WeaponTemplate) + 2*wcslen(tmpl->getName()));
|
||||
//else throw std::bad_alloc( "ItemTable:loadWeapons new L2WeaponTemplate" );
|
||||
}
|
||||
}
|
||||
else
|
||||
LogError( L"ItemTable::loadWeapons(): MySQL error: %s", con->getErrorStr() );
|
||||
}
|
||||
|
||||
const L2ItemTemplate *ItemTable::getTemplate( unsigned int itemId )
|
||||
{
|
||||
if( !m_allTemplates ) return NULL;
|
||||
if( itemId < 0 || itemId > m_maxItemId )
|
||||
{
|
||||
LogError( L"ItemTable::getTemplate( %u ): request to incorrect itemId (max id is %u)\n",
|
||||
itemId, m_maxItemId );
|
||||
return NULL;
|
||||
}
|
||||
return (const L2ItemTemplate *)m_allTemplates[itemId];
|
||||
}
|
47
L2C_Server/datatables/ItemTable.h
Normal file
47
L2C_Server/datatables/ItemTable.h
Normal file
@ -0,0 +1,47 @@
|
||||
#pragma once
|
||||
#include "l2c_utils.h"
|
||||
#include "../world/templates/StatsSet.h"
|
||||
#include "../world/templates/item/L2ItemTemplate.h"
|
||||
#include "../world/templates/item/L2ArmorTemplate.h"
|
||||
#include "../world/templates/item/L2EtcItemTemplate.h"
|
||||
#include "../world/templates/item/L2WeaponTemplate.h"
|
||||
|
||||
class ItemTable
|
||||
{
|
||||
protected:
|
||||
ItemTable();
|
||||
~ItemTable();
|
||||
static ItemTable *s_instance;
|
||||
static int s_refCount;
|
||||
|
||||
public:
|
||||
static ItemTable *getInstance();
|
||||
static void freeInstance();
|
||||
|
||||
public:
|
||||
void load();
|
||||
void reload();
|
||||
|
||||
public:
|
||||
const L2ItemTemplate *getTemplate( unsigned int itemId );
|
||||
|
||||
protected:
|
||||
static unsigned int select_count( void *vcon, const wchar_t *table );
|
||||
static unsigned int select_max( void *vcon, const wchar_t *table );
|
||||
void loadEtcItems( void *vcon );
|
||||
void loadArmors( void *vcon );
|
||||
void loadWeapons( void *vcon );
|
||||
|
||||
protected:
|
||||
CriticalSection m_lock;
|
||||
L2ItemTemplate **m_allTemplates;
|
||||
//L2ArmorTemplate *m_armorTemplates;
|
||||
//L2WeaponTemplate *m_weaponTemplates;
|
||||
//L2EtcItemTemplate *m_etcItemTemplates;
|
||||
unsigned int m_cnt_all;
|
||||
unsigned int m_maxItemId;
|
||||
//unsigned int m_cnt_armors;
|
||||
//unsigned int m_cnt_weapons;
|
||||
//unsigned int m_cnt_etcItems;
|
||||
unsigned long long int m_nTotalBytesAllocated;
|
||||
};
|
39
L2C_Server/enums.h
Normal file
39
L2C_Server/enums.h
Normal file
@ -0,0 +1,39 @@
|
||||
#pragma once
|
||||
|
||||
enum GS_LoginFailReason
|
||||
{
|
||||
REASON_IP_BANNED = 1,
|
||||
REASON_IP_RESERVED = 2,
|
||||
REASON_WRONG_HEXID = 3,
|
||||
REASON_ID_RESERVED = 4,
|
||||
REASON_NO_FREE_ID = 5,
|
||||
NOT_AUTHED = 6,
|
||||
REASON_ALREADY_LOGGED_IN = 7
|
||||
};
|
||||
|
||||
enum GS_ServerListStatus
|
||||
{
|
||||
SERVER_LIST_STATUS = 0x01,
|
||||
SERVER_LIST_CLOCK = 0x02,
|
||||
SERVER_LIST_SQUARE_BRACKET = 0x03,
|
||||
SERVER_LIST_MAX_PLAYERS = 0x04,
|
||||
SERVER_LIST_TEST_SERVER = 0x05
|
||||
};
|
||||
|
||||
enum ServerStatus
|
||||
{
|
||||
SERVER_STATUS_AUTO = 0x00,
|
||||
SERVER_STATUS_GOOD = 0x01,
|
||||
SERVER_STATUS_NORMAL = 0x02,
|
||||
SERVER_STATUS_FULL = 0x03,
|
||||
SERVER_STATUS_DOWN = 0x04,
|
||||
SERVER_STATUS_GM_ONLY = 0x05
|
||||
};
|
||||
|
||||
enum ClientState
|
||||
{
|
||||
CLIENT_STATE_OFFLINE = 0x00,
|
||||
CLIENT_STATE_CONNECTED = 0x01,
|
||||
CLIENT_STATE_AUTHED = 0x02,
|
||||
CLIENT_STATE_IN_GAME = 0x03
|
||||
};
|
110
L2C_Server/net/ClientPacketHandler.cpp
Normal file
110
L2C_Server/net/ClientPacketHandler.cpp
Normal file
@ -0,0 +1,110 @@
|
||||
#include "pch.h"
|
||||
#include "Log.h"
|
||||
#include "l2c_utils.h"
|
||||
#include "net/GameClient/GameClient.h"
|
||||
#include "ClientPacketHandler.h"
|
||||
#include "ServerPackets.h"
|
||||
#include "GS.h"
|
||||
|
||||
ClientPacketHandler::ClientPacketHandler( GameClient *cl )
|
||||
{
|
||||
m_cl = cl;
|
||||
}
|
||||
|
||||
ClientPacketHandler::~ClientPacketHandler()
|
||||
{
|
||||
m_cl = NULL;
|
||||
}
|
||||
|
||||
void ClientPacketHandler::handlePacket( L2GamePacket *pack )
|
||||
{
|
||||
L2GamePacket *reply = NULL;
|
||||
unsigned char opcode = pack->getByteAt(2);
|
||||
|
||||
switch( m_cl->getState() )
|
||||
{
|
||||
case CLIENT_STATE_CONNECTED:
|
||||
{
|
||||
switch( opcode )
|
||||
{
|
||||
case 0x0E: reply = ProtocolVersion( pack ); break;
|
||||
case 0x2B: reply = AuthLogin( pack ); break;
|
||||
default: invalidPacket( pack, opcode, 0 ); break;
|
||||
}
|
||||
} break;
|
||||
case CLIENT_STATE_AUTHED:
|
||||
{
|
||||
switch( opcode )
|
||||
{
|
||||
case 0x00: reply = Logout( pack ); break;
|
||||
case 0x0C: reply = CharacterCreate( pack ); break;
|
||||
case 0x12: reply = CharacterSelect( pack ); break;
|
||||
case 0x13: reply = NewCharacter( pack ); break;
|
||||
case 0xD0:
|
||||
{
|
||||
unsigned int opcode2 = pack->getByteAt(3) | (pack->getByteAt(4) << 8);
|
||||
switch( opcode2 )
|
||||
{
|
||||
case 0x0036: reply = RequestGotoLobby( pack ); break;
|
||||
default: invalidPacket( pack, opcode, opcode2 ); break;
|
||||
}
|
||||
} break;
|
||||
default: invalidPacket( pack, opcode, 0 ); break;
|
||||
}
|
||||
} break;
|
||||
case CLIENT_STATE_IN_GAME:
|
||||
{
|
||||
switch( opcode )
|
||||
{
|
||||
case 0x00: reply = Logout( pack ); break;
|
||||
case 0xD0:
|
||||
{
|
||||
unsigned int opcode2 = pack->getByteAt(3) | (pack->getByteAt(4) << 8);
|
||||
switch( opcode2 )
|
||||
{
|
||||
case 0x0001: break; // RequestManorList
|
||||
default: invalidPacket( pack, opcode, opcode2 ); break;
|
||||
}
|
||||
} break;
|
||||
default: invalidPacket( pack, opcode, 0 ); break;
|
||||
}
|
||||
} break;
|
||||
}
|
||||
|
||||
if( reply )
|
||||
{
|
||||
m_cl->sendPacket( reply, true ); // true - delete reply immediately
|
||||
}
|
||||
}
|
||||
|
||||
void ClientPacketHandler::invalidPacket( L2GamePacket *p, unsigned int opcode, unsigned int opcode2 )
|
||||
{
|
||||
unsigned int plen = p->getPacketSize();
|
||||
unsigned char *buf = (unsigned char *)p->getBytesPtr();
|
||||
ClientState st = m_cl->getState();
|
||||
wchar_t sn[32] = {0};
|
||||
wcscpy( sn, L"Unknown" );
|
||||
switch( st )
|
||||
{
|
||||
case CLIENT_STATE_OFFLINE: wcscpy( sn, L"OFFLINE" ); break;
|
||||
case CLIENT_STATE_CONNECTED: wcscpy( sn, L"CONNECTED" ); break;
|
||||
case CLIENT_STATE_AUTHED: wcscpy( sn, L"AUTHED" ); break;
|
||||
case CLIENT_STATE_IN_GAME: wcscpy( sn, L"IN_GAME" ); break;
|
||||
}
|
||||
if( opcode2 == 0 )
|
||||
LogWarning( L"%s: Invalid packet 0x%02X (len %u) in state: [%s]", m_cl->toString(), opcode, plen, sn );
|
||||
else
|
||||
LogWarning( L"%s: Invalid packet 0x%02X:%04X (len %u) in state: [%s]", m_cl->toString(), opcode, opcode2, plen, sn );
|
||||
unsigned int i = 0;
|
||||
while( i < plen )
|
||||
{
|
||||
wchar_t str[32] = {0};
|
||||
swprintf( str, 32, L"%02X ", buf[i] );
|
||||
LogFull( false, false, RGB(200,200,200), RGB(0,0,0), str );
|
||||
if( i>0 && (i%16 == 0) ) LogFull( false, true, RGB(200,200,200), RGB(0,0,0), L"" );
|
||||
i++;
|
||||
}
|
||||
LogFull( false, true, RGB(128,0,0), RGB(255,255,255), L"" );
|
||||
LogFull( false, true, RGB(128,0,0), RGB(255,255,255), L"dump end" );
|
||||
}
|
||||
|
28
L2C_Server/net/ClientPacketHandler.h
Normal file
28
L2C_Server/net/ClientPacketHandler.h
Normal file
@ -0,0 +1,28 @@
|
||||
#pragma once
|
||||
|
||||
class GameClient;
|
||||
|
||||
class ClientPacketHandler
|
||||
{
|
||||
public:
|
||||
ClientPacketHandler( GameClient *cl );
|
||||
~ClientPacketHandler();
|
||||
|
||||
public:
|
||||
void handlePacket( L2GamePacket *pack );
|
||||
|
||||
protected:
|
||||
L2GamePacket *ProtocolVersion( L2GamePacket *pack );
|
||||
L2GamePacket *AuthLogin( L2GamePacket *pack );
|
||||
L2GamePacket *NewCharacter( L2GamePacket *pack );
|
||||
L2GamePacket *CharacterCreate( L2GamePacket *pack );
|
||||
L2GamePacket *RequestGotoLobby( L2GamePacket *pack );
|
||||
L2GamePacket *Logout( L2GamePacket *pack );
|
||||
L2GamePacket *CharacterSelect( L2GamePacket *pack );
|
||||
|
||||
protected:
|
||||
void invalidPacket( L2GamePacket *p, unsigned int opcode, unsigned int opcode2 );
|
||||
|
||||
protected:
|
||||
GameClient *m_cl;
|
||||
};
|
237
L2C_Server/net/ClientPool.cpp
Normal file
237
L2C_Server/net/ClientPool.cpp
Normal file
@ -0,0 +1,237 @@
|
||||
#include "pch.h"
|
||||
#include "Log.h"
|
||||
#include "ClientPool.h"
|
||||
#include "GS.h"
|
||||
#include "utils/Exception.h"
|
||||
|
||||
ClientPool::ClientPool()
|
||||
{
|
||||
m_clients = new std::vector<GameClient *>();
|
||||
}
|
||||
|
||||
ClientPool::~ClientPool()
|
||||
{
|
||||
if( m_clients )
|
||||
{
|
||||
delete m_clients;
|
||||
m_clients = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
int ClientPool::getCurrentOnline()
|
||||
{
|
||||
int ret = 0;
|
||||
m_lock.Lock();
|
||||
if( m_clients ) ret = m_clients->size();
|
||||
m_lock.Unlock();
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool ClientPool::addClient( SOCKET s_cl, const char *ip, int port )
|
||||
{
|
||||
if( !m_clients || !ip || (s_cl == INVALID_SOCKET) || (port == 0) ) return false;
|
||||
bool ret = false;
|
||||
m_lock.Lock();
|
||||
// check current online
|
||||
int max_online = GameServer::getInstance()->getMaxPlayers();
|
||||
int cur_online = m_clients->size();
|
||||
if( cur_online < max_online )
|
||||
{
|
||||
// create client
|
||||
GameClient *pl = new GameClient( s_cl, ip, port );
|
||||
if( pl )
|
||||
{
|
||||
m_clients->push_back( pl );
|
||||
ret = pl->startThread();
|
||||
}
|
||||
}
|
||||
else // maximum online reached
|
||||
{
|
||||
LogWarning( L"Maximum online count (%d) is reached, cannot accept new clients", max_online );
|
||||
ret = false;
|
||||
// close client connection
|
||||
L2PNet_shutdown( s_cl );
|
||||
L2PNet_closesocket( s_cl );
|
||||
}
|
||||
m_lock.Unlock();
|
||||
return ret;
|
||||
}
|
||||
|
||||
void ClientPool::disconnectAllPlayers()
|
||||
{
|
||||
if( !m_clients ) return;
|
||||
m_lock.Lock();
|
||||
try
|
||||
{
|
||||
std::vector<GameClient *>::iterator iter;
|
||||
for( iter = m_clients->begin(); iter != m_clients->end(); iter++ )
|
||||
{
|
||||
GameClient *pl = (*iter);
|
||||
if( pl )
|
||||
{
|
||||
pl->disconnectForceAndWaitThreadToStop( true, false );
|
||||
delete pl;
|
||||
}
|
||||
}
|
||||
m_clients->clear();
|
||||
}
|
||||
catch( std::exception& e )
|
||||
{
|
||||
LogError( L"ClientPool::disconnectAllPlayers(): excpt %S", e.what() );
|
||||
}
|
||||
catch( Exception& e )
|
||||
{
|
||||
LogError( L"ClientPool::disconnectAllPlayers(): Exception: %S", e.what() );
|
||||
e.logStackTrace();
|
||||
}
|
||||
m_lock.Unlock();
|
||||
}
|
||||
|
||||
bool ClientPool::disconnectPlayerByAccount( const wchar_t *accountName, bool reasonDualLogin )
|
||||
{
|
||||
if( !m_clients ) return false;
|
||||
bool ret = false;
|
||||
m_lock.Lock();
|
||||
try
|
||||
{
|
||||
std::vector<GameClient *>::iterator iter;
|
||||
for( iter = m_clients->begin(); iter != m_clients->end(); iter++ )
|
||||
{
|
||||
GameClient *pl = (*iter);
|
||||
if( pl )
|
||||
{
|
||||
if( wcscmp( accountName, pl->getAccount() ) == 0 )
|
||||
{
|
||||
pl->disconnectForceAndWaitThreadToStop( false, reasonDualLogin );
|
||||
m_clients->erase( iter );
|
||||
ret = true;
|
||||
delete pl;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch( std::exception& e )
|
||||
{
|
||||
LogError( L"ClientPool::disconnectPlayerByAccount( %s ): excpt %S", accountName, e.what() );
|
||||
}
|
||||
catch( Exception& e )
|
||||
{
|
||||
LogError( L"ClientPool::disconnectPlayerByAccount(): Exception: %S", e.what() );
|
||||
e.logStackTrace();
|
||||
}
|
||||
m_lock.Unlock();
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool ClientPool::notifyClientThreadEnded( GameClient *ptr )
|
||||
{
|
||||
if( !m_clients || !ptr ) return false;
|
||||
bool ret = false;
|
||||
m_lock.Lock();
|
||||
try
|
||||
{
|
||||
std::vector<GameClient *>::iterator iter;
|
||||
for( iter = m_clients->begin(); iter != m_clients->end(); iter++ )
|
||||
{
|
||||
GameClient *pl = (*iter);
|
||||
if( pl == ptr )
|
||||
{
|
||||
#ifdef _DEBUG
|
||||
LogDebug( L"ClientPool: deleted player %s", pl->toString() );
|
||||
#endif
|
||||
m_clients->erase( iter );
|
||||
ret = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch( std::exception& e )
|
||||
{
|
||||
LogError( L"ClientPool::notifyClientThreadEnded(): excpt %S", e.what() );
|
||||
}
|
||||
catch( Exception& e )
|
||||
{
|
||||
LogError( L"ClientPool::notifyClientThreadEnded(): Exception: %S", e.what() );
|
||||
e.logStackTrace();
|
||||
}
|
||||
m_lock.Unlock();
|
||||
if( !ret )
|
||||
{
|
||||
LogWarning( L"ClientPool: not found player to del %s", ptr->toString() );
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
void ClientPool::notifyClientAuthResponse( const wchar_t *accountName, bool ok )
|
||||
{
|
||||
if( !m_clients || !accountName ) return;
|
||||
m_lock.Lock();
|
||||
try
|
||||
{
|
||||
std::vector<GameClient *>::iterator iter;
|
||||
for( iter = m_clients->begin(); iter != m_clients->end(); iter++ )
|
||||
{
|
||||
GameClient *pl = (*iter);
|
||||
if( pl )
|
||||
{
|
||||
if( wcscmp( pl->getAccount(), accountName ) == 0 )
|
||||
{
|
||||
if( !ok ) // client auth failed
|
||||
{
|
||||
pl->disconnectForceAndWaitThreadToStop( false, false );
|
||||
m_clients->erase( iter );
|
||||
delete pl;
|
||||
}
|
||||
else // auth ok
|
||||
{
|
||||
pl->notifySessionKeysOK();
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch( std::exception& e )
|
||||
{
|
||||
LogError( L"ClientPool::notifyClientAuthResponse(): excpt %S", e.what() );
|
||||
}
|
||||
catch( Exception& e )
|
||||
{
|
||||
LogError( L"ClientPool::notifyClientAuthResponse(): Exception: %S", e.what() );
|
||||
e.logStackTrace();
|
||||
}
|
||||
m_lock.Unlock();
|
||||
}
|
||||
|
||||
void ClientPool::dumpClientsToLog()
|
||||
{
|
||||
if( !m_clients ) return;
|
||||
int n = 0;
|
||||
Log( L"===== ClientPool clients dump =====" );
|
||||
m_lock.Lock();
|
||||
try
|
||||
{
|
||||
std::vector<GameClient *>::iterator iter;
|
||||
for( iter = m_clients->begin(); iter != m_clients->end(); iter++ )
|
||||
{
|
||||
GameClient *pl = (*iter);
|
||||
if( pl )
|
||||
{
|
||||
Log( L" %d: %s", n, pl->toString() );
|
||||
n++;
|
||||
}
|
||||
}
|
||||
m_clients->clear();
|
||||
}
|
||||
catch( std::exception& e )
|
||||
{
|
||||
LogError( L"ClientPool::dumpClientsToLog(): excpt %S", e.what() );
|
||||
}
|
||||
catch( Exception& e )
|
||||
{
|
||||
LogError( L"ClientPool::dumpClientsToLog(): Exception: %S", e.what() );
|
||||
e.logStackTrace();
|
||||
}
|
||||
m_lock.Unlock();
|
||||
Log( L"===== ClientPool clients dump end =====" );
|
||||
}
|
29
L2C_Server/net/ClientPool.h
Normal file
29
L2C_Server/net/ClientPool.h
Normal file
@ -0,0 +1,29 @@
|
||||
#pragma once
|
||||
#include "l2c_utils.h"
|
||||
#include "net/GameClient/GameClient.h"
|
||||
|
||||
class ClientPool
|
||||
{
|
||||
public:
|
||||
ClientPool();
|
||||
~ClientPool();
|
||||
|
||||
public:
|
||||
int getCurrentOnline();
|
||||
|
||||
public:
|
||||
bool addClient( SOCKET s_cl, const char *ip, int port );
|
||||
|
||||
public:
|
||||
void disconnectAllPlayers();
|
||||
bool disconnectPlayerByAccount( const wchar_t *accountName, bool reasonDualLogin );
|
||||
bool notifyClientThreadEnded( GameClient *ptr );
|
||||
void notifyClientAuthResponse( const wchar_t *accountName, bool ok );
|
||||
|
||||
public:
|
||||
void dumpClientsToLog();
|
||||
|
||||
protected:
|
||||
CriticalSection m_lock;
|
||||
std::vector<GameClient *> *m_clients;
|
||||
};
|
115
L2C_Server/net/GameClient/GameClient.cpp
Normal file
115
L2C_Server/net/GameClient/GameClient.cpp
Normal file
@ -0,0 +1,115 @@
|
||||
#include "pch.h"
|
||||
#include "l2c_utils.h"
|
||||
#include "GameClient.h"
|
||||
|
||||
GameClient::GameClient( SOCKET s_cl, const char *ip, int port )
|
||||
{
|
||||
setUnused();
|
||||
if( ip )
|
||||
{
|
||||
strncpy( m_ip, ip, 16 ); m_ip[16] = 0;
|
||||
l2c_ansi_to_unicode( m_ip, m_wip, 16 );
|
||||
}
|
||||
m_port = port;
|
||||
m_sock = s_cl;
|
||||
m_was_force_disconnected = false;
|
||||
m_flagStop = m_isRunning = false;
|
||||
swprintf( m_tostring, 128, L"Client [IP: %s:%d]", m_wip, m_port );
|
||||
m_ph = new ClientPacketHandler( this );
|
||||
}
|
||||
|
||||
GameClient::~GameClient()
|
||||
{
|
||||
if( m_ph )
|
||||
{
|
||||
delete m_ph;
|
||||
m_ph = NULL;
|
||||
}
|
||||
if( m_obf )
|
||||
{
|
||||
delete m_obf;
|
||||
m_obf = NULL;
|
||||
}
|
||||
if( m_player )
|
||||
{
|
||||
delete m_player; // really? some notification?
|
||||
m_player = NULL;
|
||||
}
|
||||
setUnused();
|
||||
}
|
||||
|
||||
void GameClient::setUnused()
|
||||
{
|
||||
// zero our members
|
||||
m_account[0] = 0;
|
||||
memset( m_playKey, 0, sizeof(m_playKey) );
|
||||
memset( m_loginKey, 0, sizeof(m_loginKey) );
|
||||
m_ip[0] = 0;
|
||||
m_wip[0] = 0;
|
||||
m_port = 0;
|
||||
m_sock = INVALID_SOCKET;
|
||||
m_tostring[0] = 0;
|
||||
m_state = CLIENT_STATE_OFFLINE;
|
||||
m_xor_enabled = false;
|
||||
memset( m_xor_key_recv, 0, sizeof(m_xor_key_recv) );
|
||||
memset( m_xor_key_send, 0, sizeof(m_xor_key_send) );
|
||||
m_protocolIsOk = false;
|
||||
m_netStats.clear();
|
||||
m_opcodeObfuscationSeed = 0;
|
||||
m_obf = NULL;
|
||||
m_player = NULL;
|
||||
}
|
||||
|
||||
const wchar_t *GameClient::getAccount() const { return (const wchar_t *)m_account; }
|
||||
void GameClient::getPlayKey( unsigned char *buffer ) const { memcpy( buffer, m_playKey, 8 ); }
|
||||
void GameClient::getLoginKey( unsigned char *buffer ) const { memcpy( buffer, m_loginKey, 8 ); }
|
||||
const char *GameClient::getIpA() const { return (const char *)m_ip; }
|
||||
const wchar_t *GameClient::getIpW() const { return (const wchar_t *)m_wip; }
|
||||
int GameClient::getPort() const { return m_port; }
|
||||
const wchar_t *GameClient::toString() const { return (const wchar_t *)m_tostring; }
|
||||
ClientState GameClient::getState() const { return m_state; }
|
||||
bool GameClient::isProtocolOk() const { return m_protocolIsOk; }
|
||||
const GameClientNetStats *GameClient::getNetStats() const { return &m_netStats; }
|
||||
unsigned int GameClient::getOpcodeObfuscationSeed() const { return m_opcodeObfuscationSeed; }
|
||||
L2PCodeObfuscator *GameClient::getOpcodeObfuscator() { return m_obf; }
|
||||
GamePlayer *GameClient::getPlayer() { return m_player; }
|
||||
|
||||
void GameClient::setAccount( const wchar_t *acc )
|
||||
{
|
||||
if( acc )
|
||||
{
|
||||
wcsncpy( m_account, acc, sizeof(m_account)/sizeof(wchar_t) );
|
||||
m_account[sizeof(m_account)/sizeof(wchar_t) - 1] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void GameClient::setSessionKeys( const unsigned char *playKey, const unsigned char *loginKey )
|
||||
{
|
||||
memcpy( m_playKey, playKey, 8 );
|
||||
memcpy( m_loginKey, loginKey, 8 );
|
||||
}
|
||||
|
||||
void GameClient::setProtocolOk( bool b ) { m_protocolIsOk = b; }
|
||||
|
||||
void GameClient::setOpcodeObfuscationSeed( unsigned int s )
|
||||
{
|
||||
m_opcodeObfuscationSeed = s;
|
||||
if( m_obf )
|
||||
{
|
||||
delete m_obf;
|
||||
m_obf = NULL;
|
||||
}
|
||||
if( s != 0 )
|
||||
{
|
||||
m_obf = new L2PCodeObfuscator();
|
||||
m_obf->init_tables( m_opcodeObfuscationSeed );
|
||||
}
|
||||
}
|
||||
|
||||
void GameClient::setPlayer( GamePlayer *newPlayer, bool changeState, ClientState newState )
|
||||
{
|
||||
if( m_player ) delete m_player;
|
||||
m_player = NULL;
|
||||
if( newPlayer ) m_player = newPlayer;
|
||||
if( changeState ) m_state = newState;
|
||||
}
|
78
L2C_Server/net/GameClient/GameClient.h
Normal file
78
L2C_Server/net/GameClient/GameClient.h
Normal file
@ -0,0 +1,78 @@
|
||||
#pragma once
|
||||
#include "enums.h"
|
||||
#include "net/ClientPacketHandler.h"
|
||||
#include "GameClientNetStats.h"
|
||||
#include "world/model/character/GamePlayer.h"
|
||||
|
||||
class GameClient
|
||||
{
|
||||
public:
|
||||
GameClient( SOCKET s_cl, const char *ip, int port );
|
||||
virtual ~GameClient();
|
||||
virtual void setUnused();
|
||||
|
||||
public:
|
||||
const wchar_t *getAccount() const;
|
||||
void getPlayKey( unsigned char *buffer ) const;
|
||||
void getLoginKey( unsigned char *buffer ) const;
|
||||
const char *getIpA() const;
|
||||
const wchar_t *getIpW() const;
|
||||
int getPort() const;
|
||||
const wchar_t *toString() const;
|
||||
ClientState getState() const;
|
||||
bool isProtocolOk() const;
|
||||
const GameClientNetStats *getNetStats() const;
|
||||
unsigned int getOpcodeObfuscationSeed() const;
|
||||
L2PCodeObfuscator *getOpcodeObfuscator();
|
||||
GamePlayer *getPlayer();
|
||||
|
||||
public:
|
||||
void setAccount( const wchar_t *acc );
|
||||
void setSessionKeys( const unsigned char *playKey, const unsigned char *loginKey );
|
||||
void setProtocolOk( bool b ) ;
|
||||
void setOpcodeObfuscationSeed( unsigned int s );
|
||||
void setPlayer( GamePlayer *newPlayer, bool changeState = false, ClientState newState = CLIENT_STATE_OFFLINE );
|
||||
|
||||
public:
|
||||
// begins client processing, starts thread
|
||||
bool startThread();
|
||||
// stops all client activity, disconnects socket, optionally sends packet before client close
|
||||
void disconnectForceAndWaitThreadToStop( bool server_shutdown, bool reasonDualLogin );
|
||||
// signal thread to stop (no wait to stop)
|
||||
void signalThreadStop( bool setMarkToDeleteSelfAndCleanupClientPool );
|
||||
bool sendPacket( L2GamePacket *pack, bool deleteAfterSend = false );
|
||||
void enable_XOR_crypt( bool bEnable, unsigned char *initial_key );
|
||||
void notifySessionKeysOK();
|
||||
|
||||
protected:
|
||||
// signals to stop and waits for thread to stop
|
||||
bool stopThread();
|
||||
static DWORD WINAPI ClientThread( LPVOID lpvParam );
|
||||
|
||||
protected:
|
||||
// informational stings
|
||||
char m_ip[16];
|
||||
wchar_t m_wip[16];
|
||||
int m_port;
|
||||
wchar_t m_account[128];
|
||||
wchar_t m_tostring[128];
|
||||
// network-related vars
|
||||
SOCKET m_sock;
|
||||
bool m_was_force_disconnected;
|
||||
bool m_flagStop;
|
||||
bool m_isRunning;
|
||||
CriticalSection m_cs_send;
|
||||
ClientState m_state;
|
||||
bool m_xor_enabled;
|
||||
unsigned char m_xor_key_send[16];
|
||||
unsigned char m_xor_key_recv[16];
|
||||
ClientPacketHandler *m_ph;
|
||||
bool m_protocolIsOk;
|
||||
GameClientNetStats m_netStats;
|
||||
unsigned int m_opcodeObfuscationSeed;
|
||||
L2PCodeObfuscator *m_obf;
|
||||
unsigned char m_playKey[8];
|
||||
unsigned char m_loginKey[8];
|
||||
//
|
||||
GamePlayer *m_player;
|
||||
};
|
67
L2C_Server/net/GameClient/GameClientNet.cpp
Normal file
67
L2C_Server/net/GameClient/GameClientNet.cpp
Normal file
@ -0,0 +1,67 @@
|
||||
#include "pch.h"
|
||||
#include "Log.h"
|
||||
#include "l2c_utils.h"
|
||||
#include "GameClient.h"
|
||||
#include "GS.h"
|
||||
#include "../ServerPackets.h"
|
||||
#include "utils/Exception.h"
|
||||
|
||||
bool GameClient::sendPacket( L2GamePacket *pack, bool deleteAfterSend /*= false*/ )
|
||||
{
|
||||
if( !pack ) return false;
|
||||
if( m_sock == INVALID_SOCKET ) return false;
|
||||
//
|
||||
int r = 0;
|
||||
unsigned int sentLen = 0;
|
||||
unsigned int pack_size = pack->getPacketSize();
|
||||
bool ret = true;
|
||||
// encode xor if xor enabled
|
||||
if( m_xor_enabled )
|
||||
{
|
||||
// KeyPacket is first packet sent to client by server.
|
||||
// it is never encrypted
|
||||
if( m_netStats.ullPacketsSent > 0 )
|
||||
pack->encodeXOR( m_xor_key_send );
|
||||
}
|
||||
// send packet (lock before send, release lock after send)
|
||||
m_cs_send.Lock();
|
||||
r = L2PacketSend( m_sock, pack, 2000, &sentLen );
|
||||
m_cs_send.Unlock();
|
||||
// delete pack, if set to do so
|
||||
if( deleteAfterSend ) delete pack;
|
||||
// validate send OK
|
||||
if( r<=0 || (sentLen != pack_size) ) // some error
|
||||
{
|
||||
ret = false;
|
||||
throw Exception( "%S: send error: sent %u, retval %d", toString(), sentLen, r );
|
||||
}
|
||||
// count stats
|
||||
m_netStats.addSentPacket( sentLen );
|
||||
//
|
||||
return ret;
|
||||
}
|
||||
|
||||
void GameClient::enable_XOR_crypt( bool bEnable, unsigned char *initial_key )
|
||||
{
|
||||
m_xor_enabled = bEnable;
|
||||
if( bEnable )
|
||||
{
|
||||
memcpy( m_xor_key_recv, initial_key, sizeof(m_xor_key_recv) );
|
||||
memcpy( m_xor_key_send, initial_key, sizeof(m_xor_key_send) );
|
||||
}
|
||||
else
|
||||
{
|
||||
memset( m_xor_key_recv, 0, sizeof(m_xor_key_recv) );
|
||||
memset( m_xor_key_send, 0, sizeof(m_xor_key_send) );
|
||||
}
|
||||
}
|
||||
|
||||
void GameClient::notifySessionKeysOK()
|
||||
{
|
||||
#ifdef _DEBUG
|
||||
LogDebug( L"GameClient::notifySessionKeysOK()" );
|
||||
#endif
|
||||
m_state = CLIENT_STATE_AUTHED;
|
||||
swprintf( m_tostring, 128, L"Client Acc: %s [IP %s:%d]", m_account, m_wip, m_port );
|
||||
sendPacket( ServerPackets::CharacterSelectionInfo( this ), true );
|
||||
}
|
25
L2C_Server/net/GameClient/GameClientNetStats.cpp
Normal file
25
L2C_Server/net/GameClient/GameClientNetStats.cpp
Normal file
@ -0,0 +1,25 @@
|
||||
#include "pch.h"
|
||||
#include "GameClientNetStats.h"
|
||||
|
||||
GameClientNetStats::GameClientNetStats()
|
||||
{
|
||||
clear();
|
||||
}
|
||||
|
||||
void GameClientNetStats::clear()
|
||||
{
|
||||
ullPacketsSent = ullBytesSent = ullPacketsRcvd = ullBytesRcvd = 0;
|
||||
}
|
||||
|
||||
void GameClientNetStats::addSentPacket( unsigned long long len )
|
||||
{
|
||||
ullPacketsSent++;
|
||||
ullBytesSent += len;
|
||||
}
|
||||
|
||||
void GameClientNetStats::addRcvdPacket( unsigned long long len )
|
||||
{
|
||||
ullPacketsRcvd++;
|
||||
ullBytesRcvd += len;
|
||||
}
|
||||
|
15
L2C_Server/net/GameClient/GameClientNetStats.h
Normal file
15
L2C_Server/net/GameClient/GameClientNetStats.h
Normal file
@ -0,0 +1,15 @@
|
||||
#pragma once
|
||||
|
||||
class GameClientNetStats
|
||||
{
|
||||
public:
|
||||
GameClientNetStats();
|
||||
void clear();
|
||||
void addSentPacket( unsigned long long len );
|
||||
void addRcvdPacket( unsigned long long len );
|
||||
public:
|
||||
unsigned long long ullPacketsSent;
|
||||
unsigned long long ullBytesSent;
|
||||
unsigned long long ullPacketsRcvd;
|
||||
unsigned long long ullBytesRcvd;
|
||||
};
|
188
L2C_Server/net/GameClient/GameClientThread.cpp
Normal file
188
L2C_Server/net/GameClient/GameClientThread.cpp
Normal file
@ -0,0 +1,188 @@
|
||||
#include "pch.h"
|
||||
#include "Log.h"
|
||||
#include "l2c_utils.h"
|
||||
#include "GameClient.h"
|
||||
#include "GS.h"
|
||||
#include "utils/Exception.h"
|
||||
|
||||
bool GameClient::startThread()
|
||||
{
|
||||
if( m_isRunning ) return false;
|
||||
m_flagStop = false;
|
||||
unsigned int tid = 0;
|
||||
HANDLE hThread = (HANDLE)_beginthreadex( NULL, 0,
|
||||
(unsigned int (__stdcall *)(void *))GameClient::ClientThread, (void *)this,
|
||||
0, &tid );
|
||||
bool ret = false;
|
||||
if( hThread != NULL )
|
||||
{
|
||||
ret = true;
|
||||
CloseHandle( hThread );
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool GameClient::stopThread()
|
||||
{
|
||||
if( !m_isRunning ) return false;
|
||||
m_flagStop = true;
|
||||
while( m_isRunning ) Sleep(50);
|
||||
m_flagStop = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
void GameClient::disconnectForceAndWaitThreadToStop( bool server_shutdown, bool reasonDualLogin )
|
||||
{
|
||||
m_was_force_disconnected = true;
|
||||
// TODO: send SystemMessage about account in use if dual login
|
||||
if( reasonDualLogin )
|
||||
{
|
||||
}
|
||||
// TODO: send ServerClose / ExRestartClient? if server shutdown
|
||||
if( server_shutdown )
|
||||
{
|
||||
}
|
||||
stopThread(); // stops thread; thread cleanups socket himself
|
||||
}
|
||||
|
||||
void GameClient::signalThreadStop( bool setMarkToDeleteSelfAndCleanupClientPool )
|
||||
{
|
||||
m_flagStop = true;
|
||||
m_was_force_disconnected = !setMarkToDeleteSelfAndCleanupClientPool;
|
||||
}
|
||||
|
||||
|
||||
DWORD WINAPI GameClient::ClientThread( LPVOID lpvParam )
|
||||
{
|
||||
GameClient *pl = (GameClient *)lpvParam;
|
||||
pl->m_was_force_disconnected = false;
|
||||
GameServer *gs = GameServer::getInstance();
|
||||
pl->m_isRunning = true;
|
||||
pl->m_state = CLIENT_STATE_CONNECTED;
|
||||
pl->m_netStats.clear(); // reset nework statistics
|
||||
|
||||
int r, bReadyRead, bReadyWrite;
|
||||
unsigned char *packbuffer = NULL;
|
||||
unsigned int rcvdLen = 0;
|
||||
L2GamePacket *pack = NULL;
|
||||
|
||||
try
|
||||
{
|
||||
// allocate packets recv buffer
|
||||
packbuffer = (unsigned char *)malloc( 65536 );
|
||||
if( !packbuffer ) throw std::bad_alloc( "allocate packbuffer (64 Kb) failed" );
|
||||
|
||||
// packets recv loop
|
||||
while( !pl->m_flagStop )
|
||||
{
|
||||
// wait for socket ready to receive data
|
||||
r = L2PNet_select( pl->m_sock, L2PNET_SELECT_READ, 100, &bReadyRead, &bReadyWrite );
|
||||
if( r == 0 )
|
||||
{
|
||||
// select timout
|
||||
// update world state? move moving objects?...
|
||||
continue;
|
||||
}
|
||||
if( r == -1 ) // select error
|
||||
{
|
||||
LogError( L"%s: L2PNet_select() error!", pl->toString() );
|
||||
throw Exception( "select error" );
|
||||
}
|
||||
|
||||
// recv packet
|
||||
r = L2PacketReceive_buffer( pl->m_sock, 1000, &rcvdLen, packbuffer );
|
||||
if( r <= 0 )
|
||||
{
|
||||
if( r == 0 )
|
||||
{
|
||||
LogWarning( L"%s: closed connection? (received %u, retval %d)", pl->toString(), rcvdLen, r );
|
||||
pl->m_flagStop = true;
|
||||
break;
|
||||
}
|
||||
if( r < 0 )
|
||||
{
|
||||
LogError( L"%s: failed to receive packet (received %u, retval %d)", pl->toString(), rcvdLen, r );
|
||||
throw Exception( "network packet receive error" );
|
||||
}
|
||||
}
|
||||
|
||||
// count stats
|
||||
pl->m_netStats.addRcvdPacket( rcvdLen );
|
||||
|
||||
// create packet object
|
||||
pack = new L2GamePacket( packbuffer, rcvdLen );
|
||||
|
||||
// decode XOR if enabled
|
||||
if( pl->m_xor_enabled ) pack->decodeXOR( pl->m_xor_key_recv );
|
||||
|
||||
// deobfuscate opcode if enabled
|
||||
if( pl->m_opcodeObfuscationSeed > 0 )
|
||||
{
|
||||
// HACK: here buffer that should be constant, is modified (but length is not changed thoug)
|
||||
pl->m_obf->decodeIDs( (unsigned char *)pack->getBytesPtr() );
|
||||
}
|
||||
|
||||
#ifdef _DEBUG
|
||||
int opcode = pack->getByteAt(2);
|
||||
LogDebug( L"%s: opcode %02X", pl->toString(), opcode );
|
||||
#endif
|
||||
|
||||
// handle game client packet...
|
||||
pl->m_ph->handlePacket( pack );
|
||||
|
||||
// free packet object
|
||||
delete pack; pack = NULL;
|
||||
}
|
||||
}
|
||||
catch( L2P_Exception& e )
|
||||
{
|
||||
LogError( L"ClientThread: %s: L2Packets exception: %S", pl->toString(), e.what() );
|
||||
}
|
||||
catch( std::bad_alloc& e )
|
||||
{
|
||||
LogError( L"ClientThread: %s: bad_alloc %S", pl->toString(), e.what() );
|
||||
}
|
||||
catch( std::exception& e )
|
||||
{
|
||||
LogError( L"ClientThread: %s: std::excpt %S", pl->toString(), e.what() );
|
||||
}
|
||||
catch( Exception& e )
|
||||
{
|
||||
LogError( L"ClientThread: Exception: %S", e.what() );
|
||||
e.logStackTrace();
|
||||
}
|
||||
|
||||
// player disconnected - remove player account from login server list of authed accounts
|
||||
if( pl->m_account[0] ) // has authed??
|
||||
gs->logoutAccountFromLoginServer( pl->m_account );
|
||||
|
||||
// cleanup socket
|
||||
L2PNet_shutdown( pl->m_sock );
|
||||
L2PNet_closesocket( pl->m_sock );
|
||||
pl->m_sock = INVALID_SOCKET;
|
||||
pl->m_state = CLIENT_STATE_OFFLINE;
|
||||
// cleanup opcode obfuscator
|
||||
if( pl->m_obf )
|
||||
{
|
||||
delete pl->m_obf;
|
||||
pl->m_obf = NULL;
|
||||
pl->m_opcodeObfuscationSeed = 0;
|
||||
}
|
||||
|
||||
// free packets buffer
|
||||
if( packbuffer ) free( packbuffer ); packbuffer = NULL;
|
||||
|
||||
// mark self as finished
|
||||
pl->m_isRunning = false;
|
||||
|
||||
// notify GS ClientPool about client stop, if needed
|
||||
if( !pl->m_was_force_disconnected )
|
||||
{
|
||||
#ifdef _DEBUG
|
||||
LogDebug( L"ClientThread s: Calling ClientPool to cleanup & deleting self", pl->toString() );
|
||||
#endif
|
||||
gs->getClientPool()->notifyClientThreadEnded( pl );
|
||||
delete pl;
|
||||
}
|
||||
return 0;
|
||||
}
|
11
L2C_Server/net/ServerPackets.h
Normal file
11
L2C_Server/net/ServerPackets.h
Normal file
@ -0,0 +1,11 @@
|
||||
#pragma once
|
||||
|
||||
class ServerPackets
|
||||
{
|
||||
public:
|
||||
static L2GamePacket *KeyPacket( GameClient *pl );
|
||||
static L2GamePacket *CharacterSelectionInfo( GameClient *pl );
|
||||
static L2GamePacket *NewCharacterSuccess( GameClient *pl );
|
||||
static L2GamePacket *CharCreateFail( GameClient *pl, int reason );
|
||||
static L2GamePacket *CharCreateOK( GameClient *pl );
|
||||
};
|
33
L2C_Server/net/clientpackets/c_AuthLogin.cpp
Normal file
33
L2C_Server/net/clientpackets/c_AuthLogin.cpp
Normal file
@ -0,0 +1,33 @@
|
||||
#include "pch.h"
|
||||
#include "Log.h"
|
||||
#include "l2c_utils.h"
|
||||
#include "net/GameClient/GameClient.h"
|
||||
#include "../ClientPacketHandler.h"
|
||||
#include "../ServerPackets.h"
|
||||
#include "GS.h"
|
||||
|
||||
/* _loginName = readS().toLowerCase();
|
||||
_playKey2 = readD();
|
||||
_playKey1 = readD();
|
||||
_loginKey1 = readD();
|
||||
_loginKey2 = readD(); */
|
||||
L2GamePacket *ClientPacketHandler::AuthLogin( L2GamePacket *pack )
|
||||
{
|
||||
GameServer *gs = GameServer::getInstance();
|
||||
pack->getPacketType();
|
||||
wchar_t login[128] = {0};
|
||||
unsigned char loginKey[8] = {0,0,0,0,0,0,0,0};
|
||||
unsigned char playKey[8] = {0,0,0,0,0,0,0,0};
|
||||
wcsncpy( login, pack->readUnicodeStringPtr(), 128 ); login[127] = 0;
|
||||
pack->readBytes( playKey+4, 4 );
|
||||
pack->readBytes( playKey, 4 );
|
||||
pack->readBytes( loginKey, 8 );
|
||||
//
|
||||
m_cl->setAccount( login );
|
||||
m_cl->setSessionKeys( playKey, loginKey );
|
||||
//
|
||||
if( gs->getConfig()->Debug ) LogDebug( L"%s: AuthLogin %s", m_cl->toString(), login );
|
||||
gs->addWaitingClientAndSendPlayerAuthRequest( login, loginKey, playKey );
|
||||
return NULL;
|
||||
}
|
||||
|
213
L2C_Server/net/clientpackets/c_CharacterCreate.cpp
Normal file
213
L2C_Server/net/clientpackets/c_CharacterCreate.cpp
Normal file
@ -0,0 +1,213 @@
|
||||
#include "pch.h"
|
||||
#include "Log.h"
|
||||
#include "l2c_utils.h"
|
||||
#include "net/GameClient/GameClient.h"
|
||||
#include "../ClientPacketHandler.h"
|
||||
#include "../ServerPackets.h"
|
||||
#include "GS.h"
|
||||
#include "utils/Utils.h"
|
||||
#include "datatables/CharNameTable.h"
|
||||
#include "datatables/CharTemplateTable.h"
|
||||
#include "datatables/ItemTable.h"
|
||||
|
||||
L2GamePacket *ClientPacketHandler::CharacterCreate( L2GamePacket *pack )
|
||||
{
|
||||
pack->getPacketType();
|
||||
const wchar_t *name = pack->readUnicodeStringPtr(); // _name = readS();
|
||||
int race = pack->readD(); // _race = readD();
|
||||
int sex = pack->readD(); // _sex = readD();
|
||||
int classId = pack->readD(); // _classId = readD();
|
||||
pack->readD(); //_int = readD();
|
||||
pack->readD(); //_str = readD();
|
||||
pack->readD(); //_con = readD();
|
||||
pack->readD(); //_men = readD();
|
||||
pack->readD(); //_dex = readD();
|
||||
pack->readD(); //_wit = readD();
|
||||
int hairStyle = pack->readD(); //_hairStyle = readD();
|
||||
int hairColor = pack->readD(); //_hairColor = readD();
|
||||
int face = pack->readD(); //_face = readD();
|
||||
|
||||
// CharacterCreate
|
||||
GameServer *gs = GameServer::getInstance();
|
||||
|
||||
int name_len = wcslen( name );
|
||||
if( name_len < 1 || name_len > 16 )
|
||||
{
|
||||
if( gs->getConfig()->Debug )
|
||||
LogWarning( L"%s: Character Creation Failure: Character name [%s] is invalid. "
|
||||
L"Message generated: Your title cannot exceed 16 characters in length. Please try again.",
|
||||
m_cl->toString(), name );
|
||||
return ServerPackets::CharCreateFail( m_cl, L2Game_CharCreateFail::REASON_16_ENG_CHARS );
|
||||
}
|
||||
|
||||
// alphanumeric name
|
||||
if( !Utils_isValidCharName( name ) )
|
||||
{
|
||||
if( gs->getConfig()->Debug )
|
||||
LogWarning( L"%s: Character Creation Failure: Character name [%s] is invalid. "
|
||||
L"Message generated: Incorrect name. Please try again.", m_cl->toString(), name );
|
||||
return ServerPackets::CharCreateFail( m_cl, L2Game_CharCreateFail::REASON_INCORRECT_NAME );
|
||||
}
|
||||
|
||||
bool create_ok = false;
|
||||
|
||||
CharNameTable::getInstance()->LockDB();
|
||||
if( !CharNameTable::getInstance()->doesCharNameExist( name ) )
|
||||
{
|
||||
// check maximum char count
|
||||
int current_char_count = CharNameTable::getInstance()->getAccountCharCount( m_cl->getAccount() );
|
||||
// TODO: CharacterCreate maximum char count may be in GS config?
|
||||
if( current_char_count >= 7 )
|
||||
{
|
||||
if( gs->getConfig()->Debug ) LogWarning( L"%s: Max number of characters reached. Creation failed.", m_cl->toString() );
|
||||
CharNameTable::getInstance()->UnlockDB();
|
||||
return ServerPackets::CharCreateFail( m_cl, L2Game_CharCreateFail::REASON_TOO_MANY_CHARACTERS );
|
||||
}
|
||||
|
||||
// get class template
|
||||
const L2PlayerTemplate *tmpl = CharTemplateTable::getTemplate( classId );
|
||||
if( !tmpl )
|
||||
{
|
||||
LogError( L"%s: CharacterCreate: template is NULL!", m_cl->toString() );
|
||||
CharNameTable::getInstance()->UnlockDB();
|
||||
return ServerPackets::CharCreateFail( m_cl, L2Game_CharCreateFail::REASON_CREATION_FAILED );
|
||||
}
|
||||
// base class must be 1 lvl
|
||||
if( tmpl->classBaseLevel > 1 )
|
||||
{
|
||||
LogError( L"%s: CharacterCreate: template base level must be 1, but: %d!",
|
||||
m_cl->toString(), tmpl->classBaseLevel );
|
||||
CharNameTable::getInstance()->UnlockDB();
|
||||
return ServerPackets::CharCreateFail( m_cl, L2Game_CharCreateFail::REASON_CREATION_FAILED );
|
||||
}
|
||||
|
||||
// generate new objectId for char
|
||||
unsigned int oid = gs->getIdFactory()->getNextObjectId();
|
||||
|
||||
// get race by class id
|
||||
const ClassId *class_id = ClassIdTree::getInstance()->getClassId( classId );
|
||||
assert( class_id != NULL );
|
||||
int race_real = (int)class_id->getRace();
|
||||
if( race_real != race )
|
||||
LogWarning( L"%s: possible hacker: invalid race in CharacterCreate (%d)!=(%d)",
|
||||
m_cl->toString(), race, race_real );
|
||||
|
||||
// insert new character into DB
|
||||
MysqlQuery q;
|
||||
q.create(
|
||||
L"INSERT INTO characters ("
|
||||
L"account_name,charId,char_name,level,maxHp,curHp,maxCp,curCp,maxMp,curMp,"
|
||||
L"x,y,z,heading,"
|
||||
L"face,hairStyle,hairColor,sex,exp,sp,karma,fame,pvpkills,pkkills,clanid,race,classid,"
|
||||
L"deletetime,cancraft,title,accesslevel,online,isin7sdungeon,clan_privs,wantspeace,base_class,"
|
||||
L"newbie,nobless,power_grade,last_recom_date) values ("
|
||||
L"'%s',%u,'%s',1, %d,%d,%d,%d,%d,%d, " // acc,oid,charName, ...
|
||||
L"%d,%d,%d,0, " // x,y,z,heading
|
||||
L"%d,%d,%d, %d,0,0,0,0,0,0,0,%d,%d, " // face,hs,hc, ..
|
||||
L"0,0,'',0,0,0,0,0,%d, " // deltime, ...
|
||||
L"1,0,0,%u)", /////////////////////////////////////////////////////////////////////////
|
||||
m_cl->getAccount(), oid, name, (int)(tmpl->baseHpMax), (int)(tmpl->baseHpMax),
|
||||
(int)(tmpl->baseCpMax), (int)(tmpl->baseCpMax), (int)(tmpl->baseMpMax), (int)(tmpl->baseMpMax),
|
||||
tmpl->spawnX, tmpl->spawnY, tmpl->spawnZ,
|
||||
face, hairStyle, hairColor, sex, race_real, classId, classId, (unsigned int)time( NULL )
|
||||
);
|
||||
MysqlConnection *con = gs->getDBConnection();
|
||||
if( con->executeQuery( q ) )
|
||||
{
|
||||
create_ok = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
LogError( L"%s: CharacterCreate: cannot create: MySQL error: %s\n", m_cl->toString(), con->getErrorStr() );
|
||||
}
|
||||
q.clear();
|
||||
|
||||
// add shortcuts
|
||||
//(int slotId, int pageId, int shortcutType, int shortcutId, int shortcutLevel, int characterType)
|
||||
// add attack shortcut L2ShortCut(0, 0, 3, 2, 0, 1);
|
||||
q.create( L"INSERT INTO character_shortcuts (charId,slot,page,type,shortcut_id,level,class_index) VALUES ('%u',0,0,3,2,0,1)", oid );
|
||||
if( !con->executeQuery( q ) ) LogError( L"CharacterCreate: shortcuts: MySQL: %s", con->getErrorStr() );
|
||||
// add take shortcut L2ShortCut(3, 0, 3, 5, 0, 1);
|
||||
q.create( L"INSERT INTO character_shortcuts (charId,slot,page,type,shortcut_id,level,class_index) VALUES ('%u',3,0,3,5,0,1)", oid );
|
||||
if( !con->executeQuery( q ) ) LogError( L"CharacterCreate: shortcuts: MySQL: %s", con->getErrorStr() );
|
||||
// add sit shortcut L2ShortCut(10, 0, 3, 0, 0, 1);
|
||||
q.create( L"INSERT INTO character_shortcuts (charId,slot,page,type,shortcut_id,level,class_index) VALUES ('%u',10,0,3,0,0,1)", oid );
|
||||
if( !con->executeQuery( q ) ) LogError( L"CharacterCreate: shortcuts: MySQL: %s", con->getErrorStr() );
|
||||
|
||||
// add start items
|
||||
const std::list<L2PlayerTemplate::PcTemplateItem> itemsList = tmpl->getItems();
|
||||
std::list<L2PlayerTemplate::PcTemplateItem>::const_iterator itemsListIter = itemsList.begin();
|
||||
while( itemsListIter != itemsList.end() )
|
||||
{
|
||||
L2PlayerTemplate::PcTemplateItem item = (*itemsListIter);
|
||||
const L2ItemTemplate *item_tmpl = ItemTable::getInstance()->getTemplate( item.getItemId() );
|
||||
if( !item_tmpl )
|
||||
{
|
||||
LogError( L"CharacterCreate: cannot add starter item: template NULL (%d)", item.getItemId() );
|
||||
continue;
|
||||
}
|
||||
//
|
||||
q.clear();
|
||||
unsigned int item_oid = gs->getIdFactory()->getNextObjectId();
|
||||
if( item.isEquipped() )
|
||||
{
|
||||
// location set to paperdoll
|
||||
q.create( L"INSERT INTO items (owner_id,item_id,count,loc,loc_data,enchant_level,"
|
||||
L"object_id,custom_type1,custom_type2,mana_left,time) VALUES "
|
||||
L"('%u','%d','%d','PAPERDOLL','%d',0,'%u','%d','%d','%d','%d')",
|
||||
oid, item.getItemId(), item.getAmount(), (int)item_tmpl->getBodyPart(),
|
||||
item_oid, item_tmpl->getType1(), item_tmpl->getType2(),
|
||||
item_tmpl->getDuration(), item_tmpl->getTime() );
|
||||
}
|
||||
else
|
||||
{
|
||||
// location set to inventory
|
||||
q.create( L"INSERT INTO items (owner_id,item_id,count,loc,loc_data,enchant_level,"
|
||||
L"object_id,custom_type1,custom_type2,mana_left,time) VALUES "
|
||||
L"('%u','%d','%d','INVENTORY','%d',0,'%u','%d','%d','%d','%d')",
|
||||
oid, item.getItemId(), item.getAmount(), (int)item_tmpl->getBodyPart(),
|
||||
item_oid, item_tmpl->getType1(), item_tmpl->getType2(),
|
||||
item_tmpl->getDuration(), item_tmpl->getTime() );
|
||||
}
|
||||
if( !con->executeQuery( q ) )
|
||||
LogError( L"CharacterCreate: cannot add starter item: MySQL error: %s", con->getErrorStr() );
|
||||
// add tutbook shortcut
|
||||
if( item.getItemId() == 5588 ) // CharacterCreate add Tutorial Guide...
|
||||
{
|
||||
// L2ShortCut(11, 0, 1, item.getObjectId(), 0, 1);
|
||||
q.create( L"INSERT INTO character_shortcuts (charId,slot,page,type,shortcut_id,level,class_index) "
|
||||
L"VALUES ('%u',11,0,1,'%u',0,1)", oid, item_oid );
|
||||
if( !con->executeQuery( q ) )
|
||||
LogError( L"CharacterCreate add Tutorial Guide: MySQL: %s", con->getErrorStr() );
|
||||
}
|
||||
//
|
||||
itemsListIter++;
|
||||
}
|
||||
q.clear();
|
||||
|
||||
// TODO: CharacterCreate add starting skills (need SkillTable & SkillLearn)
|
||||
|
||||
gs->releaseDBConnection( con );
|
||||
}
|
||||
else
|
||||
{
|
||||
if( gs->getConfig()->Debug )
|
||||
LogWarning( L"%s: Character Creation Failure: REASON_NAME_ALREADY_EXISTS.", m_cl->toString() );
|
||||
CharNameTable::getInstance()->UnlockDB();
|
||||
return ServerPackets::CharCreateFail( m_cl, L2Game_CharCreateFail::REASON_NAME_ALREADY_EXISTS );
|
||||
}
|
||||
CharNameTable::getInstance()->UnlockDB();
|
||||
|
||||
if( !create_ok )
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return ServerPackets::CharCreateOK( m_cl );
|
||||
}
|
||||
|
||||
/*
|
||||
private static final String INSERT_CHARACTER = "INSERT INTO characters (account_name,charId,char_name,level,maxHp,curHp,maxCp,curCp,maxMp,curMp,face,hairStyle,hairColor,sex,exp,sp,karma,fame,pvpkills,pkkills,clanid,race,classid,deletetime,cancraft,title,accesslevel,online,isin7sdungeon,clan_privs,wantspeace,base_class,newbie,nobless,power_grade,last_recom_date) values (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)";
|
||||
private static final String UPDATE_CHARACTER = "UPDATE characters SET level=?,maxHp=?,curHp=?,maxCp=?,curCp=?,maxMp=?,curMp=?,face=?,hairStyle=?,hairColor=?,sex=?,heading=?,x=?,y=?,z=?,exp=?,expBeforeDeath=?,sp=?,karma=?,fame=?,pvpkills=?,pkkills=?,rec_have=?,rec_left=?,clanid=?,race=?,classid=?,deletetime=?,title=?,accesslevel=?,online=?,isin7sdungeon=?,clan_privs=?,wantspeace=?,base_class=?,onlinetime=?,punish_level=?,punish_timer=?,newbie=?,nobless=?,power_grade=?,subpledge=?,last_recom_date=?,lvl_joined_academy=?,apprentice=?,sponsor=?,varka_ketra_ally=?,clan_join_expiry_time=?,clan_create_expiry_time=?,char_name=?,death_penalty_level=?,bookmarkslot=?,vitality_points=? WHERE charId=?";
|
||||
private static final String RESTORE_CHARACTER = "SELECT account_name, charId, char_name, level, maxHp, curHp, maxCp, curCp, maxMp, curMp, face, hairStyle, hairColor, sex, heading, x, y, z, exp, expBeforeDeath, sp, karma, fame, pvpkills, pkkills, clanid, race, classid, deletetime, cancraft, title, rec_have, rec_left, accesslevel, online, char_slot, lastAccess, clan_privs, wantspeace, base_class, onlinetime, isin7sdungeon, punish_level, punish_timer, newbie, nobless, power_grade, subpledge, last_recom_date, lvl_joined_academy, apprentice, sponsor, varka_ketra_ally,clan_join_expiry_time,clan_create_expiry_time,death_penalty_level,bookmarkslot,vitality_points FROM characters WHERE charId=?";
|
||||
*/
|
44
L2C_Server/net/clientpackets/c_CharacterSelect.cpp
Normal file
44
L2C_Server/net/clientpackets/c_CharacterSelect.cpp
Normal file
@ -0,0 +1,44 @@
|
||||
#include "pch.h"
|
||||
#include "Log.h"
|
||||
#include "l2c_utils.h"
|
||||
#include "net/GameClient/GameClient.h"
|
||||
#include "../ClientPacketHandler.h"
|
||||
#include "../ServerPackets.h"
|
||||
#include "GS.h"
|
||||
|
||||
|
||||
L2GamePacket *ClientPacketHandler::CharacterSelect( L2GamePacket *pack )
|
||||
{
|
||||
L2GamePacket *ret = NULL;
|
||||
pack->getPacketType();
|
||||
int charSlot = pack->readD();
|
||||
LogDebug( L"CharacterSelect: charSlot : %d", charSlot );
|
||||
// create &load player for game client if not exists
|
||||
if( m_cl->getPlayer() == NULL )
|
||||
{
|
||||
GamePlayer *pl = new GamePlayer( m_cl, 0 );
|
||||
// TODO: CharacterSelect: load char from base
|
||||
m_cl->setPlayer( pl, true, CLIENT_STATE_IN_GAME ); // sets state to IN_GAME
|
||||
//
|
||||
L2Game_CharSelected *p = new L2Game_CharSelected();
|
||||
ret = p;
|
||||
p->p_char_name[0] = 0;
|
||||
p->p_opcodeObfuscatorSeed = rand();
|
||||
p->p_title[0] = 0;
|
||||
p->p_classId = 0;
|
||||
p->p_level = 1;
|
||||
unsigned char playKey[8];
|
||||
m_cl->getPlayKey( playKey );
|
||||
p->p_sessionId = playKey[0] | (playKey[1] << 8) | (playKey[2] << 16) | (playKey[3] << 24); // playOkID1
|
||||
//
|
||||
p->create( L2_VERSION_T23 );
|
||||
//
|
||||
m_cl->setOpcodeObfuscationSeed( p->p_opcodeObfuscatorSeed );
|
||||
}
|
||||
else
|
||||
LogError( L"Net: %s: CharacterSelect: already has player attached to GameClient?? Bad Bad Error or Cheater!",
|
||||
m_cl->toString() );
|
||||
//
|
||||
return ret;
|
||||
}
|
||||
|
27
L2C_Server/net/clientpackets/c_Logout.cpp
Normal file
27
L2C_Server/net/clientpackets/c_Logout.cpp
Normal file
@ -0,0 +1,27 @@
|
||||
#include "pch.h"
|
||||
#include "Log.h"
|
||||
#include "l2c_utils.h"
|
||||
#include "net/GameClient/GameClient.h"
|
||||
#include "../ClientPacketHandler.h"
|
||||
#include "../ServerPackets.h"
|
||||
#include "GS.h"
|
||||
|
||||
L2GamePacket *ClientPacketHandler::Logout( L2GamePacket *pack )
|
||||
{
|
||||
pack->getPacketType();
|
||||
L2GamePacket *ret = NULL;
|
||||
switch( m_cl->getState() )
|
||||
{
|
||||
case CLIENT_STATE_OFFLINE:
|
||||
case CLIENT_STATE_CONNECTED:
|
||||
case CLIENT_STATE_AUTHED:
|
||||
ret = NULL;
|
||||
m_cl->signalThreadStop( true ); // we can just disconnect player now
|
||||
break;
|
||||
case CLIENT_STATE_IN_GAME:
|
||||
// TODO: check if player can logout here and now
|
||||
// answer with RestartResponse (LogoutOK) or (LogoutFailed)
|
||||
break;
|
||||
}
|
||||
return ret;
|
||||
}
|
13
L2C_Server/net/clientpackets/c_NewCharacter.cpp
Normal file
13
L2C_Server/net/clientpackets/c_NewCharacter.cpp
Normal file
@ -0,0 +1,13 @@
|
||||
#include "pch.h"
|
||||
#include "Log.h"
|
||||
#include "l2c_utils.h"
|
||||
#include "net/GameClient/GameClient.h"
|
||||
#include "../ClientPacketHandler.h"
|
||||
#include "../ServerPackets.h"
|
||||
#include "GS.h"
|
||||
|
||||
L2GamePacket *ClientPacketHandler::NewCharacter( L2GamePacket *pack )
|
||||
{
|
||||
pack = NULL; // just trigger
|
||||
return ServerPackets::NewCharacterSuccess( m_cl );
|
||||
}
|
37
L2C_Server/net/clientpackets/c_ProtocolVersion.cpp
Normal file
37
L2C_Server/net/clientpackets/c_ProtocolVersion.cpp
Normal file
@ -0,0 +1,37 @@
|
||||
#include "pch.h"
|
||||
#include "Log.h"
|
||||
#include "l2c_utils.h"
|
||||
#include "net/GameClient/GameClient.h"
|
||||
#include "../ClientPacketHandler.h"
|
||||
#include "../ServerPackets.h"
|
||||
#include "GS.h"
|
||||
|
||||
L2GamePacket *ClientPacketHandler::ProtocolVersion( L2GamePacket *pack )
|
||||
{
|
||||
GameServer *gs = GameServer::getInstance();
|
||||
pack->getPacketType();
|
||||
int pv = pack->readD();
|
||||
// just server ping?
|
||||
if( pv == -2 )
|
||||
{
|
||||
m_cl->setProtocolOk( false );
|
||||
m_cl->signalThreadStop( true );
|
||||
if( gs->getConfig()->Debug ) LogDebug( L"%s: received server ping from client (%d)", m_cl->toString(), pv );
|
||||
return NULL;
|
||||
}
|
||||
// protocol version validation
|
||||
if( gs->getConfig()->Debug ) LogDebug( L"%s: protocol version %d", m_cl->toString(), pv );
|
||||
if( pv < gs->getConfig()->min_game_protocol_version ||
|
||||
pv > gs->getConfig()->max_game_protocol_version )
|
||||
{
|
||||
m_cl->setProtocolOk( false );
|
||||
m_cl->signalThreadStop( true );
|
||||
LogWarning( L"%s: invalid protocol version %d (must be [%d..%d])",
|
||||
m_cl->toString(), pv,
|
||||
gs->getConfig()->min_game_protocol_version,
|
||||
gs->getConfig()->max_game_protocol_version );
|
||||
}
|
||||
else m_cl->setProtocolOk( true );
|
||||
// reply KeyPacket
|
||||
return ServerPackets::KeyPacket( m_cl );
|
||||
}
|
15
L2C_Server/net/clientpackets/c_RequestGotoLobby.cpp
Normal file
15
L2C_Server/net/clientpackets/c_RequestGotoLobby.cpp
Normal file
@ -0,0 +1,15 @@
|
||||
#include "pch.h"
|
||||
#include "Log.h"
|
||||
#include "l2c_utils.h"
|
||||
#include "net/GameClient/GameClient.h"
|
||||
#include "../ClientPacketHandler.h"
|
||||
#include "../ServerPackets.h"
|
||||
#include "GS.h"
|
||||
|
||||
|
||||
L2GamePacket *ClientPacketHandler::RequestGotoLobby( L2GamePacket *pack )
|
||||
{
|
||||
pack = NULL; // unused here
|
||||
return ServerPackets::CharacterSelectionInfo( m_cl );
|
||||
}
|
||||
|
15
L2C_Server/net/serverpackets/s_CharCreateFail.cpp
Normal file
15
L2C_Server/net/serverpackets/s_CharCreateFail.cpp
Normal file
@ -0,0 +1,15 @@
|
||||
#include "pch.h"
|
||||
#include "Log.h"
|
||||
#include "l2c_utils.h"
|
||||
#include "net/GameClient/GameClient.h"
|
||||
#include "../ServerPackets.h"
|
||||
#include "GS.h"
|
||||
|
||||
L2GamePacket *ServerPackets::CharCreateFail( GameClient *pl, int reason )
|
||||
{
|
||||
pl = NULL; // reference to player not needed
|
||||
L2Game_CharCreateFail *p = new L2Game_CharCreateFail();
|
||||
p->p_reasonCode = (unsigned int)reason;
|
||||
p->create( L2_VERSION_T23 );
|
||||
return p;
|
||||
}
|
14
L2C_Server/net/serverpackets/s_CharCreateOK.cpp
Normal file
14
L2C_Server/net/serverpackets/s_CharCreateOK.cpp
Normal file
@ -0,0 +1,14 @@
|
||||
#include "pch.h"
|
||||
#include "Log.h"
|
||||
#include "l2c_utils.h"
|
||||
#include "net/GameClient/GameClient.h"
|
||||
#include "../ServerPackets.h"
|
||||
#include "GS.h"
|
||||
|
||||
L2GamePacket *ServerPackets::CharCreateOK( GameClient *pl )
|
||||
{
|
||||
pl = NULL; // reference to player not needed
|
||||
L2Game_CharCreateSuccess *p = new L2Game_CharCreateSuccess();
|
||||
p->create( L2_VERSION_T23 );
|
||||
return p;
|
||||
}
|
225
L2C_Server/net/serverpackets/s_CharacterSelectionInfo.cpp
Normal file
225
L2C_Server/net/serverpackets/s_CharacterSelectionInfo.cpp
Normal file
@ -0,0 +1,225 @@
|
||||
#include "pch.h"
|
||||
#include "Log.h"
|
||||
#include "l2c_utils.h"
|
||||
#include "net/GameClient/GameClient.h"
|
||||
#include "../ServerPackets.h"
|
||||
#include "GS.h"
|
||||
#include "world/templates/item/L2ItemTemplate.h"
|
||||
|
||||
L2GamePacket *ServerPackets::CharacterSelectionInfo( GameClient *pl )
|
||||
{
|
||||
GameServer *gs = GameServer::getInstance();
|
||||
MysqlConnection *con = gs->getDBConnection();
|
||||
L2Game_CharSelectionInfoBlock csb[7];
|
||||
memset( &csb, 0, sizeof(csb) );
|
||||
int i = 0;
|
||||
int num_chars = 0;
|
||||
unsigned int max_lastAccess = 0;
|
||||
int activeCharIndex = -1;
|
||||
MysqlQuery q;
|
||||
q.create( L"SELECT account_name, charId, char_name, level, maxHp, curHp, maxMp, curMp, face, hairStyle, "
|
||||
L"hairColor, sex, heading, x, y, z, exp, sp, karma, pvpkills, pkkills, clanid, race, classid, "
|
||||
L"deletetime, cancraft, title, rec_have, rec_left, accesslevel, online, char_slot, lastAccess, "
|
||||
L"base_class, transform_id FROM characters WHERE account_name='%s' LIMIT 7", pl->getAccount() );
|
||||
if( con->executeQuery( q ) )
|
||||
{
|
||||
i = 0;
|
||||
while( q.getNextRow() )
|
||||
{
|
||||
unsigned int lastAccess = q.getFieldUInt( "lastAccess" );
|
||||
if( lastAccess > max_lastAccess )
|
||||
{
|
||||
max_lastAccess = lastAccess;
|
||||
activeCharIndex = i;
|
||||
}
|
||||
//
|
||||
q.getFieldStrW( 0, csb[i].accountName, 32 );
|
||||
csb[i].charID = q.getFieldUInt( 1 );
|
||||
q.getFieldStrW( 2, csb[i].charName, 32 );
|
||||
csb[i].level = q.getFieldUInt( 3 );
|
||||
csb[i].HP_max = q.getFieldDouble( 4 );
|
||||
csb[i].HP_cur = q.getFieldDouble( 5 );
|
||||
csb[i].MP_max = q.getFieldDouble( 6 );
|
||||
csb[i].MP_cur = q.getFieldDouble( 7 );
|
||||
csb[i].face = q.getFieldUInt( 8 );
|
||||
csb[i].hairStyle = q.getFieldUInt( 9 );
|
||||
csb[i].hairColor = q.getFieldUInt( 10 );
|
||||
csb[i].sex = q.getFieldUInt( 11 );
|
||||
//q.getFieldUInt( 12, ???? ); // no heading
|
||||
csb[i].x = q.getFieldInt( 13 );
|
||||
csb[i].y = q.getFieldInt( 14 );
|
||||
csb[i].z = q.getFieldInt( 15 );
|
||||
csb[i].Exp = q.getFieldUInt64( 16 );
|
||||
csb[i].SP = q.getFieldUInt( 17 );
|
||||
csb[i].karma = q.getFieldUInt( 18 );
|
||||
csb[i].PVP_kills = q.getFieldUInt( 19 );
|
||||
csb[i].PK_kills = q.getFieldUInt( 20 );
|
||||
csb[i].clanID = q.getFieldUInt( 21 );
|
||||
csb[i].race = q.getFieldUInt( 22 );
|
||||
csb[i].classID = q.getFieldUInt( 23 );
|
||||
csb[i].deleteSeconds = q.getFieldUInt( 24 );
|
||||
//q.getFieldUInt( 25, can craft??? );
|
||||
//q.getFieldStrW( 26, title?? );
|
||||
//q.getFieldUInt( 27, rec_have );
|
||||
csb[i].baseClassID = q.getFieldUInt( "base_class" );
|
||||
csb[i].isActive = 1;
|
||||
// fake
|
||||
//csb[i].iid_chest = 0x0967; // DC Robe
|
||||
//csb[i].iid_R_hand = 0x2ec2; // Common Branch of Mother tree
|
||||
//
|
||||
i++;
|
||||
}
|
||||
num_chars = i;
|
||||
}
|
||||
else
|
||||
{
|
||||
LogError( L"CharacterSelectionInfo: MySQL error: %s", con->getErrorStr() );
|
||||
}
|
||||
|
||||
// TODO: load also equipped inventory items
|
||||
for( i=0; i<num_chars; i++ )
|
||||
{
|
||||
q.clear();
|
||||
q.create( L"SELECT object_id,item_id,loc_data,enchant_level FROM items WHERE owner_id=%u AND loc='PAPERDOLL'", csb[i].charID );
|
||||
if( con->executeQuery( q ) )
|
||||
{
|
||||
while( q.getNextRow() )
|
||||
{
|
||||
int slot = q.getFieldInt( "loc_data" );
|
||||
int item_id = q.getFieldInt( "item_id" );
|
||||
int enchant_level = q.getFieldInt( "enchant_level" );
|
||||
//
|
||||
switch( slot )
|
||||
{
|
||||
case 1: csb[i].iid_R_ear = item_id; break;
|
||||
case 2: csb[i].iid_L_ear = item_id; break;
|
||||
//case 3: LREAR
|
||||
case 4: csb[i].iid_neck = item_id; break;
|
||||
case 5: csb[i].iid_L_finger = item_id; break;
|
||||
case 6: csb[i].iid_R_finger = item_id; break;
|
||||
//case 7: LRFINGER
|
||||
case 8: csb[i].iid_head = item_id; break;
|
||||
case 9: csb[i].iid_R_hand = item_id; break;
|
||||
case 10: csb[i].iid_L_hand = item_id; break;
|
||||
case 11: csb[i].iid_gloves = item_id; break;
|
||||
case 12: csb[i].iid_chest = item_id; break;
|
||||
case 13: csb[i].iid_legs = item_id; break;
|
||||
case 14: csb[i].iid_feet = item_id; break;
|
||||
case 15: csb[i].iid_back = item_id; break;
|
||||
case 16: csb[i].iid_R_hand = csb[i].iid_L_hand = item_id; break; // LRHAND
|
||||
case 17: csb[i].iid_chest = item_id; break; // fullarmor?
|
||||
case 18: csb[i].iid_hair = item_id; break;
|
||||
//case 19: ALLDRESS
|
||||
case 20: csb[i].iid_hair_2 = item_id; break;
|
||||
case 21: csb[i].iid_hair_all = item_id; break;
|
||||
case 22: csb[i].iid_R_bracelet = item_id; break;
|
||||
case 23: csb[i].iid_L_bracelet = item_id; break;
|
||||
// 24..29 DECOs
|
||||
case 30: csb[i].iid_belt = item_id; break;
|
||||
}
|
||||
if( slot == SLOT_R_HAND && enchant_level > 0 ) // weapon
|
||||
csb[i].enchantEffect = (unsigned char)(enchant_level & 0x000000FF);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
LogError( L"CharacterSelectionInfo: MySQL error: %s", con->getErrorStr() );
|
||||
}
|
||||
}
|
||||
q.clear();
|
||||
gs->releaseDBConnection( con );
|
||||
|
||||
// retreive playOk1
|
||||
unsigned char playKey[8];
|
||||
memset( playKey, 0, 8 );
|
||||
pl->getPlayKey( playKey );
|
||||
unsigned int playOk1 = *(unsigned int *)&playKey[0];
|
||||
//
|
||||
L2GamePacket *p = new L2GamePacket();
|
||||
p->setPacketType( 0x09 );
|
||||
p->writeD( num_chars ); // number of chars
|
||||
p->writeD( 0x07 ); // server max_chars
|
||||
p->writeC( 0x00 ); // 0x00
|
||||
//
|
||||
for( i=0; i<num_chars; i++ )
|
||||
{
|
||||
p->writeS( csb[i].charName );
|
||||
p->writeUInt( csb[i].charID );
|
||||
p->writeS( pl->getAccount() );
|
||||
p->writeUInt( playOk1 ); // TODO: playOK id 1
|
||||
p->writeUInt( csb[i].clanID );
|
||||
p->writeD( 0x00 );
|
||||
//
|
||||
p->writeUInt( csb[i].sex );
|
||||
p->writeUInt( csb[i].race );
|
||||
p->writeUInt( csb[i].baseClassID );
|
||||
p->writeUInt( (i == activeCharIndex) ? 0x01 : 0x00 ); // is active
|
||||
//
|
||||
p->writeInt( csb[i].x );
|
||||
p->writeInt( csb[i].y );
|
||||
p->writeInt( csb[i].z );
|
||||
p->writeDouble( csb[i].HP_cur );
|
||||
p->writeDouble( csb[i].MP_cur );
|
||||
//
|
||||
p->writeUInt( csb[i].SP );
|
||||
p->writeUInt64( csb[i].Exp );
|
||||
p->writeUInt( csb[i].level );
|
||||
//
|
||||
p->writeUInt( csb[i].karma );
|
||||
p->writeUInt( csb[i].PK_kills );
|
||||
p->writeUInt( csb[i].PVP_kills );
|
||||
//
|
||||
p->writeD( 0x00 ); p->writeD( 0x00 ); p->writeD( 0x00 ); p->writeD( 0x00 );
|
||||
p->writeD( 0x00 ); p->writeD( 0x00 ); p->writeD( 0x00 ); // 7 0x00
|
||||
//
|
||||
// 26 inventory item ids // TODO: equipment ids
|
||||
p->writeD( csb[i].iid_hair_all ); //writeD(charInfoPackage.getPaperdollItemId(Inventory.PAPERDOLL_HAIRALL));
|
||||
p->writeD( csb[i].iid_R_ear ); //writeD(charInfoPackage.getPaperdollItemId(Inventory.PAPERDOLL_REAR));
|
||||
p->writeD( csb[i].iid_L_ear ); //writeD(charInfoPackage.getPaperdollItemId(Inventory.PAPERDOLL_LEAR));
|
||||
p->writeD( csb[i].iid_neck ); //writeD(charInfoPackage.getPaperdollItemId(Inventory.PAPERDOLL_NECK));
|
||||
p->writeD( csb[i].iid_R_finger ); //writeD(charInfoPackage.getPaperdollItemId(Inventory.PAPERDOLL_RFINGER));
|
||||
p->writeD( csb[i].iid_L_finger ); //writeD(charInfoPackage.getPaperdollItemId(Inventory.PAPERDOLL_LFINGER));
|
||||
p->writeD( csb[i].iid_head ); //writeD(charInfoPackage.getPaperdollItemId(Inventory.PAPERDOLL_HEAD));
|
||||
p->writeD( csb[i].iid_R_hand ); //writeD(charInfoPackage.getPaperdollItemId(Inventory.PAPERDOLL_RHAND));
|
||||
p->writeD( csb[i].iid_L_hand ); //writeD(charInfoPackage.getPaperdollItemId(Inventory.PAPERDOLL_LHAND));
|
||||
p->writeD( csb[i].iid_gloves ); //writeD(charInfoPackage.getPaperdollItemId(Inventory.PAPERDOLL_GLOVES));
|
||||
p->writeD( csb[i].iid_chest ); //writeD(charInfoPackage.getPaperdollItemId(Inventory.PAPERDOLL_CHEST));
|
||||
p->writeD( csb[i].iid_legs ); //writeD(charInfoPackage.getPaperdollItemId(Inventory.PAPERDOLL_LEGS));
|
||||
p->writeD( csb[i].iid_feet ); //writeD(charInfoPackage.getPaperdollItemId(Inventory.PAPERDOLL_FEET));
|
||||
p->writeD( csb[i].iid_back ); //writeD(charInfoPackage.getPaperdollItemId(Inventory.PAPERDOLL_BACK));
|
||||
p->writeD( csb[i].iid_LR_hand ); //writeD(charInfoPackage.getPaperdollItemId(Inventory.PAPERDOLL_LRHAND));
|
||||
p->writeD( csb[i].iid_hair ); //writeD(charInfoPackage.getPaperdollItemId(Inventory.PAPERDOLL_HAIR));
|
||||
p->writeD( csb[i].iid_hair_2 ); //writeD(charInfoPackage.getPaperdollItemId(Inventory.PAPERDOLL_HAIR2));
|
||||
p->writeD( csb[i].iid_R_bracelet ); //writeD(charInfoPackage.getPaperdollItemId(Inventory.PAPERDOLL_RBRACELET));
|
||||
p->writeD( csb[i].iid_L_bracelet ); //writeD(charInfoPackage.getPaperdollItemId(Inventory.PAPERDOLL_LBRACELET));
|
||||
p->writeD( 0x00 ); //writeD(charInfoPackage.getPaperdollItemId(Inventory.PAPERDOLL_DECO1));
|
||||
p->writeD( 0x00 ); //writeD(charInfoPackage.getPaperdollItemId(Inventory.PAPERDOLL_DECO2));
|
||||
p->writeD( 0x00 ); //writeD(charInfoPackage.getPaperdollItemId(Inventory.PAPERDOLL_DECO3));
|
||||
p->writeD( 0x00 ); //writeD(charInfoPackage.getPaperdollItemId(Inventory.PAPERDOLL_DECO4));
|
||||
p->writeD( 0x00 ); //writeD(charInfoPackage.getPaperdollItemId(Inventory.PAPERDOLL_DECO5));
|
||||
p->writeD( 0x00 ); //writeD(charInfoPackage.getPaperdollItemId(Inventory.PAPERDOLL_DECO6));
|
||||
p->writeD( csb[i].iid_belt ); //writeD(charInfoPackage.getPaperdollItemId(Inventory.PAPERDOLL_BELT));
|
||||
//
|
||||
p->writeUInt( csb[i].hairStyle );
|
||||
p->writeUInt( csb[i].hairColor );
|
||||
p->writeUInt( csb[i].face );
|
||||
//LogDebug( L"Writing hs,hc,face = %u,%u,%u", csb[i].hairStyle, csb[i].hairColor, csb[i].face );
|
||||
//
|
||||
p->writeDouble( csb[i].HP_max );
|
||||
p->writeDouble( csb[i].MP_max );
|
||||
//
|
||||
p->writeUInt( csb[i].deleteSeconds );
|
||||
p->writeUInt( csb[i].classID ); // not base classid
|
||||
p->writeUInt( 0x00 ); // TODO: is last used char
|
||||
//
|
||||
p->writeC( 0x00 ); // TODO: enchant Effect
|
||||
p->writeD( 0x00 ); // TODO: aug. ID
|
||||
p->writeD( 0x00 ); // transformID is always 0 here
|
||||
}
|
||||
//
|
||||
//FILE *_f = fopen( "CharacterSelectionInfo.log", "wt" );
|
||||
//p->dumpToFile( _f );
|
||||
//fclose( _f );
|
||||
//
|
||||
return p;
|
||||
}
|
27
L2C_Server/net/serverpackets/s_KeyPacket.cpp
Normal file
27
L2C_Server/net/serverpackets/s_KeyPacket.cpp
Normal file
@ -0,0 +1,27 @@
|
||||
#include "pch.h"
|
||||
#include "Log.h"
|
||||
#include "l2c_utils.h"
|
||||
#include "net/GameClient/GameClient.h"
|
||||
#include "../ServerPackets.h"
|
||||
#include "GS.h"
|
||||
|
||||
L2GamePacket *ServerPackets::KeyPacket( GameClient *pl )
|
||||
{
|
||||
#ifdef ENABLE_OBF
|
||||
unsigned int obf_seed = rand();
|
||||
#else
|
||||
unsigned int obf_seed = 0;
|
||||
#endif
|
||||
pl->setOpcodeObfuscationSeed( obf_seed );
|
||||
//
|
||||
L2Game_KeyPacket *p = new L2Game_KeyPacket();
|
||||
p->p_protocolIsOK = pl->isProtocolOk();
|
||||
p->p_obfuscatorSeed = obf_seed;
|
||||
p->p_serverId = (unsigned char)GameServer::getInstance()->getServerId();
|
||||
// initial key is random bytes...
|
||||
L2Game_KeyPacket::createInitialHellboundKey( p->p_initialKey, p->p_initialKey );
|
||||
p->create( L2_VERSION_T23 );
|
||||
// enable crypt on client
|
||||
pl->enable_XOR_crypt( true, p->p_initialKey );
|
||||
return p;
|
||||
}
|
106
L2C_Server/net/serverpackets/s_NewCharacterSuccess.cpp
Normal file
106
L2C_Server/net/serverpackets/s_NewCharacterSuccess.cpp
Normal file
@ -0,0 +1,106 @@
|
||||
#include "pch.h"
|
||||
#include "Log.h"
|
||||
#include "l2c_utils.h"
|
||||
#include "net/GameClient/GameClient.h"
|
||||
#include "../ServerPackets.h"
|
||||
#include "GS.h"
|
||||
//
|
||||
#include "world/model/base/ClassIdTree.h"
|
||||
#include "datatables/CharTemplateTable.h"
|
||||
|
||||
/*
|
||||
Server: Len 967 [NewCharacterSuccess]
|
||||
C7 03
|
||||
0D // opcode
|
||||
0C 00 00 00 // number of templates
|
||||
|
||||
// [ for each template ]
|
||||
|
||||
00 00 00 00 // race
|
||||
00 00 00 00 // class id
|
||||
46 00 00 00 // 0x46
|
||||
28 00 00 00 // base STR
|
||||
0A 00 00 00 // 0x0A
|
||||
46 00 00 00 // 0x46
|
||||
1E 00 00 00 // base DEX
|
||||
0A 00 00 00
|
||||
46 00 00 00
|
||||
2B 00 00 00 // base CON
|
||||
0A 00 00 00
|
||||
46 00 00 00
|
||||
15 00 00 00 // base INT
|
||||
0A 00 00 00
|
||||
46 00 00 00
|
||||
0B 00 00 00 // base WIT
|
||||
0A 00 00 00
|
||||
46 00 00 00
|
||||
19 00 00 00 // base MEN
|
||||
0A 00 00 00
|
||||
*/
|
||||
|
||||
L2GamePacket *ServerPackets::NewCharacterSuccess( GameClient *pl )
|
||||
{
|
||||
pl = NULL; // reference to player is not needed here
|
||||
//
|
||||
unsigned int numTemplates = 0;
|
||||
L2Game_NewCharacterTemplate tmpl[32];
|
||||
int ic = 0;
|
||||
ClassIdTree *cidtree = ClassIdTree::getInstance();
|
||||
for( ic = 0; ic < ClassIdTree::NUM_CLASS_IDS; ic++ )
|
||||
{
|
||||
// no Inspector, though it's base class
|
||||
if( ic == ClassId::CID_INSPECTOR ) continue;
|
||||
// get ClassId if class
|
||||
const ClassId *cid = cidtree->getClassId( ic );
|
||||
if( cid )
|
||||
{
|
||||
// no parents, base classes
|
||||
if( cid->getParentId() == ClassId::CID_NONE )
|
||||
{
|
||||
// get template base stats for class
|
||||
const L2PlayerTemplate *plt = CharTemplateTable::getTemplate( ic );
|
||||
if( plt )
|
||||
{
|
||||
tmpl[numTemplates].classID = ic;
|
||||
tmpl[numTemplates].base_CON = plt->baseCON;
|
||||
tmpl[numTemplates].base_DEX = plt->baseDEX;
|
||||
tmpl[numTemplates].base_INT = plt->baseINT;
|
||||
tmpl[numTemplates].base_MEN = plt->baseMEN;
|
||||
tmpl[numTemplates].base_STR = plt->baseSTR;
|
||||
tmpl[numTemplates].base_WIT = plt->baseWIT;
|
||||
tmpl[numTemplates].race = (int)plt->race;
|
||||
numTemplates++;
|
||||
}
|
||||
}
|
||||
}
|
||||
if( numTemplates >= 32 ) break;
|
||||
}
|
||||
//
|
||||
L2GamePacket *p = new L2GamePacket();
|
||||
p->setPacketType( 0x0D ); // NewCharacterSuccess
|
||||
p->writeUInt( numTemplates );
|
||||
for( ic = 0; ic < (int)numTemplates; ic++ )
|
||||
{
|
||||
p->writeD( tmpl[ic].race );
|
||||
p->writeD( tmpl[ic].classID );
|
||||
p->writeD( 0x46 );
|
||||
p->writeD( tmpl[ic].base_STR );
|
||||
p->writeD( 0x0A );
|
||||
p->writeD( 0x46 );
|
||||
p->writeD( tmpl[ic].base_DEX );
|
||||
p->writeD( 0x0A );
|
||||
p->writeD( 0x46 );
|
||||
p->writeD( tmpl[ic].base_CON );
|
||||
p->writeD( 0x0A );
|
||||
p->writeD( 0x46 );
|
||||
p->writeD( tmpl[ic].base_INT );
|
||||
p->writeD( 0x0A );
|
||||
p->writeD( 0x46 );
|
||||
p->writeD( tmpl[ic].base_WIT );
|
||||
p->writeD( 0x0A );
|
||||
p->writeD( 0x46 );
|
||||
p->writeD( tmpl[ic].base_MEN );
|
||||
p->writeD( 0x0A );
|
||||
}
|
||||
return p;
|
||||
}
|
1
L2C_Server/pch.cpp
Normal file
1
L2C_Server/pch.cpp
Normal file
@ -0,0 +1 @@
|
||||
#include "pch.h"
|
44
L2C_Server/pch.h
Normal file
44
L2C_Server/pch.h
Normal file
@ -0,0 +1,44 @@
|
||||
#pragma once
|
||||
|
||||
#define _CRT_SECURE_NO_DEPRECATE
|
||||
#define _CRT_SECURE_NO_WARNINGS
|
||||
|
||||
#define WINVER 0x0501
|
||||
#define _WIN32_WINNT 0x0501
|
||||
#define _WIN32_IE 0x0600
|
||||
|
||||
// CRT
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <locale.h>
|
||||
#include <stdarg.h>
|
||||
#include <assert.h>
|
||||
// STL
|
||||
#include <string>
|
||||
#include <list>
|
||||
#include <map>
|
||||
#include <set>
|
||||
#include <vector>
|
||||
#include <bitset>
|
||||
|
||||
// Win32 API
|
||||
#include <winsock2.h>
|
||||
#include <windows.h>
|
||||
#include <windowsx.h>
|
||||
#include <wchar.h>
|
||||
#include <tchar.h>
|
||||
#include <process.h>
|
||||
#include <commctrl.h>
|
||||
#include <dbghelp.h>
|
||||
#include <richedit.h>
|
||||
|
||||
// extern libs
|
||||
#include <L2Packets.h>
|
||||
#include <mysqlclient/mysql.h>
|
||||
#include <openssl/ssl.h>
|
||||
#include <openssl/err.h>
|
||||
#include <openssl/rand.h>
|
||||
#include <openssl/rsa.h>
|
||||
#include <openssl/blowfish.h>
|
||||
#include <openssl/sha.h>
|
304
L2C_Server/utils/Debugging.cpp
Normal file
304
L2C_Server/utils/Debugging.cpp
Normal file
@ -0,0 +1,304 @@
|
||||
#include "pch.h"
|
||||
#include "Log.h"
|
||||
#include "Debugging.h"
|
||||
|
||||
#define MAX_SYM_NAME_LEN 256
|
||||
|
||||
Debug *Debug::s_instance = NULL;
|
||||
|
||||
Debug *Debug::getInstance()
|
||||
{
|
||||
if( !s_instance )
|
||||
s_instance = new Debug();
|
||||
return s_instance;
|
||||
}
|
||||
|
||||
void Debug::freeInstance()
|
||||
{
|
||||
if( s_instance )
|
||||
{
|
||||
delete s_instance;
|
||||
s_instance = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/*BOOL CALLBACK Debug_EnumerateModulesProc64( PCSTR ModuleName, DWORD64 BaseOfDll, PVOID UserContext )
|
||||
{
|
||||
//LogDebug( L" EnumerateModulesProc [%S] 0x%08X", ModuleName, (DWORD)BaseOfDll );
|
||||
wchar_t w[1024];
|
||||
wsprintfW( w, L" EnumerateModulesProc [%S] 0x%08X", ModuleName, (DWORD)BaseOfDll );
|
||||
MessageBox( NULL, w, L"Enum mod", 0 );
|
||||
return TRUE;
|
||||
}*/
|
||||
|
||||
Debug::Debug()
|
||||
{
|
||||
InitializeCriticalSectionAndSpinCount( &m_lock, 10 );
|
||||
EnterCriticalSection( &m_lock );
|
||||
m_initOK = false;
|
||||
m_symHandle = GetCurrentProcess();
|
||||
// set symbol handler options
|
||||
SymSetOptions( SYMOPT_UNDNAME | /*SYMOPT_DEFERRED_LOADS |*/ SYMOPT_LOAD_LINES );
|
||||
// create symbol search path
|
||||
WCHAR windir[256] = {0};
|
||||
WCHAR szSearchPath[256] = {0};
|
||||
GetWindowsDirectoryW( windir, 255 );
|
||||
wsprintfW( szSearchPath, L".;.\\Symbols;%s\\Symbols;%s\\Symbols\\dll", windir, windir );
|
||||
//MessageBoxW( NULL, szSearchPath, NULL, 0 );
|
||||
// initialize symbol handler
|
||||
BOOL ok = SymInitializeW( m_symHandle, szSearchPath/*NULL*/, TRUE );
|
||||
if( !ok )
|
||||
{
|
||||
DWORD le = GetLastError();
|
||||
wchar_t msg[512];
|
||||
wsprintfW( msg, L"SymInitialize() failed: %u", le );
|
||||
MessageBox( NULL, msg, L"Error", MB_ICONSTOP );
|
||||
}
|
||||
else
|
||||
{
|
||||
m_initOK = true;
|
||||
//
|
||||
// load PDBs for all modules possible
|
||||
//SymEnumerateModules64( m_symHandle, Debug_EnumerateModulesProc64, NULL );
|
||||
//
|
||||
}
|
||||
LeaveCriticalSection( &m_lock );
|
||||
}
|
||||
|
||||
Debug::~Debug()
|
||||
{
|
||||
EnterCriticalSection( &m_lock );
|
||||
if( m_initOK )
|
||||
{
|
||||
SymCleanup( m_symHandle );
|
||||
m_initOK = false;
|
||||
}
|
||||
LeaveCriticalSection( &m_lock );
|
||||
DeleteCriticalSection( &m_lock );
|
||||
}
|
||||
|
||||
void Debug::createStackTrace( DebugStackTrace& traceObject )
|
||||
{
|
||||
traceObject.clear();
|
||||
EnterCriticalSection( &m_lock );
|
||||
//
|
||||
// fill stackframe struct
|
||||
STACKFRAME64 stackFrame;
|
||||
memset( &stackFrame, 0, sizeof(stackFrame) );
|
||||
stackFrame.AddrPC.Mode = AddrModeFlat;
|
||||
stackFrame.AddrFrame.Mode = AddrModeFlat;
|
||||
stackFrame.AddrStack.Mode = AddrModeFlat;
|
||||
unsigned int i;
|
||||
__asm mov i, esp
|
||||
stackFrame.AddrStack.Offset = i;
|
||||
__asm mov i, ebp
|
||||
stackFrame.AddrFrame.Offset = i;
|
||||
//stackFrame.AddrPC.Offset = (DWORD64)(void *)(Debug::createStackTrace);
|
||||
stackFrame.AddrPC.Offset = (DWORD64)(void *)&stackFrame;
|
||||
BOOL bRet = TRUE;
|
||||
//
|
||||
bRet = StackWalk64( IMAGE_FILE_MACHINE_I386, this->m_symHandle, GetCurrentThread(), &stackFrame,
|
||||
NULL, NULL, SymFunctionTableAccess64, SymGetModuleBase64, NULL );
|
||||
while( bRet )
|
||||
{
|
||||
DebugOneStackFrameInfo frameInfo;
|
||||
DWORD64 addr_offset = 0; // address offset
|
||||
SYMBOL_INFO *psymInfo = (SYMBOL_INFO *)malloc( sizeof(SYMBOL_INFO) + MAX_SYM_NAME_LEN + 1 );
|
||||
memset( psymInfo, 0, sizeof(SYMBOL_INFO) + MAX_SYM_NAME_LEN + 1 );
|
||||
psymInfo->SizeOfStruct = sizeof(SYMBOL_INFO);
|
||||
psymInfo->MaxNameLen = MAX_SYM_NAME_LEN;
|
||||
if( stackFrame.AddrReturn.Offset > 0 )
|
||||
{
|
||||
bRet = SymFromAddr( this->m_symHandle, stackFrame.AddrReturn.Offset, &addr_offset, psymInfo );
|
||||
if( bRet )
|
||||
{
|
||||
// get module info
|
||||
char *moduleName = NULL;
|
||||
IMAGEHLP_MODULE64 moduleInfo;
|
||||
memset( &moduleInfo, 0, sizeof(moduleInfo) );
|
||||
moduleInfo.SizeOfStruct = sizeof(IMAGEHLP_MODULE64); // 584?
|
||||
if( !SymGetModuleInfo64( this->m_symHandle, psymInfo->Address, &moduleInfo ) )
|
||||
{
|
||||
Log_Win32Error( L"SymGetModuleInfo64() failed", GetLastError() );
|
||||
LogError( L"For addr 0x%08X name %S", (DWORD)psymInfo->Address, psymInfo->Name );
|
||||
}
|
||||
else moduleName = moduleInfo.ModuleName;
|
||||
//
|
||||
// get file name and line number
|
||||
IMAGEHLP_LINE64 lineinfo;
|
||||
memset( &lineinfo, 0, sizeof(lineinfo) );
|
||||
lineinfo.SizeOfStruct = sizeof(lineinfo);
|
||||
DWORD disp = 0;
|
||||
if( SymGetLineFromAddr64( GetCurrentProcess(), psymInfo->Address, &disp, &lineinfo ) )
|
||||
{
|
||||
frameInfo.set( psymInfo->Name, psymInfo->Address, addr_offset,
|
||||
lineinfo.FileName, lineinfo.LineNumber,
|
||||
moduleName );
|
||||
}
|
||||
else
|
||||
{
|
||||
DWORD le = GetLastError();
|
||||
if( le != ERROR_INVALID_ADDRESS ) // Attempt to access invalid address.
|
||||
{
|
||||
Log_Win32Error( L"dbghelp!SymGetLineFromAddr64() failed", le );
|
||||
LogError( L"For name: [%S]", psymInfo->Name );
|
||||
}
|
||||
frameInfo.set( psymInfo->Name, psymInfo->Address, addr_offset, NULL, 0, moduleName );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
DWORD le = GetLastError();
|
||||
Log_Win32Error( L"dbghelp!SymFromAddr() failed", le );
|
||||
LogError( L"For addr: 0x%08X", (DWORD)stackFrame.AddrReturn.Offset );
|
||||
}
|
||||
// add frame info
|
||||
traceObject.addStackFrame( frameInfo );
|
||||
}
|
||||
free( psymInfo );
|
||||
//
|
||||
bRet = StackWalk64( IMAGE_FILE_MACHINE_I386, GetCurrentProcess(), GetCurrentThread(), &stackFrame, NULL,
|
||||
NULL, SymFunctionTableAccess64, SymGetModuleBase64, NULL );
|
||||
}
|
||||
DWORD le = GetLastError();
|
||||
if( le != ERROR_INVALID_ADDRESS && le != NO_ERROR )
|
||||
Log_Win32Error( L"StackWalk64() ended", le );
|
||||
//
|
||||
// unlock
|
||||
LeaveCriticalSection( &m_lock );
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
DebugOneStackFrameInfo::~DebugOneStackFrameInfo()
|
||||
{
|
||||
if( m_name ) free( m_name );
|
||||
m_name = NULL;
|
||||
if( m_fileName ) free( m_fileName );
|
||||
m_fileName = NULL;
|
||||
if( m_moduleName ) free( m_moduleName );
|
||||
m_moduleName = NULL;
|
||||
m_addr = 0;
|
||||
m_addr_offset = 0;
|
||||
m_line = 0;
|
||||
}
|
||||
|
||||
DebugOneStackFrameInfo::DebugOneStackFrameInfo( const DebugOneStackFrameInfo& other )
|
||||
{
|
||||
m_name = NULL;
|
||||
m_fileName = NULL;
|
||||
m_addr = 0;
|
||||
m_addr_offset = 0;
|
||||
m_line = 0;
|
||||
m_moduleName = NULL;
|
||||
this->operator=( other );
|
||||
}
|
||||
|
||||
const DebugOneStackFrameInfo& DebugOneStackFrameInfo::operator=( const DebugOneStackFrameInfo& other )
|
||||
{
|
||||
if( this == &other ) return (*this);
|
||||
set( other.m_name, other.m_addr, other.m_addr_offset, other.m_fileName, other.m_line, other.m_moduleName );
|
||||
return (*this);
|
||||
}
|
||||
|
||||
// compares only address & addr offset
|
||||
bool DebugOneStackFrameInfo::operator==( const DebugOneStackFrameInfo& other )
|
||||
{
|
||||
if( this->m_addr != other.m_addr ) return false; // different addr
|
||||
if( this->m_addr_offset != other.m_addr_offset ) return false; // different offset
|
||||
return true; // addresses equal
|
||||
}
|
||||
|
||||
void DebugOneStackFrameInfo::set(
|
||||
const char *name,
|
||||
unsigned long long addr,
|
||||
unsigned long long addr_offset,
|
||||
const char *fileName,
|
||||
unsigned int line,
|
||||
const char *moduleName )
|
||||
{
|
||||
m_addr = addr;
|
||||
m_addr_offset = addr_offset;
|
||||
m_line = line;
|
||||
if( m_name ) free( m_name );
|
||||
m_name = NULL;
|
||||
if( name ) m_name = _strdup( name );
|
||||
if( m_fileName ) free( m_fileName );
|
||||
m_fileName = NULL;
|
||||
if( fileName ) m_fileName = _strdup( fileName );
|
||||
if( m_moduleName ) free( m_moduleName );
|
||||
m_moduleName = NULL;
|
||||
if( moduleName ) m_moduleName = _strdup( moduleName );
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
DebugStackTrace::DebugStackTrace()
|
||||
{
|
||||
m_list.clear();
|
||||
}
|
||||
|
||||
DebugStackTrace::~DebugStackTrace()
|
||||
{
|
||||
m_list.clear();
|
||||
}
|
||||
|
||||
void DebugStackTrace::clear()
|
||||
{
|
||||
m_list.clear();
|
||||
}
|
||||
|
||||
void DebugStackTrace::addStackFrame( DebugOneStackFrameInfo& ref )
|
||||
{
|
||||
m_list.push_back( ref );
|
||||
}
|
||||
|
||||
void DebugStackTrace::logStackTrace()
|
||||
{
|
||||
std::list<DebugOneStackFrameInfo>::const_iterator iter = m_list.begin();
|
||||
int nFrame = 0;
|
||||
while( iter != m_list.end() )
|
||||
{
|
||||
DebugOneStackFrameInfo frm = (*iter);
|
||||
if( frm.getModuleName() == NULL )
|
||||
{
|
||||
if( frm.getFileName() )
|
||||
{
|
||||
Log( L" %2d: %S() (0x%08X + %I64u) [%S line %u]", nFrame,
|
||||
frm.getName(), (unsigned int)frm.getAddr(), frm.getAddrOffset(),
|
||||
frm.getFileName(), frm.getLine() );
|
||||
}
|
||||
else
|
||||
{
|
||||
Log( L" %2d: %S() (0x%08X + %I64u)", nFrame,
|
||||
frm.getName(), (unsigned int)frm.getAddr(), frm.getAddrOffset() );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if( frm.getFileName() )
|
||||
{
|
||||
Log( L" %2d: %S!%S() (0x%08X + %I64u) [%S line %u]", nFrame,
|
||||
frm.getModuleName(), frm.getName(), (unsigned int)frm.getAddr(), frm.getAddrOffset(),
|
||||
frm.getFileName(), frm.getLine() );
|
||||
}
|
||||
else
|
||||
{
|
||||
Log( L" %2d: %S!%S() (0x%08X + %I64u)", nFrame,
|
||||
frm.getModuleName(), frm.getName(), (unsigned int)frm.getAddr(), frm.getAddrOffset() );
|
||||
}
|
||||
}
|
||||
nFrame++;
|
||||
iter++;
|
||||
}
|
||||
}
|
71
L2C_Server/utils/Debugging.h
Normal file
71
L2C_Server/utils/Debugging.h
Normal file
@ -0,0 +1,71 @@
|
||||
#pragma once
|
||||
|
||||
class DebugOneStackFrameInfo
|
||||
{
|
||||
public:
|
||||
DebugOneStackFrameInfo():
|
||||
m_name(NULL), m_fileName(NULL), m_moduleName(NULL), m_addr(0), m_addr_offset(0), m_line(0) {}
|
||||
DebugOneStackFrameInfo( const DebugOneStackFrameInfo& other );
|
||||
~DebugOneStackFrameInfo();
|
||||
const DebugOneStackFrameInfo& operator=( const DebugOneStackFrameInfo& other );
|
||||
bool operator==( const DebugOneStackFrameInfo& other );
|
||||
public:
|
||||
void set(
|
||||
const char *name,
|
||||
unsigned long long addr,
|
||||
unsigned long long addr_offset,
|
||||
const char *fileName,
|
||||
unsigned int line,
|
||||
const char *moduleName
|
||||
);
|
||||
char *getName() const { return m_name; }
|
||||
unsigned long long getAddr() const { return m_addr; }
|
||||
unsigned long long getAddrOffset() const { return m_addr_offset; }
|
||||
char *getFileName() const { return m_fileName; }
|
||||
unsigned int getLine() const { return m_line; }
|
||||
char *getModuleName() const { return m_moduleName; }
|
||||
public:
|
||||
char *m_name;
|
||||
unsigned long long m_addr;
|
||||
unsigned long long m_addr_offset;
|
||||
char *m_fileName;
|
||||
unsigned int m_line;
|
||||
char *m_moduleName;
|
||||
};
|
||||
|
||||
|
||||
|
||||
class DebugStackTrace
|
||||
{
|
||||
public:
|
||||
DebugStackTrace();
|
||||
~DebugStackTrace();
|
||||
public:
|
||||
void clear();
|
||||
void addStackFrame( DebugOneStackFrameInfo& ref );
|
||||
void logStackTrace();
|
||||
protected:
|
||||
std::list<DebugOneStackFrameInfo> m_list;
|
||||
};
|
||||
|
||||
|
||||
|
||||
class Debug
|
||||
{
|
||||
protected:
|
||||
Debug();
|
||||
~Debug();
|
||||
static Debug *s_instance;
|
||||
|
||||
public:
|
||||
static Debug *getInstance();
|
||||
static void freeInstance();
|
||||
|
||||
public:
|
||||
void createStackTrace( DebugStackTrace& traceObject );
|
||||
|
||||
protected:
|
||||
CRITICAL_SECTION m_lock;
|
||||
HANDLE m_symHandle;
|
||||
bool m_initOK;
|
||||
};
|
98
L2C_Server/utils/Exception.cpp
Normal file
98
L2C_Server/utils/Exception.cpp
Normal file
@ -0,0 +1,98 @@
|
||||
#include "pch.h"
|
||||
#include "Log.h"
|
||||
#include "Exception.h"
|
||||
|
||||
Exception::Exception()
|
||||
{
|
||||
m_what = NULL;
|
||||
m_trace = new DebugStackTrace();
|
||||
Debug::getInstance()->createStackTrace( (*m_trace) );
|
||||
}
|
||||
|
||||
Exception::Exception( const char *_Format, ... )
|
||||
{
|
||||
m_what = NULL;
|
||||
m_trace = new DebugStackTrace();
|
||||
Debug::getInstance()->createStackTrace( (*m_trace) );
|
||||
//
|
||||
va_list _ArgPtr;
|
||||
va_start( _ArgPtr, _Format );
|
||||
// _vscprintf(): Returns the number of characters in the formatted string using a pointer to a list of arguments.
|
||||
size_t what_len = _vscprintf( _Format, _ArgPtr );
|
||||
m_what = (char *)malloc( what_len + 16 );
|
||||
if( m_what )
|
||||
{
|
||||
va_start( _ArgPtr, _Format );
|
||||
vsprintf( m_what, _Format, _ArgPtr );
|
||||
}
|
||||
}
|
||||
|
||||
Exception::Exception( const Exception& other )
|
||||
{
|
||||
m_what = NULL;
|
||||
m_trace = new DebugStackTrace();
|
||||
Debug::getInstance()->createStackTrace( (*m_trace) );
|
||||
this->operator=( other );
|
||||
}
|
||||
|
||||
Exception::~Exception()
|
||||
{
|
||||
if( m_what )
|
||||
{
|
||||
free( m_what );
|
||||
m_what = NULL;
|
||||
}
|
||||
if( m_trace )
|
||||
{
|
||||
delete m_trace;
|
||||
m_trace = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
const Exception& Exception::operator=( const Exception& other )
|
||||
{
|
||||
if( this == &other ) return (*this);
|
||||
if( m_what )
|
||||
{
|
||||
free( m_what );
|
||||
m_what = NULL;
|
||||
}
|
||||
if( other.m_what ) m_what = _strdup( other.m_what );
|
||||
return (*this);
|
||||
}
|
||||
|
||||
const char *Exception::what() const
|
||||
{
|
||||
if( !m_what ) return "(no comment)";
|
||||
return m_what;
|
||||
}
|
||||
|
||||
void Exception::setWhat( const char *_Format, ... )
|
||||
{
|
||||
if( m_what )
|
||||
{
|
||||
free( m_what );
|
||||
m_what = NULL;
|
||||
}
|
||||
va_list _ArgPtr;
|
||||
va_start( _ArgPtr, _Format );
|
||||
// _vscprintf(): Returns the number of characters in the formatted string using a pointer to a list of arguments.
|
||||
size_t what_len = _vscprintf( _Format, _ArgPtr );
|
||||
m_what = (char *)malloc( what_len + 16 );
|
||||
if( m_what )
|
||||
{
|
||||
va_start( _ArgPtr, _Format );
|
||||
vsprintf( m_what, _Format, _ArgPtr );
|
||||
}
|
||||
}
|
||||
|
||||
void Exception::logStackTrace()
|
||||
{
|
||||
if( !m_trace )
|
||||
{
|
||||
Log( L"Exception: no stack trace!" );
|
||||
return;
|
||||
}
|
||||
m_trace->logStackTrace();
|
||||
}
|
||||
|
22
L2C_Server/utils/Exception.h
Normal file
22
L2C_Server/utils/Exception.h
Normal file
@ -0,0 +1,22 @@
|
||||
#pragma once
|
||||
#include "Debugging.h"
|
||||
|
||||
class Exception
|
||||
{
|
||||
public:
|
||||
Exception();
|
||||
Exception( const char *_Format, ... );
|
||||
Exception( const Exception& other );
|
||||
virtual ~Exception();
|
||||
|
||||
const Exception& operator=( const Exception& other );
|
||||
|
||||
public:
|
||||
virtual void setWhat( const char *_Format, ... );
|
||||
virtual const char *what() const;
|
||||
virtual void logStackTrace();
|
||||
|
||||
protected:
|
||||
char *m_what;
|
||||
DebugStackTrace *m_trace;
|
||||
};
|
177
L2C_Server/utils/IdFactory.cpp
Normal file
177
L2C_Server/utils/IdFactory.cpp
Normal file
@ -0,0 +1,177 @@
|
||||
#include "pch.h"
|
||||
#include "IdFactory.h"
|
||||
#include "GS.h"
|
||||
#include "Log.h"
|
||||
//#include "Exception.h"
|
||||
|
||||
IdFactory::IdFactory()
|
||||
{
|
||||
m_bitset = NULL;
|
||||
m_usedMemory = 0;
|
||||
m_lastOid = MIN_OBJECT_ID;
|
||||
}
|
||||
|
||||
IdFactory::~IdFactory()
|
||||
{
|
||||
clear();
|
||||
}
|
||||
|
||||
bool IdFactory::init()
|
||||
{
|
||||
if( m_bitset ) return false;
|
||||
m_lock.Lock();
|
||||
MEMORYSTATUSEX ms;
|
||||
ms.dwLength = sizeof(ms);
|
||||
GlobalMemoryStatusEx( &ms );
|
||||
unsigned long long mem_before = ms.ullAvailVirtual;
|
||||
unsigned long long mem_after = 0;
|
||||
m_bitset = new std::bitset<OBJECT_ID_COUNT>();
|
||||
if( m_bitset )
|
||||
{
|
||||
ms.dwLength = sizeof(ms);
|
||||
GlobalMemoryStatusEx( &ms );
|
||||
mem_after = ms.ullAvailVirtual;
|
||||
m_usedMemory = mem_before - mem_after;
|
||||
m_lastOid = MIN_OBJECT_ID;
|
||||
// restore all currently used objectId's
|
||||
fillObjectIdsFromDB();
|
||||
m_lock.Unlock();
|
||||
return true;
|
||||
}
|
||||
m_lock.Unlock();
|
||||
return false;
|
||||
}
|
||||
|
||||
void IdFactory::clear()
|
||||
{
|
||||
m_lock.Lock();
|
||||
if( m_bitset )
|
||||
{
|
||||
delete m_bitset;
|
||||
m_bitset = NULL;
|
||||
m_usedMemory = 0;
|
||||
}
|
||||
m_lastOid = MIN_OBJECT_ID;
|
||||
m_lock.Unlock();
|
||||
}
|
||||
|
||||
unsigned int IdFactory::getUsedCount()
|
||||
{
|
||||
if( !m_bitset ) return 0;
|
||||
unsigned int r = 0;
|
||||
m_lock.Lock();
|
||||
r = m_bitset->count();
|
||||
m_lock.Unlock();
|
||||
return r;
|
||||
}
|
||||
|
||||
unsigned int IdFactory::getNextObjectId() throw(NoFreeObjectIdException)
|
||||
{
|
||||
if( !m_bitset ) return 0;
|
||||
m_lock.Lock();
|
||||
unsigned int o = findUnusedOid();
|
||||
if( o == 0 )
|
||||
{
|
||||
m_lock.Unlock();
|
||||
throw NoFreeObjectIdException();
|
||||
}
|
||||
m_bitset->set( o-MIN_OBJECT_ID );
|
||||
m_lastOid = o+1; // next time try to give next objectId
|
||||
if( m_lastOid > MAX_OBJECT_ID ) m_lastOid = MIN_OBJECT_ID;
|
||||
m_lock.Unlock();
|
||||
return o;
|
||||
}
|
||||
|
||||
bool IdFactory::releaseObjectId( unsigned int o )
|
||||
{
|
||||
if( !m_bitset ) return false;
|
||||
m_lock.Lock();
|
||||
bool ret = false;
|
||||
if( m_bitset->test( o ) )
|
||||
{
|
||||
ret = true;
|
||||
m_bitset->reset( o );
|
||||
}
|
||||
m_lock.Unlock();
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool IdFactory::isUsedObjectId( unsigned int o )
|
||||
{
|
||||
if( !m_bitset ) return false;
|
||||
bool ret = false;
|
||||
m_lock.Lock();
|
||||
ret = m_bitset->test( o );
|
||||
m_lock.Unlock();
|
||||
return ret;
|
||||
}
|
||||
|
||||
unsigned long long int IdFactory::getUsedMemoryBytes() const
|
||||
{
|
||||
return m_usedMemory;
|
||||
}
|
||||
|
||||
unsigned int IdFactory::findUnusedOid() const
|
||||
{
|
||||
if( !m_bitset ) return 0;
|
||||
// lock should be locked!
|
||||
unsigned int o = m_lastOid;
|
||||
unsigned int start_o = o;
|
||||
while( o >= MIN_OBJECT_ID )
|
||||
{
|
||||
#ifdef _DEBUG_IDFACTORY_INT
|
||||
try
|
||||
{
|
||||
#endif /*_DEBUG_IDFACTORY_INT*/
|
||||
if( !m_bitset->test( o-MIN_OBJECT_ID ) ) return o;
|
||||
#ifdef _DEBUG_IDFACTORY_INT
|
||||
}
|
||||
catch( std::exception& e )
|
||||
{
|
||||
printf( "std::exception: %s\n", e.what() );
|
||||
printf( "o = %u (m_lastOid = %u, start_o = %u)\n", o, m_lastOid, start_o );
|
||||
}
|
||||
#endif /*_DEBUG_IDFACTORY_INT*/
|
||||
o++;
|
||||
if( o > MAX_OBJECT_ID ) o = MIN_OBJECT_ID;
|
||||
if( o == start_o ) break; // search reached start point
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void IdFactory::fillObjectIdsFromDB()
|
||||
{
|
||||
MysqlConnection *con = GameServer::getInstance()->getDBConnection();
|
||||
MysqlQuery q;
|
||||
//"SELECT charId FROM characters"
|
||||
//"SELECT object_id FROM items"
|
||||
//"SELECT object_id FROM itemsonground
|
||||
//"SELECT clan_id FROM clan_data"
|
||||
q.create( L"SELECT charId FROM characters UNION SELECT object_id FROM items "
|
||||
L"UNION SELECT object_id FROM itemsonground UNION SELECT clan_id FROM clan_data" );
|
||||
if( con->executeQuery( q ) )
|
||||
{
|
||||
while( q.getNextRow() )
|
||||
{
|
||||
unsigned int oid = q.getFieldUInt( 0 );
|
||||
fillObjectIdsFromDB_insertOne( oid );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
LogError( L"IdFactory::fillObjectIdsFromDB(): MySQL ERROR: %s\n", con->getErrorStr() );
|
||||
}
|
||||
|
||||
GameServer::getInstance()->releaseDBConnection( con );
|
||||
}
|
||||
|
||||
void IdFactory::fillObjectIdsFromDB_insertOne( unsigned int oid )
|
||||
{
|
||||
if( oid >= IdFactory::MIN_OBJECT_ID && oid <= IdFactory::MAX_OBJECT_ID )
|
||||
{
|
||||
m_bitset->set( oid - IdFactory::MIN_OBJECT_ID );
|
||||
}
|
||||
else
|
||||
LogError( L"IdFactory: RESTORE: oid [%u] is not between MIN_OBJECT_ID[%u] and MAX_OBJECT_ID[%u]!\n",
|
||||
oid, IdFactory::MIN_OBJECT_ID, IdFactory::MAX_OBJECT_ID );
|
||||
}
|
44
L2C_Server/utils/IdFactory.h
Normal file
44
L2C_Server/utils/IdFactory.h
Normal file
@ -0,0 +1,44 @@
|
||||
#pragma once
|
||||
#include "l2c_utils.h"
|
||||
|
||||
// warning C4290: C++ exception specification ignored except to indicate a function is not __declspec(nothrow)
|
||||
#pragma warning (disable: 4290)
|
||||
|
||||
class Exception;
|
||||
|
||||
class IdFactory
|
||||
{
|
||||
public:
|
||||
static const unsigned int MIN_OBJECT_ID = 0x10000000;
|
||||
static const unsigned int MAX_OBJECT_ID = 0x7FFFFFFF;
|
||||
static const unsigned int OBJECT_ID_COUNT = MAX_OBJECT_ID - MIN_OBJECT_ID + 1;
|
||||
|
||||
public:
|
||||
class NoFreeObjectIdException: public std::exception {};
|
||||
|
||||
public:
|
||||
IdFactory();
|
||||
~IdFactory();
|
||||
bool init();
|
||||
void clear();
|
||||
|
||||
public:
|
||||
unsigned int getUsedCount();
|
||||
unsigned int getNextObjectId() throw(NoFreeObjectIdException);
|
||||
bool releaseObjectId( unsigned int o );
|
||||
bool isUsedObjectId( unsigned int o );
|
||||
|
||||
public:
|
||||
unsigned long long int getUsedMemoryBytes() const;
|
||||
|
||||
protected:
|
||||
unsigned int findUnusedOid() const;
|
||||
void fillObjectIdsFromDB();
|
||||
void fillObjectIdsFromDB_insertOne( unsigned int oid );
|
||||
|
||||
protected:
|
||||
std::bitset<OBJECT_ID_COUNT> *m_bitset;
|
||||
unsigned long long int m_usedMemory;
|
||||
CriticalSection m_lock;
|
||||
unsigned int m_lastOid;
|
||||
};
|
16
L2C_Server/utils/Utils.cpp
Normal file
16
L2C_Server/utils/Utils.cpp
Normal file
@ -0,0 +1,16 @@
|
||||
#include "pch.h"
|
||||
#include "Utils.h"
|
||||
|
||||
bool Utils_isValidCharName( const wchar_t *name )
|
||||
{
|
||||
if( !name ) return false;
|
||||
size_t nl = wcslen( name );
|
||||
if( nl < 3 ) return false;
|
||||
size_t i = 0;
|
||||
for( i=0; i<nl; i++ )
|
||||
{
|
||||
wchar_t c = name[i];
|
||||
if( !iswalnum( c ) ) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
3
L2C_Server/utils/Utils.h
Normal file
3
L2C_Server/utils/Utils.h
Normal file
@ -0,0 +1,3 @@
|
||||
#pragma once
|
||||
|
||||
bool Utils_isValidCharName( const wchar_t *name );
|
59
L2C_Server/world/model/GameObject.cpp
Normal file
59
L2C_Server/world/model/GameObject.cpp
Normal file
@ -0,0 +1,59 @@
|
||||
#include "pch.h"
|
||||
#include "Log.h"
|
||||
#include "GameObject.h"
|
||||
|
||||
GameObject::GameObject( unsigned int objectId )
|
||||
{
|
||||
m_objectId = objectId;
|
||||
m_x = m_y = m_z = m_reflection = 0;
|
||||
m_toString_buffer = NULL;
|
||||
}
|
||||
|
||||
GameObject::GameObject( int x, int y, int z, int reflection, unsigned int objectId )
|
||||
{
|
||||
m_x = x; m_y = y; m_z = z;
|
||||
m_reflection = reflection;
|
||||
m_objectId = objectId;
|
||||
m_toString_buffer = NULL;
|
||||
}
|
||||
|
||||
GameObject::~GameObject()
|
||||
{
|
||||
if( m_toString_buffer )
|
||||
{
|
||||
free( m_toString_buffer );
|
||||
m_toString_buffer = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
void GameObject::LockObject()
|
||||
{
|
||||
m_lock.Lock();
|
||||
}
|
||||
|
||||
void GameObject::LockRelease()
|
||||
{
|
||||
m_lock.Unlock();
|
||||
}
|
||||
|
||||
void GameObject::setXYZ( int x, int y, int z )
|
||||
{
|
||||
m_x = x;
|
||||
m_y = y;
|
||||
m_z = z;
|
||||
}
|
||||
|
||||
void GameObject::setReflection( int rId )
|
||||
{
|
||||
m_reflection = rId;
|
||||
}
|
||||
|
||||
wchar_t *GameObject::toString() const
|
||||
{
|
||||
GameObject *non_const_this = const_cast<GameObject *>(this);
|
||||
if( non_const_this->m_toString_buffer == NULL )
|
||||
non_const_this->m_toString_buffer = (wchar_t *)malloc( 512 );
|
||||
if( non_const_this->m_toString_buffer )
|
||||
swprintf( non_const_this->m_toString_buffer, 255, L"ObjectID[%u] (%d,%d,%d,%d)", m_objectId, m_x, m_y, m_z, m_reflection );
|
||||
return non_const_this->m_toString_buffer;
|
||||
}
|
35
L2C_Server/world/model/GameObject.h
Normal file
35
L2C_Server/world/model/GameObject.h
Normal file
@ -0,0 +1,35 @@
|
||||
#pragma once
|
||||
#include "l2c_utils.h"
|
||||
|
||||
class GameObject
|
||||
{
|
||||
public:
|
||||
GameObject( unsigned int objectId );
|
||||
GameObject( int x, int y, int z, int reflection, unsigned int objectId );
|
||||
virtual ~GameObject();
|
||||
|
||||
public:
|
||||
unsigned int getObjectId() const { return m_objectId; }
|
||||
int getX() const { return m_x; }
|
||||
int getY() const { return m_y; }
|
||||
int getZ() const { return m_z; }
|
||||
int getReflection() const { return m_reflection; }
|
||||
|
||||
public:
|
||||
void setXYZ( int x, int y, int z );
|
||||
void setReflection( int rId );
|
||||
|
||||
public:
|
||||
virtual void LockObject();
|
||||
virtual void LockRelease();
|
||||
virtual wchar_t *toString() const;
|
||||
|
||||
protected:
|
||||
unsigned int m_objectId;
|
||||
int m_x;
|
||||
int m_y;
|
||||
int m_z;
|
||||
int m_reflection;
|
||||
wchar_t *m_toString_buffer;
|
||||
CriticalSection m_lock;
|
||||
};
|
43
L2C_Server/world/model/base/ClassId.cpp
Normal file
43
L2C_Server/world/model/base/ClassId.cpp
Normal file
@ -0,0 +1,43 @@
|
||||
#include "pch.h"
|
||||
#include "ClassId.h"
|
||||
#include "ClassIdTree.h"
|
||||
|
||||
ClassId::ClassId( int id, const wchar_t *name, bool isMage, bool isSummoner, Race race, int parentId )
|
||||
{
|
||||
m_id = id; m_isMage = isMage; m_isSummoner = isSummoner;
|
||||
m_race = race; m_parentId = parentId;
|
||||
m_name[0] = 0;
|
||||
if( name )
|
||||
{
|
||||
wcsncpy( m_name, name, sizeof(m_name)/sizeof(m_name[0]) );
|
||||
m_name[sizeof(m_name)/sizeof(m_name[0])-1] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
int ClassId::getId() const { return m_id; }
|
||||
const wchar_t *ClassId::getName() const { return m_name; }
|
||||
bool ClassId::isMage() const { return m_isMage; }
|
||||
bool ClassId::isSummoner() const { return m_isSummoner; }
|
||||
Race ClassId::getRace() const { return m_race; }
|
||||
int ClassId::getParentId() const { return m_parentId; }
|
||||
|
||||
int ClassId::level() const
|
||||
{
|
||||
if( m_id != 0x87 && m_parentId == -1 ) return 0; // every non-Inspector without a parent is 0th prof
|
||||
if( m_id == 0x87 ) return 2; // Inspector, has no parent, but is 2nd class
|
||||
if( m_id == 0x88 ) return 3; // Judicator
|
||||
return 1 + ClassIdTree::getInstance()->getParentClassId( m_id )->level();
|
||||
}
|
||||
|
||||
bool ClassId::isChildOf( int classId ) const
|
||||
{
|
||||
if( m_parentId == -1 ) return false; // no parents...
|
||||
if( m_parentId == classId ) return true; // direct child of
|
||||
// maybe is parent's child?
|
||||
return ClassIdTree::getInstance()->getClassId( m_parentId )->isChildOf( classId );
|
||||
}
|
||||
|
||||
bool ClassId::isEqualOrChildOf( int classId ) const
|
||||
{
|
||||
return (m_id == classId) || isChildOf( classId );
|
||||
}
|
112
L2C_Server/world/model/base/ClassId.h
Normal file
112
L2C_Server/world/model/base/ClassId.h
Normal file
@ -0,0 +1,112 @@
|
||||
#pragma once
|
||||
#include "Race.h"
|
||||
|
||||
class ClassId
|
||||
{
|
||||
public:
|
||||
static const int CID_NONE = -1;
|
||||
static const int CID_HUMAN_FIGHTER = 0x00;
|
||||
static const int CID_WARRIOR = 0x01;
|
||||
static const int CID_GLADIATOR = 0x02;
|
||||
static const int CID_WARLORD = 0x03;
|
||||
static const int CID_KNIGHT = 0x04;
|
||||
static const int CID_PALLADIN = 0x05;
|
||||
static const int CID_DARK_AVENGER = 0x06;
|
||||
static const int CID_ROGUE = 0x07;
|
||||
static const int CID_TREASURE_HUNTER = 0x08;
|
||||
static const int CID_HAWKEYE = 0x09;
|
||||
|
||||
static const int CID_HUMAN_MAGE = 0x0A;
|
||||
static const int CID_HUMAN_WIZARD = 0x0B;
|
||||
static const int CID_SORCEROR = 0x0C;
|
||||
static const int CID_NECROMANCER = 0x0D;
|
||||
static const int CID_WARLOCK = 0x0E;
|
||||
static const int CID_CLERIC = 0x0F;
|
||||
static const int CID_BISHOP = 0x10;
|
||||
static const int CID_PROPHET = 0x11;
|
||||
|
||||
static const int CID_ELVEN_FIGHTER = 0x12;
|
||||
static const int CID_ELVEN_KNIGHT = 0x13;
|
||||
static const int CID_TEMPLE_KNIGHT = 0x14;
|
||||
static const int CID_SWORDSINGER = 0x15;
|
||||
static const int CID_ELVEN_SCOUT = 0x16;
|
||||
static const int CID_PLAINSWALKER = 0x17;
|
||||
static const int CID_SILVER_RANGER = 0x18;
|
||||
|
||||
static const int CID_ELVEN_MAGE = 0x19;
|
||||
static const int CID_ELVEN_WIZARD = 0x1A;
|
||||
static const int CID_SPELLSINGER = 0x1B;
|
||||
static const int CID_ELEMENTAL_SUMMONER = 0x1C;
|
||||
static const int CID_ELVEN_ORACLE = 0x1D;
|
||||
static const int CID_ELVEN_ELDER = 0x1E;
|
||||
|
||||
static const int CID_DARK_FIGHTER = 0x1F;
|
||||
static const int CID_PALUS_KNIGHT = 0x20;
|
||||
static const int CID_SHILLEN_KNIGHT = 0x21;
|
||||
static const int CID_BLADEDANCER = 0x22;
|
||||
static const int CID_ASSASIN = 0x23;
|
||||
static const int CID_ABYSS_WALKER = 0x24;
|
||||
static const int CID_PHANTOM_RANGER = 0x25;
|
||||
|
||||
static const int CID_DARK_MAGE = 0x26;
|
||||
static const int CID_DARK_WIZARD = 0x27;
|
||||
static const int CID_SPELLHOWLER = 0x28;
|
||||
static const int CID_PHANTOM_SUMMONER = 0x29;
|
||||
static const int CID_SHILLEN_ORACLE = 0x2A;
|
||||
static const int CID_SHILLEN_ELDER = 0x2B;
|
||||
|
||||
static const int CID_ORC_FIGHTER = 0x2C;
|
||||
static const int CID_ORC_RIDER = 0x2D;
|
||||
static const int CID_DESTROYER = 0x2E;
|
||||
static const int CID_ORC_MONK = 0x2F;
|
||||
static const int CID_TYRANT = 0x30;
|
||||
|
||||
static const int CID_ORC_MAGE = 0x31;
|
||||
static const int CID_ORC_SHAMAN = 0x32;
|
||||
static const int CID_OVERLORD = 0x33;
|
||||
static const int CID_WARCRYER = 0x34;
|
||||
|
||||
static const int CID_DWARVEN_FIGHTER = 0x35;
|
||||
static const int CID_SCAVENGER = 0x36;
|
||||
static const int CID_BOUNTY_HUNTER = 0x37;
|
||||
static const int CID_ARTISAN = 0x38;
|
||||
static const int CID_WARSMITH = 0x39;
|
||||
|
||||
static const int CID_DUELIST = 0x58;
|
||||
|
||||
static const int CID_KAMAEL_MALE_SOLDIER = 0x7B;
|
||||
static const int CID_KAMAEL_FEMALE_SOLDIER = 0x7C;
|
||||
static const int CID_TROOPER = 0x7D;
|
||||
static const int CID_WARDER = 0x7E;
|
||||
static const int CID_BERSERKER = 0x7F;
|
||||
static const int CID_MALE_SOULBREAKER = 0x80;
|
||||
static const int CID_FEMALE_SOULBREAKER = 0x81;
|
||||
static const int CID_ARBALESTER = 0x82;
|
||||
static const int CID_DOOMBRINGER = 0x83;
|
||||
static const int CID_MALE_SOULHOUND = 0x84;
|
||||
static const int CID_FEMALE_SOULHOUND = 0x85;
|
||||
static const int CID_TRICKSTER = 0x86;
|
||||
static const int CID_INSPECTOR = 0x87;
|
||||
static const int CID_JUDICATOR = 0x88;
|
||||
|
||||
public:
|
||||
ClassId( int id, const wchar_t *name, bool isMage, bool isSummoner, Race race, int parentId );
|
||||
int getId() const;
|
||||
const wchar_t *getName() const;
|
||||
bool isMage() const;
|
||||
bool isSummoner() const;
|
||||
Race getRace() const;
|
||||
int getParentId() const;
|
||||
public:
|
||||
// 0 - 0 profession, 1 - 1st profession, 2 - 2nd prof, 3 - 3rd prof
|
||||
int level() const;
|
||||
bool isChildOf( int classId ) const;
|
||||
bool isEqualOrChildOf( int classId ) const;
|
||||
protected:
|
||||
int m_id;
|
||||
wchar_t m_name[32];
|
||||
bool m_isMage;
|
||||
bool m_isSummoner;
|
||||
Race m_race;
|
||||
int m_parentId; // -1 - no parent class
|
||||
};
|
196
L2C_Server/world/model/base/ClassIdTree.cpp
Normal file
196
L2C_Server/world/model/base/ClassIdTree.cpp
Normal file
@ -0,0 +1,196 @@
|
||||
#include "pch.h"
|
||||
#include "ClassIdTree.h"
|
||||
|
||||
// mem for static vars
|
||||
ClassIdTree *ClassIdTree::_instance = NULL;
|
||||
int ClassIdTree::_refCount = 0;
|
||||
|
||||
ClassIdTree *ClassIdTree::getInstance()
|
||||
{
|
||||
if( !_instance )
|
||||
{
|
||||
_instance = new ClassIdTree();
|
||||
_refCount++;
|
||||
}
|
||||
return _instance;
|
||||
}
|
||||
|
||||
void ClassIdTree::freeInstance()
|
||||
{
|
||||
if( _instance )
|
||||
{
|
||||
_refCount--;
|
||||
if( _refCount == 0 )
|
||||
{
|
||||
delete _instance;
|
||||
_instance = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ClassIdTree::ClassIdTree()
|
||||
{
|
||||
int i;
|
||||
for( i=0; i<NUM_CLASS_IDS; i++ ) m_class[i] = NULL;
|
||||
|
||||
// add info about all classes
|
||||
addClassId( 0x00, L"Fighter", false, false, RACE_Human, ClassId::CID_NONE );
|
||||
addClassId( 0x01, L"Warrior", false, false, RACE_Human, ClassId::CID_HUMAN_FIGHTER );
|
||||
addClassId( 0x02, L"Gladiator", false, false, RACE_Human, ClassId::CID_WARRIOR );
|
||||
addClassId( 0x03, L"Warlord", false, false, RACE_Human, ClassId::CID_WARRIOR );
|
||||
addClassId( 0x04, L"Knight", false, false, RACE_Human, ClassId::CID_HUMAN_FIGHTER );
|
||||
addClassId( 0x05, L"Palladin", false, false, RACE_Human, ClassId::CID_KNIGHT );
|
||||
addClassId( 0x06, L"Dark Avenger", false, false, RACE_Human, ClassId::CID_KNIGHT );
|
||||
addClassId( 0x07, L"Rogue", false, false, RACE_Human, ClassId::CID_HUMAN_FIGHTER );
|
||||
addClassId( 0x08, L"Treasure Hunter", false, false, RACE_Human, ClassId::CID_ROGUE );
|
||||
addClassId( 0x09, L"Hawkeye", false, false, RACE_Human, ClassId::CID_ROGUE );
|
||||
|
||||
addClassId( 0x0A, L"Mage", true, false, RACE_Human, ClassId::CID_NONE );
|
||||
addClassId( 0x0B, L"Wizard", true, false, RACE_Human, ClassId::CID_HUMAN_MAGE );
|
||||
addClassId( 0x0C, L"Sorceror", true, false, RACE_Human, ClassId::CID_HUMAN_WIZARD );
|
||||
addClassId( 0x0D, L"Necromancer", true, false, RACE_Human, ClassId::CID_HUMAN_WIZARD );
|
||||
addClassId( 0x0E, L"Warlock", true, true, RACE_Human, ClassId::CID_HUMAN_WIZARD );
|
||||
addClassId( 0x0F, L"Cleric", true, false, RACE_Human, ClassId::CID_HUMAN_MAGE );
|
||||
addClassId( 0x10, L"Bishop", true, false, RACE_Human, ClassId::CID_CLERIC );
|
||||
addClassId( 0x11, L"Prophet", true, false, RACE_Human, ClassId::CID_CLERIC );
|
||||
|
||||
addClassId( 0x12, L"Elven fighter", false, false, RACE_Elf, ClassId::CID_NONE );
|
||||
addClassId( 0x13, L"Elven Knight", false, false, RACE_Elf, ClassId::CID_ELVEN_FIGHTER );
|
||||
addClassId( 0x14, L"Temple Knight", false, false, RACE_Elf, ClassId::CID_ELVEN_KNIGHT );
|
||||
addClassId( 0x15, L"Swordsinger", false, false, RACE_Elf, ClassId::CID_ELVEN_KNIGHT );
|
||||
addClassId( 0x16, L"Elven Scout", false, false, RACE_Elf, ClassId::CID_ELVEN_FIGHTER );
|
||||
addClassId( 0x17, L"Plains Walker", false, false, RACE_Elf, ClassId::CID_ELVEN_SCOUT );
|
||||
addClassId( 0x18, L"Silver Ranger", false, false, RACE_Elf, ClassId::CID_ELVEN_SCOUT );
|
||||
|
||||
addClassId( 0x19, L"Elven Mage", true, false, RACE_Elf, ClassId::CID_NONE );
|
||||
addClassId( 0x1A, L"Elven Wizard", true, false, RACE_Elf, ClassId::CID_ELVEN_MAGE );
|
||||
addClassId( 0x1B, L"Spellsinger", true, false, RACE_Elf, ClassId::CID_ELVEN_WIZARD );
|
||||
addClassId( 0x1C, L"Elemenal Summoner", true, true, RACE_Elf, ClassId::CID_ELVEN_WIZARD );
|
||||
addClassId( 0x1D, L"Elven Oracle", true, false, RACE_Elf, ClassId::CID_ELVEN_MAGE );
|
||||
addClassId( 0x1E, L"Elven Elder", true, false, RACE_Elf, ClassId::CID_ELVEN_ORACLE );
|
||||
|
||||
addClassId( 0x1F, L"Dark Fighter", false, false, RACE_DarkElf, ClassId::CID_NONE );
|
||||
addClassId( 0x20, L"Palus Knight", false, false, RACE_DarkElf, ClassId::CID_DARK_FIGHTER );
|
||||
addClassId( 0x21, L"Shillen Knight", false, false, RACE_DarkElf, ClassId::CID_PALUS_KNIGHT );
|
||||
addClassId( 0x22, L"Bladedancer", false, false, RACE_DarkElf, ClassId::CID_PALUS_KNIGHT );
|
||||
addClassId( 0x23, L"Assasin", false, false, RACE_DarkElf, ClassId::CID_DARK_FIGHTER );
|
||||
addClassId( 0x24, L"Abyss Walker", false, false, RACE_DarkElf, ClassId::CID_ASSASIN );
|
||||
addClassId( 0x25, L"Phantom Ranger", false, false, RACE_DarkElf, ClassId::CID_ASSASIN );
|
||||
|
||||
addClassId( 0x26, L"Dark Mage", true, false, RACE_DarkElf, ClassId::CID_NONE );
|
||||
addClassId( 0x27, L"Dark Wizard", true, false, RACE_DarkElf, ClassId::CID_DARK_MAGE );
|
||||
addClassId( 0x28, L"Spellhowler", true, false, RACE_DarkElf, ClassId::CID_DARK_WIZARD );
|
||||
addClassId( 0x29, L"Phantom Summoner", true, true, RACE_DarkElf, ClassId::CID_DARK_WIZARD );
|
||||
addClassId( 0x2A, L"Shillen Oracle", true, false, RACE_DarkElf, ClassId::CID_DARK_MAGE );
|
||||
addClassId( 0x2B, L"Shillen ELder", true, false, RACE_DarkElf, ClassId::CID_SHILLEN_ORACLE );
|
||||
|
||||
addClassId( 0x2C, L"Orc Fighter", false, false, RACE_Orc, ClassId::CID_NONE );
|
||||
addClassId( 0x2D, L"Orc Rider", false, false, RACE_Orc, ClassId::CID_ORC_FIGHTER );
|
||||
addClassId( 0x2E, L"Destroyer", false, false, RACE_Orc, ClassId::CID_ORC_RIDER );
|
||||
addClassId( 0x2F, L"Orc Monk", false, false, RACE_Orc, ClassId::CID_ORC_FIGHTER );
|
||||
addClassId( 0x30, L"Tyrant", false, false, RACE_Orc, ClassId::CID_ORC_MONK );
|
||||
|
||||
addClassId( 0x31, L"Orc Mage", true, false, RACE_Orc, ClassId::CID_NONE );
|
||||
addClassId( 0x32, L"Orc Shaman", true, false, RACE_Orc, ClassId::CID_ORC_MAGE );
|
||||
addClassId( 0x33, L"Overlord", true, false, RACE_Orc, ClassId::CID_ORC_SHAMAN );
|
||||
addClassId( 0x34, L"Warcryer", true, false, RACE_Orc, ClassId::CID_ORC_SHAMAN );
|
||||
|
||||
addClassId( 0x35, L"Dwarven Fighter", false, false, RACE_Dwarf, ClassId::CID_NONE );
|
||||
addClassId( 0x36, L"Scavenger", false, false, RACE_Dwarf, ClassId::CID_DWARVEN_FIGHTER );
|
||||
addClassId( 0x37, L"Bounty Hunter", false, false, RACE_Dwarf, ClassId::CID_SCAVENGER );
|
||||
addClassId( 0x38, L"Artisan", false, false, RACE_Dwarf, ClassId::CID_DWARVEN_FIGHTER );
|
||||
addClassId( 0x39, L"Warsmith", false, false, RACE_Dwarf, ClassId::CID_ARTISAN );
|
||||
|
||||
// 3rd prof
|
||||
addClassId( 0x58, L"Duelist", false, false, RACE_Human, ClassId::CID_GLADIATOR );
|
||||
addClassId( 0x59, L"Dreadnought", false, false, RACE_Human, ClassId::CID_WARLORD );
|
||||
addClassId( 0x5A, L"Phoenix Knight", false, false, RACE_Human, ClassId::CID_PALLADIN );
|
||||
addClassId( 0x5B, L"Hell Knight", false, false, RACE_Human, ClassId::CID_DARK_AVENGER );
|
||||
addClassId( 0x5C, L"Sagittarius", false, false, RACE_Human, ClassId::CID_HAWKEYE );
|
||||
addClassId( 0x5D, L"Adventurer", false, false, RACE_Human, ClassId::CID_TREASURE_HUNTER );
|
||||
|
||||
addClassId( 0x5E, L"Archmage", true, false, RACE_Human, ClassId::CID_SORCEROR );
|
||||
addClassId( 0x5F, L"Soultaker", true, false, RACE_Human, ClassId::CID_NECROMANCER );
|
||||
addClassId( 0x60, L"Arcana Lord", true, true, RACE_Human, ClassId::CID_WARLOCK );
|
||||
addClassId( 0x61, L"Cardinal", true, false, RACE_Human, ClassId::CID_BISHOP );
|
||||
addClassId( 0x62, L"Hierophant", true, false, RACE_Human, ClassId::CID_PROPHET );
|
||||
|
||||
addClassId( 0x63, L"Eva's Templar", false, false, RACE_Elf, ClassId::CID_TEMPLE_KNIGHT );
|
||||
addClassId( 0x64, L"Sword Muse", false, false, RACE_Elf, ClassId::CID_SWORDSINGER );
|
||||
addClassId( 0x65, L"Wind Rider", false, false, RACE_Elf, ClassId::CID_PLAINSWALKER );
|
||||
addClassId( 0x66, L"Moonlight Sentinel", false, false, RACE_Elf, ClassId::CID_SILVER_RANGER );
|
||||
addClassId( 0x67, L"Mustic Muse", true, false, RACE_Elf, ClassId::CID_SPELLSINGER );
|
||||
addClassId( 0x68, L"Elemental Master", true, true, RACE_Elf, ClassId::CID_ELEMENTAL_SUMMONER );
|
||||
addClassId( 0x69, L"Eva's Saint", true, false, RACE_Elf, ClassId::CID_ELVEN_ELDER );
|
||||
|
||||
addClassId( 0x6A, L"Shillen Templar", false, false, RACE_DarkElf, ClassId::CID_SHILLEN_KNIGHT );
|
||||
addClassId( 0x6B, L"Spectral Dancer", false, false, RACE_DarkElf, ClassId::CID_BLADEDANCER );
|
||||
addClassId( 0x6C, L"Ghost Hunter", false, false, RACE_DarkElf, ClassId::CID_ABYSS_WALKER );
|
||||
addClassId( 0x6D, L"Ghost Sentinel", false, false, RACE_DarkElf, ClassId::CID_PHANTOM_RANGER );
|
||||
|
||||
addClassId( 0x6E, L"Storm Screamer", true, false, RACE_DarkElf, ClassId::CID_SPELLHOWLER );
|
||||
addClassId( 0x6F, L"Spectral Master", true, true, RACE_DarkElf, ClassId::CID_PHANTOM_SUMMONER );
|
||||
addClassId( 0x70, L"Shillen Saint", true, false, RACE_DarkElf, ClassId::CID_SHILLEN_ELDER );
|
||||
|
||||
addClassId( 0x71, L"Titan", false, false, RACE_Orc, ClassId::CID_DESTROYER );
|
||||
addClassId( 0x72, L"Grand Khavatari", false, false, RACE_Orc, ClassId::CID_TYRANT );
|
||||
addClassId( 0x73, L"Dominator", true, false, RACE_Orc, ClassId::CID_OVERLORD );
|
||||
addClassId( 0x74, L"Doomcryer", true, false, RACE_Orc, ClassId::CID_WARCRYER );
|
||||
|
||||
addClassId( 0x75, L"Fortune Seeker", false, false, RACE_Dwarf, ClassId::CID_BOUNTY_HUNTER );
|
||||
addClassId( 0x76, L"Maestro", false, false, RACE_Dwarf, ClassId::CID_WARSMITH );
|
||||
|
||||
addClassId( 0x7B, L"Kamael Male Soldier", false, false, RACE_Kamael, ClassId::CID_NONE );
|
||||
addClassId( 0x7C, L"Kamael Female Soldier", false, false, RACE_Kamael, ClassId::CID_NONE );
|
||||
addClassId( 0x7D, L"Trooper", false, false, RACE_Kamael, ClassId::CID_KAMAEL_MALE_SOLDIER );
|
||||
addClassId( 0x7E, L"Warder", false, false, RACE_Kamael, ClassId::CID_KAMAEL_FEMALE_SOLDIER );
|
||||
|
||||
addClassId( 0x7F, L"Berserker", false, false, RACE_Kamael, ClassId::CID_TROOPER );
|
||||
addClassId( 0x80, L"Male Soulbreaker", false, false, RACE_Kamael, ClassId::CID_TROOPER );
|
||||
addClassId( 0x81, L"Female Soulbreaker", false, false, RACE_Kamael, ClassId::CID_WARDER );
|
||||
addClassId( 0x82, L"Arbalester", false, false, RACE_Kamael, ClassId::CID_WARDER );
|
||||
|
||||
addClassId( 0x83, L"Doombringer", false, false, RACE_Kamael, ClassId::CID_BERSERKER );
|
||||
addClassId( 0x84, L"Male Soulhound", false, false, RACE_Kamael, ClassId::CID_MALE_SOULBREAKER );
|
||||
addClassId( 0x85, L"Female Soulhound", false, false, RACE_Kamael, ClassId::CID_FEMALE_SOULBREAKER );
|
||||
addClassId( 0x86, L"Trickster", false, false, RACE_Kamael, ClassId::CID_ARBALESTER );
|
||||
addClassId( 0x87, L"Inspector", false, false, RACE_Kamael, ClassId::CID_NONE ); // Hidden subclass, no first class which it comes from
|
||||
addClassId( 0x88, L"Judicator", false, false, RACE_Kamael, ClassId::CID_INSPECTOR );
|
||||
}
|
||||
|
||||
ClassIdTree::~ClassIdTree()
|
||||
{
|
||||
int i;
|
||||
for( i=0; i<NUM_CLASS_IDS; i++ )
|
||||
{
|
||||
if( m_class[i] )
|
||||
{
|
||||
delete m_class[i];
|
||||
m_class[i] = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ClassIdTree::addClassId( int id, const wchar_t *name, bool isMage, bool isSummoner, Race race, int parentId )
|
||||
{
|
||||
if( (id>=0) && (id<NUM_CLASS_IDS) )
|
||||
{
|
||||
assert( m_class[id] == NULL );
|
||||
if( m_class[id] == NULL )
|
||||
m_class[id] = new ClassId( id, name, isMage, isSummoner, race, parentId );
|
||||
}
|
||||
}
|
||||
|
||||
const ClassId *ClassIdTree::getClassId( int class_id ) const
|
||||
{
|
||||
if( (class_id>=0) && (class_id<NUM_CLASS_IDS) ) return m_class[class_id];
|
||||
return NULL;
|
||||
}
|
||||
|
||||
const ClassId *ClassIdTree::getParentClassId( int class_id ) const
|
||||
{
|
||||
const ClassId *c = getClassId( class_id );
|
||||
if( c ) return getClassId( c->getParentId() );
|
||||
return NULL;
|
||||
}
|
||||
|
27
L2C_Server/world/model/base/ClassIdTree.h
Normal file
27
L2C_Server/world/model/base/ClassIdTree.h
Normal file
@ -0,0 +1,27 @@
|
||||
#pragma once
|
||||
#include "Race.h"
|
||||
#include "ClassId.h"
|
||||
|
||||
class ClassIdTree
|
||||
{
|
||||
protected:
|
||||
static ClassIdTree *_instance;
|
||||
static int _refCount;
|
||||
public:
|
||||
static ClassIdTree *getInstance();
|
||||
static void freeInstance();
|
||||
enum NumClassIds { NUM_CLASS_IDS = 137 };
|
||||
|
||||
protected:
|
||||
ClassIdTree();
|
||||
~ClassIdTree();
|
||||
void addClassId( int id, const wchar_t *name, bool isMage, bool isSummoner, Race race, int parentId );
|
||||
|
||||
public:
|
||||
const ClassId *getClassId( int class_id ) const;
|
||||
const ClassId *getParentClassId( int class_id ) const;
|
||||
|
||||
protected:
|
||||
// array to hold class tree info
|
||||
ClassId *m_class[NUM_CLASS_IDS]; // 136 classes
|
||||
};
|
11
L2C_Server/world/model/base/Race.h
Normal file
11
L2C_Server/world/model/base/Race.h
Normal file
@ -0,0 +1,11 @@
|
||||
#pragma once
|
||||
|
||||
enum Race
|
||||
{
|
||||
RACE_Human,
|
||||
RACE_Elf,
|
||||
RACE_DarkElf,
|
||||
RACE_Orc,
|
||||
RACE_Dwarf,
|
||||
RACE_Kamael
|
||||
};
|
45
L2C_Server/world/model/character/GameCharacter.cpp
Normal file
45
L2C_Server/world/model/character/GameCharacter.cpp
Normal file
@ -0,0 +1,45 @@
|
||||
#include "pch.h"
|
||||
#include "GameCharacter.h"
|
||||
|
||||
GameCharacter::GameCharacter( unsigned int objectId ): GameObject( objectId )
|
||||
{
|
||||
m_name = NULL;
|
||||
}
|
||||
|
||||
GameCharacter::~GameCharacter()
|
||||
{
|
||||
if( m_name )
|
||||
{
|
||||
free( m_name );
|
||||
m_name = NULL;
|
||||
}
|
||||
GameObject::~GameObject();
|
||||
}
|
||||
|
||||
void GameCharacter::setName( const wchar_t *name )
|
||||
{
|
||||
if( m_name ) free( m_name );
|
||||
m_name = NULL;
|
||||
if( name ) m_name = _wcsdup( name );
|
||||
}
|
||||
|
||||
void GameCharacter::setLevel( int level )
|
||||
{
|
||||
m_level = level;
|
||||
if( m_level < 0 ) m_level = 0;
|
||||
}
|
||||
|
||||
const wchar_t *GameCharacter::getName() const
|
||||
{
|
||||
return (const wchar_t *)m_name;
|
||||
}
|
||||
|
||||
wchar_t *GameCharacter::toString() const
|
||||
{
|
||||
GameCharacter *nc_this = const_cast<GameCharacter *>(this);
|
||||
if( !nc_this->m_toString_buffer )
|
||||
nc_this->m_toString_buffer = (wchar_t *)malloc( 512 );
|
||||
if( nc_this->m_toString_buffer )
|
||||
swprintf( nc_this->m_toString_buffer, 255, L"%s lvl %d [oid %u]", m_name, m_level, m_objectId );
|
||||
return m_toString_buffer;
|
||||
}
|
24
L2C_Server/world/model/character/GameCharacter.h
Normal file
24
L2C_Server/world/model/character/GameCharacter.h
Normal file
@ -0,0 +1,24 @@
|
||||
#pragma once
|
||||
#include "../GameObject.h"
|
||||
|
||||
class GameCharacter: public GameObject
|
||||
{
|
||||
public:
|
||||
GameCharacter( unsigned int objectId );
|
||||
virtual ~GameCharacter();
|
||||
|
||||
public:
|
||||
void setName( const wchar_t *name );
|
||||
void setLevel( int level );
|
||||
|
||||
public:
|
||||
const wchar_t *getName() const;
|
||||
int getLevel() const { return m_level; }
|
||||
|
||||
public:
|
||||
virtual wchar_t *toString() const;
|
||||
|
||||
protected:
|
||||
wchar_t *m_name;
|
||||
int m_level;
|
||||
};
|
11
L2C_Server/world/model/character/GameNpc.cpp
Normal file
11
L2C_Server/world/model/character/GameNpc.cpp
Normal file
@ -0,0 +1,11 @@
|
||||
#include "pch.h"
|
||||
#include "GameNpc.h"
|
||||
|
||||
GameNpc::GameNpc( unsigned int objectId, int npcTemplateId ): GameCharacter( objectId )
|
||||
{
|
||||
m_npcTemplateId = npcTemplateId;
|
||||
}
|
||||
|
||||
GameNpc::~GameNpc()
|
||||
{
|
||||
}
|
18
L2C_Server/world/model/character/GameNpc.h
Normal file
18
L2C_Server/world/model/character/GameNpc.h
Normal file
@ -0,0 +1,18 @@
|
||||
#pragma once
|
||||
#include "GameCharacter.h"
|
||||
|
||||
class GameNpc: public GameCharacter
|
||||
{
|
||||
public:
|
||||
GameNpc( unsigned int objectId, int npcTemplateId );
|
||||
virtual ~GameNpc();
|
||||
|
||||
public:
|
||||
int getNpcTemplateId() const { return m_npcTemplateId; }
|
||||
|
||||
public:
|
||||
void setNpcTemplateId( int tmplId ) { m_npcTemplateId = tmplId; }
|
||||
|
||||
protected:
|
||||
int m_npcTemplateId;
|
||||
};
|
19
L2C_Server/world/model/character/GamePlayer.cpp
Normal file
19
L2C_Server/world/model/character/GamePlayer.cpp
Normal file
@ -0,0 +1,19 @@
|
||||
#include "pch.h"
|
||||
#include "Log.h"
|
||||
#include "../../../net/GameClient/GameClient.h"
|
||||
#include "GamePlayer.h"
|
||||
|
||||
GamePlayer::GamePlayer( GameClient *clnt, unsigned int objectId ): GameCharacter( objectId )
|
||||
{
|
||||
m_gameClient = clnt;
|
||||
}
|
||||
|
||||
GamePlayer::~GamePlayer()
|
||||
{
|
||||
m_gameClient = NULL;
|
||||
}
|
||||
|
||||
GameClient *GamePlayer::getGameClient()
|
||||
{
|
||||
return m_gameClient;
|
||||
}
|
16
L2C_Server/world/model/character/GamePlayer.h
Normal file
16
L2C_Server/world/model/character/GamePlayer.h
Normal file
@ -0,0 +1,16 @@
|
||||
#pragma once
|
||||
#include "GameCharacter.h"
|
||||
class GameClient; // forward decl
|
||||
|
||||
class GamePlayer: public GameCharacter
|
||||
{
|
||||
public:
|
||||
GamePlayer( GameClient *clnt, unsigned int objectId );
|
||||
virtual ~GamePlayer();
|
||||
|
||||
public:
|
||||
GameClient *getGameClient();
|
||||
|
||||
protected:
|
||||
GameClient *m_gameClient;
|
||||
};
|
385
L2C_Server/world/templates/StatsSet.cpp
Normal file
385
L2C_Server/world/templates/StatsSet.cpp
Normal file
@ -0,0 +1,385 @@
|
||||
#include "pch.h"
|
||||
#include "StatsSet.h"
|
||||
#include "utils/Debugging.h"
|
||||
#include "utils/Exception.h"
|
||||
#include "l2c_utils.h"
|
||||
|
||||
StatsSet::StatsSet()
|
||||
{
|
||||
m_map.clear();
|
||||
}
|
||||
|
||||
StatsSet::~StatsSet()
|
||||
{
|
||||
m_map.clear();
|
||||
}
|
||||
|
||||
StatsSet::StatsSet( const StatsSet& other )
|
||||
{
|
||||
this->operator=( other );
|
||||
}
|
||||
|
||||
const StatsSet& StatsSet::operator=( const StatsSet& other )
|
||||
{
|
||||
if( this == &other ) return (*this);
|
||||
m_map = other.m_map;
|
||||
return (*this);
|
||||
}
|
||||
|
||||
bool StatsSet::getInt( const char *name, int *val )
|
||||
{
|
||||
std::wstring value;
|
||||
std::string sname( name );
|
||||
std::map<std::string, std::wstring>::const_iterator iter = m_map.find( sname );
|
||||
if( iter == m_map.end() )
|
||||
{
|
||||
throw Exception( "StatsSet: trying to get non-existent Int var [%s] with no default value!", name );
|
||||
//return false; // warning C4702: unreachable code
|
||||
}
|
||||
value = iter->second;
|
||||
int i = 0;
|
||||
int r = swscanf( value.c_str(), L"%d", &i );
|
||||
if( r == 1 )
|
||||
{
|
||||
(*val) = i;
|
||||
return true;
|
||||
}
|
||||
throw Exception( "StatsSet.getInt: failed to scanf %%d from [%s]=[%S]", name, value.c_str() );
|
||||
}
|
||||
|
||||
bool StatsSet::getInt( const char *name, int *val, int defVal )
|
||||
{
|
||||
std::wstring ws;
|
||||
std::wstring ws_def(L"0");
|
||||
getWString( name, ws, ws_def );
|
||||
int v = 0;
|
||||
int r = swscanf( ws.c_str(), L"%d", &v );
|
||||
if( r <= 0 ) (*val) = defVal; // 0 tokens were read by swscanf or EOL reached unexpectedly
|
||||
else (*val) = v;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool StatsSet::getUInt( const char *name, unsigned int *val )
|
||||
{
|
||||
std::wstring value;
|
||||
std::string sname( name );
|
||||
std::map<std::string, std::wstring>::const_iterator iter = m_map.find( sname );
|
||||
if( iter == m_map.end() )
|
||||
{
|
||||
throw Exception( "StatsSet: trying to get non-existent UInt var [%s] with no default value!", name );
|
||||
//return false; // warning C4702: unreachable code
|
||||
}
|
||||
value = iter->second;
|
||||
unsigned int ui = 0;
|
||||
int r = swscanf( value.c_str(), L"%u", &ui );
|
||||
if( r == 1 )
|
||||
{
|
||||
(*val) = ui;
|
||||
return true;
|
||||
}
|
||||
throw Exception( "StatsSet.getInt: failed to scanf %%u from [%s]=[%S]", name, value.c_str() );
|
||||
}
|
||||
|
||||
bool StatsSet::getUInt( const char *name, unsigned int *val, unsigned int defVal )
|
||||
{
|
||||
std::wstring ws;
|
||||
std::wstring ws_def(L"0");
|
||||
getWString( name, ws, ws_def );
|
||||
unsigned int v = 0;
|
||||
int r = swscanf( ws.c_str(), L"%u", &v );
|
||||
if( r <= 0 ) (*val) = defVal; // 0 tokens were read by swscanf or EOL reached unexpectedly
|
||||
else (*val) = v;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool StatsSet::getInt64( const char *name, long long int *val )
|
||||
{
|
||||
std::wstring value;
|
||||
std::string sname( name );
|
||||
std::map<std::string, std::wstring>::const_iterator iter = m_map.find( sname );
|
||||
if( iter == m_map.end() )
|
||||
{
|
||||
throw Exception( "StatsSet: trying to get non-existent Int64 var [%s] with no default value!", name );
|
||||
//return false; // warning C4702: unreachable code
|
||||
}
|
||||
value = iter->second;
|
||||
long long int i64 = 0;
|
||||
int r = swscanf( value.c_str(), L"%I64d", &i64 );
|
||||
if( r == 1 )
|
||||
{
|
||||
(*val) = i64;
|
||||
return true;
|
||||
}
|
||||
throw Exception( "StatsSet.getInt: failed to scanf %%I64d from [%s]=[%S]", name, value.c_str() );
|
||||
}
|
||||
|
||||
bool StatsSet::getInt64( const char *name, long long int *val, long long int defVal )
|
||||
{
|
||||
std::wstring ws;
|
||||
std::wstring ws_def(L"0");
|
||||
getWString( name, ws, ws_def );
|
||||
long long int v = 0;
|
||||
int r = swscanf( ws.c_str(), L"%I64d", &v );
|
||||
if( r <= 0 ) (*val) = defVal; // 0 tokens were read by swscanf or EOL reached unexpectedly
|
||||
else (*val) = v;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool StatsSet::getUInt64( const char *name, unsigned long long int *val )
|
||||
{
|
||||
std::wstring value;
|
||||
std::string sname( name );
|
||||
std::map<std::string, std::wstring>::const_iterator iter = m_map.find( sname );
|
||||
if( iter == m_map.end() )
|
||||
{
|
||||
throw Exception( "StatsSet: trying to get non-existent UInt64 var [%s] with no default value!", name );
|
||||
//return false; // warning C4702: unreachable code
|
||||
}
|
||||
value = iter->second;
|
||||
unsigned long long int ui64 = 0;
|
||||
int r = swscanf( value.c_str(), L"%I64u", &ui64 );
|
||||
if( r == 1 )
|
||||
{
|
||||
(*val) = ui64;
|
||||
return true;
|
||||
}
|
||||
throw Exception( "StatsSet.getInt: failed to scanf %%I64u from [%s]=[%S]", name, value.c_str() );
|
||||
}
|
||||
|
||||
bool StatsSet::getUInt64( const char *name, unsigned long long int *val, unsigned long long int defVal )
|
||||
{
|
||||
std::wstring ws;
|
||||
std::wstring ws_def(L"0");
|
||||
getWString( name, ws, ws_def );
|
||||
unsigned long long int v = 0;
|
||||
int r = swscanf( ws.c_str(), L"%I64u", &v );
|
||||
if( r <= 0 ) (*val) = defVal; // 0 tokens were read by swscanf or EOL reached unexpectedly
|
||||
else (*val) = v;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool StatsSet::getDouble( const char *name, double *val )
|
||||
{
|
||||
std::wstring value;
|
||||
std::string sname( name );
|
||||
std::map<std::string, std::wstring>::const_iterator iter = m_map.find( sname );
|
||||
if( iter == m_map.end() )
|
||||
{
|
||||
throw Exception( "StatsSet: trying to get non-existent Double var [%s] with no default value!", name );
|
||||
//return false; // warning C4702: unreachable code
|
||||
}
|
||||
value = iter->second;
|
||||
double lf = 0;
|
||||
int r = swscanf( value.c_str(), L"%lf", &lf );
|
||||
if( r == 1 )
|
||||
{
|
||||
(*val) = lf;
|
||||
return true;
|
||||
}
|
||||
throw Exception( "StatsSet.getInt: failed to scanf %%lf from [%s]=[%S]", name, value.c_str() );
|
||||
}
|
||||
|
||||
bool StatsSet::getDouble( const char *name, double *val, double defVal )
|
||||
{
|
||||
std::wstring ws;
|
||||
std::wstring ws_def(L"0.0");
|
||||
getWString( name, ws, ws_def );
|
||||
double v = 0.0;
|
||||
int r = swscanf( ws.c_str(), L"%lf", &v );
|
||||
if( r <= 0 ) (*val) = defVal; // 0 tokens were read by swscanf or EOL reached unexpectedly
|
||||
else (*val) = v;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool StatsSet::getString( const char *name, std::string& val )
|
||||
{
|
||||
std::wstring ws;
|
||||
std::string sname( name );
|
||||
std::map<std::string, std::wstring>::const_iterator iter = m_map.find( sname );
|
||||
if( iter == m_map.end() )
|
||||
{
|
||||
throw Exception( "StatsSet: trying to get non-existent String var [%s] with no default value!", name );
|
||||
//return false; // warning C4702: unreachable code
|
||||
}
|
||||
ws = iter->second;
|
||||
const wchar_t *cws = ws.c_str(); // get wchar_t *
|
||||
char *c_tmp = (char *)malloc( ws.size() + 16 ); // allocate space for Unicode -> ANSI convert
|
||||
if( c_tmp )
|
||||
{
|
||||
l2c_unicode_to_ansi( cws, c_tmp, ws.size()+1 );
|
||||
c_tmp[ws.size()] = 0;
|
||||
val.assign( c_tmp, ws.size() );
|
||||
free( c_tmp );
|
||||
}
|
||||
else val.assign( "malloc() failed" );
|
||||
return true;
|
||||
}
|
||||
|
||||
bool StatsSet::getString( const char *name, std::string& val, std::string& defVal )
|
||||
{
|
||||
std::wstring ws;
|
||||
std::wstring ws_def( L"" );
|
||||
getWString( name, ws, ws_def ); // always returns true
|
||||
const wchar_t *cws = ws.c_str();
|
||||
char *c_tmp = (char *)malloc( ws.size() + 16 );
|
||||
if( c_tmp )
|
||||
{
|
||||
l2c_unicode_to_ansi( cws, c_tmp, ws.size()+1 );
|
||||
c_tmp[ws.size()] = 0;
|
||||
val.assign( c_tmp, ws.size() );
|
||||
free( c_tmp );
|
||||
}
|
||||
else val = defVal;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool StatsSet::getWString( const char *name, std::wstring& val )
|
||||
{
|
||||
std::string sname( name );
|
||||
std::map<std::string, std::wstring>::const_iterator iter = m_map.find( sname );
|
||||
if( iter == m_map.end() )
|
||||
{
|
||||
throw Exception( "StatsSet: trying to get non-existent WString var [%s] with no default value!", name );
|
||||
//return false; // warning C4702: unreachable code
|
||||
}
|
||||
val = iter->second;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool StatsSet::getWString( const char *name, std::wstring& val, std::wstring& defVal )
|
||||
{
|
||||
std::string sname( name );
|
||||
std::map<std::string, std::wstring>::const_iterator iter = m_map.find( sname );
|
||||
if( iter == m_map.end() )
|
||||
{
|
||||
val = defVal;
|
||||
return true;
|
||||
}
|
||||
val = iter->second;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool StatsSet::getBool( const char *name, bool *val )
|
||||
{
|
||||
std::wstring value;
|
||||
std::string sname( name );
|
||||
std::map<std::string, std::wstring>::const_iterator iter = m_map.find( sname );
|
||||
if( iter == m_map.end() )
|
||||
{
|
||||
throw Exception( "StatsSet: trying to get non-existent Bool var [%s] with no default value!", name );
|
||||
//return false; // warning C4702: unreachable code
|
||||
}
|
||||
value = iter->second;
|
||||
int i = 0;
|
||||
swscanf( value.c_str(), L"%d", &i );
|
||||
(*val) = (bool)(i != 0);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool StatsSet::getBool( const char *name, bool *val, bool defVal )
|
||||
{
|
||||
std::wstring value;
|
||||
std::string sname( name );
|
||||
std::map<std::string, std::wstring>::const_iterator iter = m_map.find( sname );
|
||||
if( iter == m_map.end() )
|
||||
{
|
||||
(*val) = defVal;
|
||||
return true;
|
||||
}
|
||||
value = iter->second;
|
||||
int i = 0;
|
||||
swscanf( value.c_str(), L"%d", &i );
|
||||
(*val) = (bool)(i != 0);
|
||||
return true; // defVal funcs never return false and never throw exceptions
|
||||
}
|
||||
|
||||
|
||||
|
||||
bool StatsSet::setInt( const char *name, int val )
|
||||
{
|
||||
std::string sname( name );
|
||||
wchar_t wval[32];
|
||||
swprintf( wval, 31, L"%d", val );
|
||||
std::wstring v( wval );
|
||||
m_map[name] = v;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool StatsSet::setUInt( const char *name, unsigned int val )
|
||||
{
|
||||
std::string sname( name );
|
||||
wchar_t wval[32];
|
||||
swprintf( wval, 31, L"%u", val );
|
||||
std::wstring v( wval );
|
||||
m_map[name] = v;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool StatsSet::setInt64( const char *name, long long int val )
|
||||
{
|
||||
std::string sname( name );
|
||||
wchar_t wval[32];
|
||||
swprintf( wval, 31, L"%I64d", val );
|
||||
std::wstring v( wval );
|
||||
m_map[name] = v;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool StatsSet::setUInt64( const char *name, unsigned long long int val )
|
||||
{
|
||||
std::string sname( name );
|
||||
wchar_t wval[32];
|
||||
swprintf( wval, 31, L"%I64u", val );
|
||||
std::wstring v( wval );
|
||||
m_map[name] = v;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool StatsSet::setDouble( const char *name, double val )
|
||||
{
|
||||
std::string sname( name );
|
||||
wchar_t wval[32];
|
||||
swprintf( wval, 31, L"%0.20f", val );
|
||||
std::wstring v( wval );
|
||||
m_map[name] = v;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool StatsSet::setString( const char *name, std::string& val )
|
||||
{
|
||||
std::string sname( name );
|
||||
wchar_t *wval = (wchar_t *)malloc( (val.size()+16) * sizeof(wchar_t) );
|
||||
if( wval )
|
||||
{
|
||||
swprintf( wval, val.size()+1, L"%S", val.c_str() );
|
||||
std::wstring v( wval );
|
||||
m_map[sname] = v;
|
||||
free( wval );
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool StatsSet::setString( const char *name, const char *val )
|
||||
{
|
||||
std::string sval( val );
|
||||
return setString( name, sval );
|
||||
}
|
||||
|
||||
bool StatsSet::setWString( const char *name, const wchar_t *val )
|
||||
{
|
||||
std::wstring sval( val );
|
||||
return setWString( name, sval );
|
||||
}
|
||||
|
||||
bool StatsSet::setWString( const char *name, std::wstring& val )
|
||||
{
|
||||
std::string sname( name );
|
||||
m_map[sname] = val;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool StatsSet::setBool( const char *name, bool val )
|
||||
{
|
||||
return setInt( name, (int)val );
|
||||
}
|
44
L2C_Server/world/templates/StatsSet.h
Normal file
44
L2C_Server/world/templates/StatsSet.h
Normal file
@ -0,0 +1,44 @@
|
||||
#pragma once
|
||||
|
||||
class StatsSet
|
||||
{
|
||||
public:
|
||||
StatsSet();
|
||||
~StatsSet();
|
||||
StatsSet( const StatsSet& other );
|
||||
const StatsSet& operator=( const StatsSet& other );
|
||||
|
||||
public:
|
||||
bool getInt( const char *name, int *val );
|
||||
bool getInt( const char *name, int *val, int defVal );
|
||||
bool getUInt( const char *name, unsigned int *val );
|
||||
bool getUInt( const char *name, unsigned int *val, unsigned int defVal );
|
||||
bool getInt64( const char *name, long long int *val );
|
||||
bool getInt64( const char *name, long long int *val, long long int defVal );
|
||||
bool getUInt64( const char *name, unsigned long long int *val );
|
||||
bool getUInt64( const char *name, unsigned long long int *val, unsigned long long int defVal );
|
||||
bool getDouble( const char *name, double *val );
|
||||
bool getDouble( const char *name, double *val, double defVal );
|
||||
bool getString( const char *name, std::string& val );
|
||||
bool getString( const char *name, std::string& val, std::string& defVal );
|
||||
bool getWString( const char *name, std::wstring& val );
|
||||
bool getWString( const char *name, std::wstring& val, std::wstring& defVal );
|
||||
bool getBool( const char *name, bool *val );
|
||||
bool getBool( const char *name, bool *val, bool defVal );
|
||||
|
||||
public:
|
||||
bool setInt( const char *name, int val );
|
||||
bool setUInt( const char *name, unsigned int val );
|
||||
bool setInt64( const char *name, long long int val );
|
||||
bool setUInt64( const char *name, unsigned long long int val );
|
||||
bool setDouble( const char *name, double val );
|
||||
bool setString( const char *name, std::string& val );
|
||||
bool setString( const char *name, const char *val );
|
||||
bool setWString( const char *name, std::wstring& val );
|
||||
bool setWString( const char *name, const wchar_t *val );
|
||||
bool setBool( const char *name, bool val );
|
||||
|
||||
protected:
|
||||
// map of pair: <"name", L"value">
|
||||
std::map<std::string, std::wstring> m_map;
|
||||
};
|
79
L2C_Server/world/templates/chars/L2CharTemplate.cpp
Normal file
79
L2C_Server/world/templates/chars/L2CharTemplate.cpp
Normal file
@ -0,0 +1,79 @@
|
||||
#include "pch.h"
|
||||
#include "L2CharTemplate.h"
|
||||
|
||||
L2CharTemplate::L2CharTemplate( StatsSet& set )
|
||||
{
|
||||
// Base stats
|
||||
set.getInt( "baseSTR", &baseSTR );
|
||||
set.getInt( "baseCON", &baseCON );
|
||||
set.getInt( "baseDEX", &baseDEX );
|
||||
set.getInt( "baseINT", &baseINT );
|
||||
set.getInt( "baseWIT", &baseWIT );
|
||||
set.getInt( "baseMEN", &baseMEN );
|
||||
set.getDouble( "baseHpMax", &baseHpMax );
|
||||
set.getDouble( "baseCpMax", &baseCpMax );
|
||||
set.getDouble( "baseMpMax", &baseMpMax );
|
||||
set.getDouble( "baseHpReg", &baseHpReg );
|
||||
set.getDouble( "baseMpReg", &baseMpReg );
|
||||
set.getInt( "basePAtk", &basePAtk );
|
||||
set.getInt( "baseMAtk", &baseMAtk );
|
||||
set.getInt( "basePDef", &basePDef );
|
||||
set.getInt( "baseMDef", &baseMDef );
|
||||
set.getInt( "basePAtkSpd", &basePAtkSpd );
|
||||
set.getInt( "baseMAtkSpd", &baseMAtkSpd );
|
||||
set.getDouble( "baseMReuseDelay", &baseMReuseRate, 1.0 );
|
||||
set.getInt( "baseShldDef", &baseShldDef );
|
||||
set.getInt( "baseAtkRange", &baseAtkRange );
|
||||
set.getInt( "baseShldRate", &baseShldRate );
|
||||
set.getInt( "baseCritRate", &baseCritRate );
|
||||
set.getInt( "baseMCritRate", &baseMCritRate, 80 ); // CT2: The magic critical rate has been increased to 10 times.
|
||||
set.getInt( "baseWalkSpd", &baseWalkSpd );
|
||||
set.getInt( "baseRunSpd", &baseRunSpd );
|
||||
|
||||
// missed base stats
|
||||
set.getInt( "baseAccuracy", &baseAccuracy );
|
||||
set.getInt( "baseEvasion", &baseEvasion );
|
||||
|
||||
// SpecialStats
|
||||
set.getInt( "baseBreath", &baseBreath, 100 );
|
||||
set.getInt( "baseAggression", &baseAggression, 0 );
|
||||
set.getInt( "baseBleed", &baseBleed, 0 );
|
||||
set.getInt( "basePoison", &basePoison, 0 );
|
||||
set.getInt( "baseStun", &baseStun, 0 );
|
||||
set.getInt( "baseRoot", &baseRoot, 0 );
|
||||
set.getInt( "baseMovement", &baseMovement, 0 );
|
||||
set.getInt( "baseConfusion", &baseConfusion, 0 );
|
||||
set.getInt( "baseSleep", &baseSleep, 0 );
|
||||
set.getInt( "baseFire", &baseFire, 0 );
|
||||
set.getInt( "baseWind", &baseWind, 0 );
|
||||
set.getInt( "baseWater", &baseWater, 0 );
|
||||
set.getInt( "baseEarth", &baseEarth, 0 );
|
||||
set.getInt( "baseHoly", &baseHoly, 0 );
|
||||
set.getInt( "baseDark", &baseDark, 0 );
|
||||
set.getDouble( "baseAaggressionVuln", &baseAggressionVuln, 1.0 );
|
||||
set.getDouble( "baseBleedVuln", &baseBleedVuln, 1.0 );
|
||||
set.getDouble( "basePoisonVuln", &basePoisonVuln, 1.0 );
|
||||
set.getDouble( "baseStunVuln", &baseStunVuln, 1.0 );
|
||||
set.getDouble( "baseRootVuln", &baseRootVuln, 1.0 );
|
||||
set.getDouble( "baseMovementVuln", &baseMovementVuln, 1.0 );
|
||||
set.getDouble( "baseConfusionVuln", &baseConfusionVuln, 1.0 );
|
||||
set.getDouble( "baseSleepVuln", &baseSleepVuln, 1.0 );
|
||||
set.getDouble( "baseFireRes", &baseFireRes, 0.0 );
|
||||
set.getDouble( "baseWindRes", &baseWindRes, 0.0 );
|
||||
set.getDouble( "baseWaterRes", &baseWaterRes, 0.0 );
|
||||
set.getDouble( "baseEarthRes", &baseEarthRes, 0.0 );
|
||||
set.getDouble( "baseHolyRes", &baseHolyRes, 0.0 );
|
||||
set.getDouble( "baseDarkRes", &baseDarkRes, 0.0 );
|
||||
set.getDouble( "baseCritVuln", &baseCritVuln, 1.0 );
|
||||
|
||||
// undead? default NO
|
||||
set.getBool( "isUndead", &isUndead, false );
|
||||
|
||||
//C4 Stats
|
||||
set.getInt( "baseMpConsumeRate", &baseMpConsumeRate, 0 );
|
||||
set.getInt( "baseHpConsumeRate", &baseHpConsumeRate, 0 );
|
||||
|
||||
// Geometry
|
||||
set.getInt( "collision_radius", &collisionRadius, 10 );
|
||||
set.getInt( "collision_height", &collisionHeight, 10 );
|
||||
}
|
81
L2C_Server/world/templates/chars/L2CharTemplate.h
Normal file
81
L2C_Server/world/templates/chars/L2CharTemplate.h
Normal file
@ -0,0 +1,81 @@
|
||||
#pragma once
|
||||
#include "world/templates/StatsSet.h"
|
||||
|
||||
class L2CharTemplate
|
||||
{
|
||||
public:
|
||||
L2CharTemplate( StatsSet& set );
|
||||
|
||||
public:
|
||||
// BaseStats
|
||||
int baseSTR;
|
||||
int baseCON;
|
||||
int baseDEX;
|
||||
int baseINT;
|
||||
int baseWIT;
|
||||
int baseMEN;
|
||||
double baseHpMax;
|
||||
double baseCpMax;
|
||||
double baseMpMax;
|
||||
double baseHpReg;
|
||||
double baseMpReg;
|
||||
|
||||
int basePAtk;
|
||||
int baseMAtk;
|
||||
int basePDef;
|
||||
int baseMDef;
|
||||
int basePAtkSpd;
|
||||
int baseMAtkSpd;
|
||||
double baseMReuseRate;
|
||||
int baseShldDef;
|
||||
int baseAtkRange;
|
||||
int baseShldRate;
|
||||
int baseCritRate;
|
||||
int baseMCritRate;
|
||||
int baseWalkSpd;
|
||||
int baseRunSpd;
|
||||
// missed base stats
|
||||
int baseAccuracy;
|
||||
int baseEvasion;
|
||||
|
||||
// SpecialStats
|
||||
int baseBreath;
|
||||
int baseAggression;
|
||||
int baseBleed;
|
||||
int basePoison;
|
||||
int baseStun;
|
||||
int baseRoot;
|
||||
int baseMovement;
|
||||
int baseConfusion;
|
||||
int baseSleep;
|
||||
int baseFire;
|
||||
int baseWind;
|
||||
int baseWater;
|
||||
int baseEarth;
|
||||
int baseHoly;
|
||||
int baseDark;
|
||||
double baseAggressionVuln;
|
||||
double baseBleedVuln;
|
||||
double basePoisonVuln;
|
||||
double baseStunVuln;
|
||||
double baseRootVuln;
|
||||
double baseMovementVuln;
|
||||
double baseConfusionVuln;
|
||||
double baseSleepVuln;
|
||||
double baseFireRes;
|
||||
double baseWindRes;
|
||||
double baseWaterRes;
|
||||
double baseEarthRes;
|
||||
double baseHolyRes;
|
||||
double baseDarkRes;
|
||||
double baseCritVuln;
|
||||
|
||||
bool isUndead;
|
||||
|
||||
//C4 Stats
|
||||
int baseMpConsumeRate;
|
||||
int baseHpConsumeRate;
|
||||
|
||||
int collisionRadius;
|
||||
int collisionHeight;
|
||||
};
|
83
L2C_Server/world/templates/chars/L2PlayerTemplate.cpp
Normal file
83
L2C_Server/world/templates/chars/L2PlayerTemplate.cpp
Normal file
@ -0,0 +1,83 @@
|
||||
#include "pch.h"
|
||||
#include "L2PlayerTemplate.h"
|
||||
|
||||
L2PlayerTemplate::L2PlayerTemplate( StatsSet& set ): L2CharTemplate( set )
|
||||
{
|
||||
set.getInt( "classId", &iClassId );
|
||||
classId = ClassIdTree::getInstance()->getClassId( iClassId );
|
||||
set.getInt( "raceId", (int *)&race );
|
||||
|
||||
set.getInt( "spawnX", &spawnX );
|
||||
set.getInt( "spawnY", &spawnY );
|
||||
set.getInt( "spawnZ", &spawnZ );
|
||||
|
||||
set.getInt( "classBaseLevel", &classBaseLevel );
|
||||
|
||||
set.getDouble( "lvlHpAdd", &lvlHpAdd );
|
||||
set.getDouble( "lvlHpMod", &lvlHpMod );
|
||||
set.getDouble( "lvlCpAdd", &lvlCpAdd );
|
||||
set.getDouble( "lvlCpMod", &lvlCpMod );
|
||||
set.getDouble( "lvlMpAdd", &lvlMpAdd );
|
||||
set.getDouble( "lvlMpMod", &lvlMpMod );
|
||||
|
||||
// missed loaded vars
|
||||
set.getInt( "baseLoad", &baseLoad, 62500 );
|
||||
set.getBool( "canCraft", &canCraft, false );
|
||||
}
|
||||
|
||||
void L2PlayerTemplate::addItem( int itemId, int amount, bool equipped )
|
||||
{
|
||||
PcTemplateItem it( itemId, amount, equipped );
|
||||
m_items.push_back( it );
|
||||
}
|
||||
|
||||
const std::list<L2PlayerTemplate::PcTemplateItem>& L2PlayerTemplate::getItems() const
|
||||
{
|
||||
return m_items;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
L2PlayerTemplate::PcTemplateItem::PcTemplateItem()
|
||||
{
|
||||
m_itemId = 0;
|
||||
m_amount = 0;
|
||||
m_equipped = false;
|
||||
}
|
||||
|
||||
L2PlayerTemplate::PcTemplateItem::PcTemplateItem( int itemId, int amount, bool equipped )
|
||||
{
|
||||
m_itemId = itemId;
|
||||
m_amount = amount;
|
||||
m_equipped = equipped;
|
||||
}
|
||||
|
||||
L2PlayerTemplate::PcTemplateItem::PcTemplateItem( const L2PlayerTemplate::PcTemplateItem& other )
|
||||
{
|
||||
this->operator=( other );
|
||||
}
|
||||
|
||||
const L2PlayerTemplate::PcTemplateItem& L2PlayerTemplate::PcTemplateItem::operator=( const L2PlayerTemplate::PcTemplateItem& other )
|
||||
{
|
||||
if( this == &other ) return (*this);
|
||||
m_itemId = other.m_itemId;
|
||||
m_amount = other.m_amount;
|
||||
m_equipped = other.m_equipped;
|
||||
return (*this);
|
||||
}
|
||||
|
||||
int L2PlayerTemplate::PcTemplateItem::getItemId() const
|
||||
{
|
||||
return m_itemId;
|
||||
}
|
||||
|
||||
int L2PlayerTemplate::PcTemplateItem::getAmount() const
|
||||
{
|
||||
return m_amount;
|
||||
}
|
||||
|
||||
bool L2PlayerTemplate::PcTemplateItem::isEquipped() const
|
||||
{
|
||||
return m_equipped;
|
||||
}
|
53
L2C_Server/world/templates/chars/L2PlayerTemplate.h
Normal file
53
L2C_Server/world/templates/chars/L2PlayerTemplate.h
Normal file
@ -0,0 +1,53 @@
|
||||
#pragma once
|
||||
#include "L2CharTemplate.h"
|
||||
#include "world/model/base/ClassId.h"
|
||||
#include "world/model/base/ClassIdTree.h"
|
||||
#include "world/model/base/Race.h"
|
||||
|
||||
class L2PlayerTemplate: public L2CharTemplate
|
||||
{
|
||||
public: // class for template item
|
||||
class PcTemplateItem
|
||||
{
|
||||
public:
|
||||
PcTemplateItem();
|
||||
PcTemplateItem( int itemId, int amount, bool equipped );
|
||||
PcTemplateItem( const PcTemplateItem& other );
|
||||
const PcTemplateItem& operator=( const PcTemplateItem& other );
|
||||
int getItemId() const;
|
||||
int getAmount() const;
|
||||
bool isEquipped() const;
|
||||
protected:
|
||||
int m_itemId;
|
||||
int m_amount;
|
||||
bool m_equipped;
|
||||
};
|
||||
|
||||
public:
|
||||
L2PlayerTemplate( StatsSet& set );
|
||||
void addItem( int itemId, int amount, bool equipped );
|
||||
const std::list<L2PlayerTemplate::PcTemplateItem>& getItems() const;
|
||||
|
||||
public:
|
||||
int iClassId;
|
||||
const ClassId *classId;
|
||||
Race race;
|
||||
|
||||
int spawnX;
|
||||
int spawnY;
|
||||
int spawnZ;
|
||||
|
||||
int classBaseLevel;
|
||||
double lvlHpAdd;
|
||||
double lvlHpMod;
|
||||
double lvlCpAdd;
|
||||
double lvlCpMod;
|
||||
double lvlMpAdd;
|
||||
double lvlMpMod;
|
||||
|
||||
int baseLoad;
|
||||
bool canCraft;
|
||||
|
||||
protected:
|
||||
std::list<L2PlayerTemplate::PcTemplateItem> m_items;
|
||||
};
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user