forked from metin2/server
337 lines
8.2 KiB
C++
337 lines
8.2 KiB
C++
#include "stdafx.h"
|
||
#include "config.h"
|
||
#include "utils.h"
|
||
#include "desc_client.h"
|
||
#include "desc_manager.h"
|
||
#include "char.h"
|
||
#include "protocol.h"
|
||
#include "p2p.h"
|
||
#include "buffer_manager.h"
|
||
#include "guild_manager.h"
|
||
#include "db.h"
|
||
#include "party.h"
|
||
|
||
LPCLIENT_DESC db_clientdesc = NULL;
|
||
LPCLIENT_DESC g_pkAuthMasterDesc = NULL;
|
||
LPCLIENT_DESC g_NetmarbleDBDesc = NULL;
|
||
LPCLIENT_DESC g_TeenDesc = NULL;
|
||
|
||
static const char* GetKnownClientDescName(LPCLIENT_DESC desc) {
|
||
if (desc == db_clientdesc) {
|
||
return "db_clientdesc";
|
||
} else if (desc == g_pkAuthMasterDesc) {
|
||
return "g_pkAuthMasterDesc";
|
||
} else if (desc == g_NetmarbleDBDesc) {
|
||
return "g_NetmarbleDBDesc";
|
||
} else if (desc == g_TeenDesc) {
|
||
return "g_TeenDesc";
|
||
}
|
||
return "unknown";
|
||
}
|
||
|
||
void ClientDescEventHandler(bufferevent *bev, short events, void *ptr) {
|
||
auto * clientDesc = static_cast<LPCLIENT_DESC>(ptr);
|
||
|
||
if (events & BEV_EVENT_CONNECTED) {
|
||
sys_log(0, "SYSTEM: connected to server (ptr %p)", clientDesc);
|
||
clientDesc->OnConnectSuccessful();
|
||
|
||
// Now that we're connected, we can set the read/write/event handlers (and therefore stop using this handler)
|
||
auto * desc = static_cast<LPDESC>(clientDesc);
|
||
bufferevent_setcb(bev, DescReadHandler, DescWriteHandler, DescEventHandler, desc);
|
||
} else if (events & (BEV_EVENT_ERROR|BEV_EVENT_EOF)) {
|
||
if (events & BEV_EVENT_ERROR) {
|
||
int err = bufferevent_socket_get_dns_error(bev);
|
||
if (err)
|
||
sys_err("SYSTEM: Client connection DNS error: %s", evutil_gai_strerror(err));
|
||
}
|
||
|
||
sys_log(0, "SYSTEM: closing client connection (ptr %p)", clientDesc);
|
||
clientDesc->SetPhase(PHASE_CLOSE);
|
||
}
|
||
}
|
||
|
||
CLIENT_DESC::CLIENT_DESC()
|
||
{
|
||
m_iPhaseWhenSucceed = 0;
|
||
m_bRetryWhenClosed = false;
|
||
m_LastTryToConnectTime = 0;
|
||
m_tLastChannelStatusUpdateTime = 0;
|
||
}
|
||
|
||
CLIENT_DESC::~CLIENT_DESC()
|
||
{
|
||
}
|
||
|
||
void CLIENT_DESC::Destroy()
|
||
{
|
||
if (m_bufevent == nullptr)
|
||
return;
|
||
|
||
P2P_MANAGER::instance().UnregisterConnector(this);
|
||
|
||
if (this == db_clientdesc)
|
||
{
|
||
CPartyManager::instance().DeleteAllParty();
|
||
CPartyManager::instance().DisablePCParty();
|
||
CGuildManager::instance().StopAllGuildWar();
|
||
DBManager::instance().StopAllBilling();
|
||
}
|
||
|
||
sys_log(0, "SYSTEM: closing client socket.");
|
||
|
||
bufferevent_free(m_bufevent);
|
||
m_bufevent = nullptr;
|
||
|
||
// Chain up to base class Destroy()
|
||
DESC::Destroy();
|
||
}
|
||
|
||
void CLIENT_DESC::SetRetryWhenClosed(bool b)
|
||
{
|
||
m_bRetryWhenClosed = b;
|
||
}
|
||
|
||
bool CLIENT_DESC::Connect(int iPhaseWhenSucceed)
|
||
{
|
||
if (iPhaseWhenSucceed != 0)
|
||
m_iPhaseWhenSucceed = iPhaseWhenSucceed;
|
||
|
||
if (get_global_time() - m_LastTryToConnectTime < 3) // 3<><33>
|
||
return false;
|
||
|
||
m_LastTryToConnectTime = get_global_time();
|
||
|
||
if (m_bufevent != nullptr)
|
||
return false;
|
||
|
||
sys_log(0, "SYSTEM: Trying to connect to %s:%d", m_stHost.c_str(), m_wPort);
|
||
|
||
if (m_evbase == nullptr) {
|
||
sys_err("SYSTEM: event base not set!");
|
||
return false;
|
||
}
|
||
if (m_dnsBase == nullptr) {
|
||
sys_err("SYSTEM: DNS event base not set!");
|
||
return false;
|
||
}
|
||
|
||
m_bufevent = bufferevent_socket_new(m_evbase, -1, BEV_OPT_CLOSE_ON_FREE);
|
||
bufferevent_setcb(m_bufevent, NULL, NULL, ClientDescEventHandler, this);
|
||
bufferevent_enable(m_bufevent, EV_READ|EV_WRITE);
|
||
bufferevent_socket_connect_hostname(m_bufevent, m_dnsBase, AF_UNSPEC, m_stHost.c_str(), m_wPort);
|
||
|
||
SetPhase(PHASE_CLIENT_CONNECTING);
|
||
return true;
|
||
}
|
||
|
||
void CLIENT_DESC::OnConnectSuccessful() {
|
||
sys_log(0, "SYSTEM: connected to server (ptr %p)", this);
|
||
SetPhase(m_iPhaseWhenSucceed);
|
||
}
|
||
|
||
void CLIENT_DESC::Setup(event_base* base, evdns_base * dns_base, const char * _host, WORD _port)
|
||
{
|
||
m_evbase = base;
|
||
m_dnsBase = dns_base;
|
||
m_stHost = _host;
|
||
m_wPort = _port;
|
||
m_bufevent = nullptr;
|
||
}
|
||
|
||
void CLIENT_DESC::SetPhase(int iPhase)
|
||
{
|
||
switch (iPhase)
|
||
{
|
||
case PHASE_CLIENT_CONNECTING:
|
||
sys_log(1, "PHASE_CLIENT_DESC::CONNECTING");
|
||
m_pInputProcessor = NULL;
|
||
break;
|
||
|
||
case PHASE_DBCLIENT:
|
||
{
|
||
sys_log(1, "PHASE_DBCLIENT");
|
||
|
||
if (!g_bAuthServer)
|
||
{
|
||
static bool bSentBoot = false;
|
||
|
||
if (!bSentBoot)
|
||
{
|
||
bSentBoot = true;
|
||
TPacketGDBoot p;
|
||
p.dwItemIDRange[0] = 0;
|
||
p.dwItemIDRange[1] = 0;
|
||
memcpy(p.szIP, g_szPublicIP, 16);
|
||
DBPacket(HEADER_GD_BOOT, 0, &p, sizeof(p));
|
||
}
|
||
}
|
||
|
||
TEMP_BUFFER buf;
|
||
|
||
TPacketGDSetup p;
|
||
|
||
memset(&p, 0, sizeof(p));
|
||
strncpy(p.szPublicIP, g_szPublicIP, sizeof(p.szPublicIP));
|
||
|
||
if (!g_bAuthServer)
|
||
{
|
||
p.bChannel = g_bChannel;
|
||
p.wListenPort = mother_port;
|
||
p.wP2PPort = p2p_port;
|
||
p.bAuthServer = false;
|
||
map_allow_copy(p.alMaps, 32);
|
||
|
||
const DESC_MANAGER::DESC_SET & c_set = DESC_MANAGER::instance().GetClientSet();
|
||
DESC_MANAGER::DESC_SET::const_iterator it;
|
||
|
||
for (it = c_set.begin(); it != c_set.end(); ++it)
|
||
{
|
||
LPDESC d = *it;
|
||
|
||
if (d->GetAccountTable().id != 0)
|
||
++p.dwLoginCount;
|
||
}
|
||
|
||
buf.write(&p, sizeof(p));
|
||
|
||
if (p.dwLoginCount)
|
||
{
|
||
TPacketLoginOnSetup pck;
|
||
|
||
for (it = c_set.begin(); it != c_set.end(); ++it)
|
||
{
|
||
LPDESC d = *it;
|
||
|
||
TAccountTable & r = d->GetAccountTable();
|
||
|
||
if (r.id != 0)
|
||
{
|
||
pck.dwID = r.id;
|
||
strncpy(pck.szLogin, r.login, sizeof(pck.szLogin));
|
||
strncpy(pck.szSocialID, r.social_id, sizeof(pck.szSocialID));
|
||
strncpy(pck.szHost, d->GetHostName(), sizeof(pck.szHost));
|
||
pck.dwLoginKey = d->GetLoginKey();
|
||
|
||
buf.write(&pck, sizeof(TPacketLoginOnSetup));
|
||
}
|
||
}
|
||
}
|
||
|
||
sys_log(0, "DB_SETUP current user %d size %d", p.dwLoginCount, buf.size());
|
||
|
||
// <20><>Ƽ<EFBFBD><C6BC> ó<><C3B3><EFBFBD><EFBFBD> <20><> <20>ְ<EFBFBD> <20><>.
|
||
CPartyManager::instance().EnablePCParty();
|
||
//CPartyManager::instance().SendPartyToDB();
|
||
}
|
||
else
|
||
{
|
||
p.bAuthServer = true;
|
||
buf.write(&p, sizeof(p));
|
||
}
|
||
|
||
DBPacket(HEADER_GD_SETUP, 0, buf.read_peek(), buf.size());
|
||
m_pInputProcessor = &m_inputDB;
|
||
}
|
||
break;
|
||
|
||
case PHASE_P2P:
|
||
sys_log(1, "PHASE_P2P");
|
||
m_pInputProcessor = &m_inputP2P;
|
||
break;
|
||
|
||
case PHASE_CLOSE:
|
||
m_pInputProcessor = NULL;
|
||
break;
|
||
|
||
case PHASE_TEEN:
|
||
m_inputTeen.SetStep(0);
|
||
m_pInputProcessor = &m_inputTeen;
|
||
break;
|
||
|
||
}
|
||
|
||
m_iPhase = iPhase;
|
||
}
|
||
|
||
void CLIENT_DESC::DBPacketHeader(BYTE bHeader, DWORD dwHandle, DWORD dwSize)
|
||
{
|
||
DESC::RawPacket(encode_byte(bHeader), sizeof(BYTE));
|
||
DESC::RawPacket(encode_4bytes(dwHandle), sizeof(DWORD));
|
||
DESC::RawPacket(encode_4bytes(dwSize), sizeof(DWORD));
|
||
}
|
||
|
||
void CLIENT_DESC::DBPacket(BYTE bHeader, DWORD dwHandle, const void * c_pvData, DWORD dwSize)
|
||
{
|
||
if (m_bufevent == nullptr) {
|
||
sys_log(0, "CLIENT_DESC [%s] trying DBPacket() while not connected",
|
||
GetKnownClientDescName(this));
|
||
return;
|
||
}
|
||
sys_log(1, "DB_PACKET: header %d handle %d size %d", bHeader, dwHandle, dwSize);
|
||
DBPacketHeader(bHeader, dwHandle, dwSize);
|
||
|
||
if (c_pvData)
|
||
DESC::RawPacket(c_pvData, dwSize);
|
||
}
|
||
|
||
void CLIENT_DESC::Packet(const void * c_pvData, int iSize)
|
||
{
|
||
if (m_bufevent == nullptr) {
|
||
sys_log(0, "CLIENT_DESC [%s] trying Packet() while not connected",
|
||
GetKnownClientDescName(this));
|
||
return;
|
||
}
|
||
|
||
DESC::RawPacket(c_pvData, iSize);
|
||
}
|
||
|
||
bool CLIENT_DESC::IsRetryWhenClosed()
|
||
{
|
||
return (0 == thecore_is_shutdowned() && m_bRetryWhenClosed);
|
||
}
|
||
|
||
void CLIENT_DESC::Update(DWORD t)
|
||
{
|
||
if (!g_bAuthServer) {
|
||
UpdateChannelStatus(t, false);
|
||
}
|
||
}
|
||
|
||
void CLIENT_DESC::UpdateChannelStatus(DWORD t, bool fForce)
|
||
{
|
||
enum {
|
||
CHANNELSTATUS_UPDATE_PERIOD = 5*60*1000, // 5<>и<EFBFBD><D0B8><EFBFBD>
|
||
};
|
||
if (fForce || m_tLastChannelStatusUpdateTime+CHANNELSTATUS_UPDATE_PERIOD < t) {
|
||
int iTotal;
|
||
int * paiEmpireUserCount;
|
||
int iLocal;
|
||
DESC_MANAGER::instance().GetUserCount(iTotal, &paiEmpireUserCount, iLocal);
|
||
|
||
TChannelStatus channelStatus;
|
||
channelStatus.nPort = mother_port;
|
||
|
||
if (g_bNoMoreClient) channelStatus.bStatus = 0;
|
||
else channelStatus.bStatus = iTotal > g_iFullUserCount ? 3 : iTotal > g_iBusyUserCount ? 2 : 1;
|
||
|
||
DBPacket(HEADER_GD_UPDATE_CHANNELSTATUS, 0, &channelStatus, sizeof(channelStatus));
|
||
m_tLastChannelStatusUpdateTime = t;
|
||
}
|
||
}
|
||
|
||
void CLIENT_DESC::Reset()
|
||
{
|
||
// Backup connection target info
|
||
event_base * evbase = m_evbase;
|
||
std::string host = m_stHost;
|
||
WORD port = m_wPort;
|
||
|
||
Destroy();
|
||
Initialize();
|
||
|
||
// Restore connection target info
|
||
m_evbase = evbase;
|
||
m_stHost = host;
|
||
m_wPort = port;
|
||
} |