forked from metin2/server
1084 lines
28 KiB
C++
1084 lines
28 KiB
C++
#include "stdafx.h"
|
|
#include "constants.h"
|
|
#include <common/teen_packet.h>
|
|
#include "config.h"
|
|
#include "utils.h"
|
|
#include "input.h"
|
|
#include "desc_client.h"
|
|
#include "desc_manager.h"
|
|
#include "char.h"
|
|
#include "char_manager.h"
|
|
#include "cmd.h"
|
|
#include "buffer_manager.h"
|
|
#include "protocol.h"
|
|
#include "pvp.h"
|
|
#include "start_position.h"
|
|
#include "messenger_manager.h"
|
|
#include "guild_manager.h"
|
|
#include "party.h"
|
|
#include "dungeon.h"
|
|
#include "war_map.h"
|
|
#include "questmanager.h"
|
|
#include "building.h"
|
|
#include "wedding.h"
|
|
#include "affect.h"
|
|
#include "arena.h"
|
|
#include "OXEvent.h"
|
|
#include "priv_manager.h"
|
|
#include "log.h"
|
|
#include "horsename_manager.h"
|
|
#include "MarkManager.h"
|
|
|
|
static void _send_bonus_info(LPCHARACTER ch)
|
|
{
|
|
int item_drop_bonus = 0;
|
|
int gold_drop_bonus = 0;
|
|
int gold10_drop_bonus = 0;
|
|
int exp_bonus = 0;
|
|
|
|
item_drop_bonus = CPrivManager::instance().GetPriv(ch, PRIV_ITEM_DROP);
|
|
gold_drop_bonus = CPrivManager::instance().GetPriv(ch, PRIV_GOLD_DROP);
|
|
gold10_drop_bonus = CPrivManager::instance().GetPriv(ch, PRIV_GOLD10_DROP);
|
|
exp_bonus = CPrivManager::instance().GetPriv(ch, PRIV_EXP_PCT);
|
|
|
|
if (item_drop_bonus)
|
|
{
|
|
ch->ChatPacket(CHAT_TYPE_NOTICE,
|
|
LC_TEXT("\xBE\xC6\xC0\xCC\xC5\xDB \xB5\xE5\xB7\xD3\xB7\xFC %d%% \xC3\xDF\xB0\xA1 \xC0\xCC\xBA\xA5\xC6\xAE \xC1\xDF\xC0\xD4\xB4\xCF\xB4\xD9."), item_drop_bonus);
|
|
}
|
|
if (gold_drop_bonus)
|
|
{
|
|
ch->ChatPacket(CHAT_TYPE_NOTICE,
|
|
LC_TEXT("\xB0\xF1\xB5\xE5 \xB5\xE5\xB7\xD3\xB7\xFC %d%% \xC3\xDF\xB0\xA1 \xC0\xCC\xBA\xA5\xC6\xAE \xC1\xDF\xC0\xD4\xB4\xCF\xB4\xD9."), gold_drop_bonus);
|
|
}
|
|
if (gold10_drop_bonus)
|
|
{
|
|
ch->ChatPacket(CHAT_TYPE_NOTICE,
|
|
LC_TEXT("\xB4\xEB\xB9\xDA\xB0\xF1\xB5\xE5 \xB5\xE5\xB7\xD3\xB7\xFC %d%% \xC3\xDF\xB0\xA1 \xC0\xCC\xBA\xA5\xC6\xAE \xC1\xDF\xC0\xD4\xB4\xCF\xB4\xD9."), gold10_drop_bonus);
|
|
}
|
|
if (exp_bonus)
|
|
{
|
|
ch->ChatPacket(CHAT_TYPE_NOTICE,
|
|
LC_TEXT("\xB0\xE6\xC7\xE8\xC4\xA1 %d%% \xC3\xDF\xB0\xA1 \xC8\xB9\xB5\xE6 \xC0\xCC\xBA\xA5\xC6\xAE \xC1\xDF\xC0\xD4\xB4\xCF\xB4\xD9."), exp_bonus);
|
|
}
|
|
}
|
|
|
|
static bool FN_is_battle_zone(LPCHARACTER ch)
|
|
{
|
|
switch (ch->GetMapIndex())
|
|
{
|
|
case 1: // 신수 1차 마을
|
|
case 2: // 신수 2차 마을
|
|
case 21: // 천조 1차 마을
|
|
case 23: // 천조 2차 마을
|
|
case 41: // 진노 1차 마을
|
|
case 43: // 진노 2차 마을
|
|
case 113: // OX 맵
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void CInputLogin::Login(LPDESC d, const char * data)
|
|
{
|
|
TPacketCGLogin * pinfo = (TPacketCGLogin *) data;
|
|
|
|
char login[LOGIN_MAX_LEN + 1];
|
|
trim_and_lower(pinfo->login, login, sizeof(login));
|
|
|
|
SPDLOG_DEBUG("InputLogin::Login : {}", login);
|
|
|
|
TPacketGCLoginFailure failurePacket;
|
|
|
|
if (g_iUseLocale && !test_server)
|
|
{
|
|
failurePacket.header = HEADER_GC_LOGIN_FAILURE;
|
|
strlcpy(failurePacket.szStatus, "VERSION", sizeof(failurePacket.szStatus));
|
|
d->Packet(&failurePacket, sizeof(TPacketGCLoginFailure));
|
|
return;
|
|
}
|
|
|
|
if (g_bNoMoreClient)
|
|
{
|
|
failurePacket.header = HEADER_GC_LOGIN_FAILURE;
|
|
strlcpy(failurePacket.szStatus, "SHUTDOWN", sizeof(failurePacket.szStatus));
|
|
d->Packet(&failurePacket, sizeof(TPacketGCLoginFailure));
|
|
return;
|
|
}
|
|
|
|
if (g_iUserLimit > 0)
|
|
{
|
|
int iTotal;
|
|
int * paiEmpireUserCount;
|
|
int iLocal;
|
|
|
|
DESC_MANAGER::instance().GetUserCount(iTotal, &paiEmpireUserCount, iLocal);
|
|
|
|
if (g_iUserLimit <= iTotal)
|
|
{
|
|
failurePacket.header = HEADER_GC_LOGIN_FAILURE;
|
|
strlcpy(failurePacket.szStatus, "FULL", sizeof(failurePacket.szStatus));
|
|
d->Packet(&failurePacket, sizeof(TPacketGCLoginFailure));
|
|
return;
|
|
}
|
|
}
|
|
|
|
TLoginPacket login_packet;
|
|
|
|
strlcpy(login_packet.login, login, sizeof(login_packet.login));
|
|
strlcpy(login_packet.passwd, pinfo->passwd, sizeof(login_packet.passwd));
|
|
|
|
db_clientdesc->DBPacket(HEADER_GD_LOGIN, d->GetHandle(), &login_packet, sizeof(TLoginPacket));
|
|
}
|
|
|
|
void CInputLogin::LoginByKey(LPDESC d, const char * data)
|
|
{
|
|
TPacketCGLogin2 * pinfo = (TPacketCGLogin2 *) data;
|
|
|
|
char login[LOGIN_MAX_LEN + 1];
|
|
trim_and_lower(pinfo->login, login, sizeof(login));
|
|
|
|
if (g_bNoMoreClient)
|
|
{
|
|
TPacketGCLoginFailure failurePacket;
|
|
|
|
failurePacket.header = HEADER_GC_LOGIN_FAILURE;
|
|
strlcpy(failurePacket.szStatus, "SHUTDOWN", sizeof(failurePacket.szStatus));
|
|
d->Packet(&failurePacket, sizeof(TPacketGCLoginFailure));
|
|
return;
|
|
}
|
|
|
|
if (g_iUserLimit > 0)
|
|
{
|
|
int iTotal;
|
|
int * paiEmpireUserCount;
|
|
int iLocal;
|
|
|
|
DESC_MANAGER::instance().GetUserCount(iTotal, &paiEmpireUserCount, iLocal);
|
|
|
|
if (g_iUserLimit <= iTotal)
|
|
{
|
|
TPacketGCLoginFailure failurePacket;
|
|
|
|
failurePacket.header = HEADER_GC_LOGIN_FAILURE;
|
|
strlcpy(failurePacket.szStatus, "FULL", sizeof(failurePacket.szStatus));
|
|
|
|
d->Packet(&failurePacket, sizeof(TPacketGCLoginFailure));
|
|
return;
|
|
}
|
|
}
|
|
|
|
SPDLOG_DEBUG("LOGIN_BY_KEY: {} key {}", login, pinfo->dwLoginKey);
|
|
|
|
d->SetLoginKey(pinfo->dwLoginKey);
|
|
|
|
TPacketGDLoginByKey ptod;
|
|
|
|
strlcpy(ptod.szLogin, login, sizeof(ptod.szLogin));
|
|
ptod.dwLoginKey = pinfo->dwLoginKey;
|
|
memcpy(ptod.adwClientKey, pinfo->adwClientKey, sizeof(DWORD) * 4);
|
|
strlcpy(ptod.szIP, d->GetHostName(), sizeof(ptod.szIP));
|
|
|
|
db_clientdesc->DBPacket(HEADER_GD_LOGIN_BY_KEY, d->GetHandle(), &ptod, sizeof(TPacketGDLoginByKey));
|
|
}
|
|
|
|
void CInputLogin::ChangeName(LPDESC d, const char * data)
|
|
{
|
|
TPacketCGChangeName * p = (TPacketCGChangeName *) data;
|
|
const TAccountTable & c_r = d->GetAccountTable();
|
|
|
|
if (!c_r.id)
|
|
{
|
|
SPDLOG_ERROR("no account table");
|
|
return;
|
|
}
|
|
|
|
if (!c_r.players[p->index].bChangeName)
|
|
return;
|
|
|
|
if (!check_name(p->name))
|
|
{
|
|
TPacketGCCreateFailure pack;
|
|
pack.header = HEADER_GC_CHARACTER_CREATE_FAILURE;
|
|
pack.bType = 0;
|
|
d->Packet(&pack, sizeof(pack));
|
|
return;
|
|
}
|
|
|
|
TPacketGDChangeName pdb;
|
|
|
|
pdb.pid = c_r.players[p->index].dwID;
|
|
strlcpy(pdb.name, p->name, sizeof(pdb.name));
|
|
db_clientdesc->DBPacket(HEADER_GD_CHANGE_NAME, d->GetHandle(), &pdb, sizeof(TPacketGDChangeName));
|
|
}
|
|
|
|
void CInputLogin::CharacterSelect(LPDESC d, const char * data)
|
|
{
|
|
struct command_player_select * pinfo = (struct command_player_select *) data;
|
|
const TAccountTable & c_r = d->GetAccountTable();
|
|
|
|
SPDLOG_DEBUG("player_select: login: {} index: {}", c_r.login, pinfo->index);
|
|
|
|
if (!c_r.id)
|
|
{
|
|
SPDLOG_ERROR("no account table");
|
|
return;
|
|
}
|
|
|
|
if (pinfo->index >= PLAYER_PER_ACCOUNT)
|
|
{
|
|
SPDLOG_ERROR("index overflow {}, login: {}", pinfo->index, c_r.login);
|
|
return;
|
|
}
|
|
|
|
if (c_r.players[pinfo->index].bChangeName)
|
|
{
|
|
SPDLOG_ERROR("name must be changed idx {}, login {}, name {}",
|
|
pinfo->index, c_r.login, c_r.players[pinfo->index].szName);
|
|
return;
|
|
}
|
|
|
|
TPlayerLoadPacket player_load_packet;
|
|
|
|
player_load_packet.account_id = c_r.id;
|
|
player_load_packet.player_id = c_r.players[pinfo->index].dwID;
|
|
player_load_packet.account_index = pinfo->index;
|
|
|
|
db_clientdesc->DBPacket(HEADER_GD_PLAYER_LOAD, d->GetHandle(), &player_load_packet, sizeof(TPlayerLoadPacket));
|
|
}
|
|
|
|
bool NewPlayerTable(TPlayerTable * table,
|
|
const char * name,
|
|
BYTE job,
|
|
BYTE shape,
|
|
BYTE bEmpire,
|
|
BYTE bCon,
|
|
BYTE bInt,
|
|
BYTE bStr,
|
|
BYTE bDex)
|
|
{
|
|
if (job >= JOB_MAX_NUM)
|
|
return false;
|
|
|
|
memset(table, 0, sizeof(TPlayerTable));
|
|
|
|
strlcpy(table->name, name, sizeof(table->name));
|
|
|
|
table->level = 1;
|
|
table->job = job;
|
|
table->voice = 0;
|
|
table->part_base = shape;
|
|
|
|
table->st = JobInitialPoints[job].st;
|
|
table->dx = JobInitialPoints[job].dx;
|
|
table->ht = JobInitialPoints[job].ht;
|
|
table->iq = JobInitialPoints[job].iq;
|
|
|
|
table->hp = JobInitialPoints[job].max_hp + table->ht * JobInitialPoints[job].hp_per_ht;
|
|
table->sp = JobInitialPoints[job].max_sp + table->iq * JobInitialPoints[job].sp_per_iq;
|
|
table->stamina = JobInitialPoints[job].max_stamina;
|
|
|
|
table->x = CREATE_START_X(bEmpire) + Random::get(-300, 300);
|
|
table->y = CREATE_START_Y(bEmpire) + Random::get(-300, 300);
|
|
table->z = 0;
|
|
table->dir = 0;
|
|
table->playtime = 0;
|
|
table->gold = 0;
|
|
|
|
table->skill_group = 0;
|
|
|
|
if (china_event_server)
|
|
{
|
|
table->level = 35;
|
|
|
|
for (int i = 1; i < 35; ++i)
|
|
{
|
|
int iHP = Random::get(JobInitialPoints[job].hp_per_lv_begin, JobInitialPoints[job].hp_per_lv_end);
|
|
int iSP = Random::get(JobInitialPoints[job].sp_per_lv_begin, JobInitialPoints[job].sp_per_lv_end);
|
|
table->sRandomHP += iHP;
|
|
table->sRandomSP += iSP;
|
|
table->stat_point += 3;
|
|
}
|
|
|
|
table->hp += table->sRandomHP;
|
|
table->sp += table->sRandomSP;
|
|
|
|
table->gold = 1000000;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool RaceToJob(unsigned race, unsigned* ret_job)
|
|
{
|
|
*ret_job = 0;
|
|
|
|
if (race >= MAIN_RACE_MAX_NUM)
|
|
return false;
|
|
|
|
switch (race)
|
|
{
|
|
case MAIN_RACE_WARRIOR_M:
|
|
*ret_job = JOB_WARRIOR;
|
|
break;
|
|
|
|
case MAIN_RACE_WARRIOR_W:
|
|
*ret_job = JOB_WARRIOR;
|
|
break;
|
|
|
|
case MAIN_RACE_ASSASSIN_M:
|
|
*ret_job = JOB_ASSASSIN;
|
|
break;
|
|
|
|
case MAIN_RACE_ASSASSIN_W:
|
|
*ret_job = JOB_ASSASSIN;
|
|
break;
|
|
|
|
case MAIN_RACE_SURA_M:
|
|
*ret_job = JOB_SURA;
|
|
break;
|
|
|
|
case MAIN_RACE_SURA_W:
|
|
*ret_job = JOB_SURA;
|
|
break;
|
|
|
|
case MAIN_RACE_SHAMAN_M:
|
|
*ret_job = JOB_SHAMAN;
|
|
break;
|
|
|
|
case MAIN_RACE_SHAMAN_W:
|
|
*ret_job = JOB_SHAMAN;
|
|
break;
|
|
|
|
default:
|
|
return false;
|
|
break;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
// 신규 캐릭터 지원
|
|
bool NewPlayerTable2(TPlayerTable * table, const char * name, BYTE race, BYTE shape, BYTE bEmpire)
|
|
{
|
|
if (race >= MAIN_RACE_MAX_NUM)
|
|
{
|
|
SPDLOG_ERROR("NewPlayerTable2.OUT_OF_RACE_RANGE({} >= max({}))", race, (int) MAIN_RACE_MAX_NUM);
|
|
return false;
|
|
}
|
|
|
|
unsigned job;
|
|
|
|
if (!RaceToJob(race, &job))
|
|
{
|
|
SPDLOG_ERROR("NewPlayerTable2.RACE_TO_JOB_ERROR({})", race);
|
|
return false;
|
|
}
|
|
|
|
SPDLOG_DEBUG("NewPlayerTable2(name={}, race={}, job={})", name, race, job);
|
|
|
|
memset(table, 0, sizeof(TPlayerTable));
|
|
|
|
strlcpy(table->name, name, sizeof(table->name));
|
|
|
|
table->level = 1;
|
|
table->job = race; // 직업대신 종족을 넣는다
|
|
table->voice = 0;
|
|
table->part_base = shape;
|
|
|
|
table->st = JobInitialPoints[job].st;
|
|
table->dx = JobInitialPoints[job].dx;
|
|
table->ht = JobInitialPoints[job].ht;
|
|
table->iq = JobInitialPoints[job].iq;
|
|
|
|
table->hp = JobInitialPoints[job].max_hp + table->ht * JobInitialPoints[job].hp_per_ht;
|
|
table->sp = JobInitialPoints[job].max_sp + table->iq * JobInitialPoints[job].sp_per_iq;
|
|
table->stamina = JobInitialPoints[job].max_stamina;
|
|
|
|
table->x = CREATE_START_X(bEmpire) + Random::get(-300, 300);
|
|
table->y = CREATE_START_Y(bEmpire) + Random::get(-300, 300);
|
|
table->z = 0;
|
|
table->dir = 0;
|
|
table->playtime = 0;
|
|
table->gold = 0;
|
|
|
|
table->skill_group = 0;
|
|
|
|
return true;
|
|
}
|
|
|
|
void CInputLogin::CharacterCreate(LPDESC d, const char * data)
|
|
{
|
|
struct command_player_create * pinfo = (struct command_player_create *) data;
|
|
TPlayerCreatePacket player_create_packet;
|
|
|
|
SPDLOG_DEBUG("PlayerCreate: name {} pos {} job {} shape {}",
|
|
pinfo->name,
|
|
pinfo->index,
|
|
pinfo->job,
|
|
pinfo->shape);
|
|
|
|
TPacketGCLoginFailure packFailure;
|
|
memset(&packFailure, 0, sizeof(packFailure));
|
|
packFailure.header = HEADER_GC_CHARACTER_CREATE_FAILURE;
|
|
|
|
if (true == g_BlockCharCreation)
|
|
{
|
|
d->Packet(&packFailure, sizeof(packFailure));
|
|
return;
|
|
}
|
|
|
|
// 사용할 수 없는 이름이거나, 잘못된 평상복이면 생설 실패
|
|
if (!check_name(pinfo->name) || pinfo->shape > 1)
|
|
{
|
|
if (LC_IsCanada() == true)
|
|
{
|
|
TPacketGCCreateFailure pack;
|
|
pack.header = HEADER_GC_CHARACTER_CREATE_FAILURE;
|
|
pack.bType = 1;
|
|
|
|
d->Packet(&pack, sizeof(pack));
|
|
return;
|
|
}
|
|
|
|
d->Packet(&packFailure, sizeof(packFailure));
|
|
return;
|
|
}
|
|
|
|
if (LC_IsEurope() == true)
|
|
{
|
|
const TAccountTable & c_rAccountTable = d->GetAccountTable();
|
|
|
|
if (0 == strcmp(c_rAccountTable.login, pinfo->name))
|
|
{
|
|
TPacketGCCreateFailure pack;
|
|
pack.header = HEADER_GC_CHARACTER_CREATE_FAILURE;
|
|
pack.bType = 1;
|
|
|
|
d->Packet(&pack, sizeof(pack));
|
|
return;
|
|
}
|
|
}
|
|
|
|
memset(&player_create_packet, 0, sizeof(TPlayerCreatePacket));
|
|
|
|
if (!NewPlayerTable2(&player_create_packet.player_table, pinfo->name, pinfo->job, pinfo->shape, d->GetEmpire()))
|
|
{
|
|
SPDLOG_ERROR("player_prototype error: job {} face {} ", pinfo->job);
|
|
d->Packet(&packFailure, sizeof(packFailure));
|
|
return;
|
|
}
|
|
|
|
const TAccountTable & c_rAccountTable = d->GetAccountTable();
|
|
|
|
trim_and_lower(c_rAccountTable.login, player_create_packet.login, sizeof(player_create_packet.login));
|
|
strlcpy(player_create_packet.passwd, c_rAccountTable.passwd, sizeof(player_create_packet.passwd));
|
|
|
|
player_create_packet.account_id = c_rAccountTable.id;
|
|
player_create_packet.account_index = pinfo->index;
|
|
|
|
SPDLOG_DEBUG("PlayerCreate: name {} account_id {}, TPlayerCreatePacketSize({}), Packet->Gold {}",
|
|
pinfo->name,
|
|
pinfo->index,
|
|
sizeof(TPlayerCreatePacket),
|
|
player_create_packet.player_table.gold);
|
|
|
|
db_clientdesc->DBPacket(HEADER_GD_PLAYER_CREATE, d->GetHandle(), &player_create_packet, sizeof(TPlayerCreatePacket));
|
|
}
|
|
|
|
void CInputLogin::CharacterDelete(LPDESC d, const char * data)
|
|
{
|
|
struct command_player_delete * pinfo = (struct command_player_delete *) data;
|
|
const TAccountTable & c_rAccountTable = d->GetAccountTable();
|
|
|
|
if (!c_rAccountTable.id)
|
|
{
|
|
SPDLOG_ERROR("PlayerDelete: no login data");
|
|
return;
|
|
}
|
|
|
|
SPDLOG_DEBUG("PlayerDelete: login: {} index: {}, social_id {}", c_rAccountTable.login, pinfo->index, pinfo->private_code);
|
|
|
|
if (pinfo->index >= PLAYER_PER_ACCOUNT)
|
|
{
|
|
SPDLOG_ERROR("PlayerDelete: index overflow {}, login: {}", pinfo->index, c_rAccountTable.login);
|
|
return;
|
|
}
|
|
|
|
if (!c_rAccountTable.players[pinfo->index].dwID)
|
|
{
|
|
SPDLOG_ERROR("PlayerDelete: Wrong Social ID index {}, login: {}", pinfo->index, c_rAccountTable.login);
|
|
d->Packet(encode_byte(HEADER_GC_CHARACTER_DELETE_WRONG_SOCIAL_ID), 1);
|
|
return;
|
|
}
|
|
|
|
TPlayerDeletePacket player_delete_packet;
|
|
|
|
trim_and_lower(c_rAccountTable.login, player_delete_packet.login, sizeof(player_delete_packet.login));
|
|
player_delete_packet.player_id = c_rAccountTable.players[pinfo->index].dwID;
|
|
player_delete_packet.account_index = pinfo->index;
|
|
strlcpy(player_delete_packet.private_code, pinfo->private_code, sizeof(player_delete_packet.private_code));
|
|
|
|
db_clientdesc->DBPacket(HEADER_GD_PLAYER_DELETE, d->GetHandle(), &player_delete_packet, sizeof(TPlayerDeletePacket));
|
|
}
|
|
|
|
#pragma pack(1)
|
|
typedef struct SPacketGTLogin
|
|
{
|
|
BYTE header;
|
|
WORD empty;
|
|
DWORD id;
|
|
} TPacketGTLogin;
|
|
#pragma pack()
|
|
|
|
void CInputLogin::Entergame(LPDESC d, const char * data)
|
|
{
|
|
LPCHARACTER ch;
|
|
|
|
if (!(ch = d->GetCharacter()))
|
|
{
|
|
d->SetPhase(PHASE_CLOSE);
|
|
return;
|
|
}
|
|
|
|
PIXEL_POSITION pos = ch->GetXYZ();
|
|
|
|
if (!SECTREE_MANAGER::instance().GetMovablePosition(ch->GetMapIndex(), pos.x, pos.y, pos))
|
|
{
|
|
PIXEL_POSITION pos2;
|
|
SECTREE_MANAGER::instance().GetRecallPositionByEmpire(ch->GetMapIndex(), ch->GetEmpire(), pos2);
|
|
|
|
SPDLOG_ERROR("!GetMovablePosition (name {} {}x{} map {} changed to {}x{})",
|
|
ch->GetName(),
|
|
pos.x, pos.y,
|
|
ch->GetMapIndex(),
|
|
pos2.x, pos2.y);
|
|
pos = pos2;
|
|
}
|
|
|
|
CGuildManager::instance().LoginMember(ch);
|
|
|
|
// 캐릭터를 맵에 추가
|
|
ch->Show(ch->GetMapIndex(), pos.x, pos.y, pos.z);
|
|
|
|
SECTREE_MANAGER::instance().SendNPCPosition(ch);
|
|
ch->ReviveInvisible(5);
|
|
|
|
d->SetPhase(PHASE_GAME);
|
|
|
|
if(ch->GetItemAward_cmd()) //게임페이즈 들어가면
|
|
quest::CQuestManager::instance().ItemInformer(ch->GetPlayerID(),ch->GetItemAward_vnum()); //questmanager 호출
|
|
|
|
SPDLOG_DEBUG("ENTERGAME: {} {}x{}x{} {} map_index {}",
|
|
ch->GetName(), ch->GetX(), ch->GetY(), ch->GetZ(), d->GetHostName(), ch->GetMapIndex());
|
|
|
|
if (ch->GetHorseLevel() > 0)
|
|
{
|
|
ch->EnterHorse();
|
|
}
|
|
|
|
// 플레이시간 레코딩 시작
|
|
ch->ResetPlayTime();
|
|
|
|
// 자동 저장 이벤트 추가
|
|
ch->StartSaveEvent();
|
|
ch->StartRecoveryEvent();
|
|
ch->StartCheckSpeedHackEvent();
|
|
|
|
CPVPManager::instance().Connect(ch);
|
|
CPVPManager::instance().SendList(d);
|
|
|
|
MessengerManager::instance().Login(ch->GetName());
|
|
|
|
CPartyManager::instance().SetParty(ch);
|
|
CGuildManager::instance().SendGuildWar(ch);
|
|
|
|
building::CManager::instance().SendLandList(d, ch->GetMapIndex());
|
|
|
|
marriage::CManager::instance().Login(ch);
|
|
|
|
TPacketGCTime p;
|
|
p.bHeader = HEADER_GC_TIME;
|
|
p.time = get_global_time();
|
|
d->Packet(&p, sizeof(p));
|
|
|
|
TPacketGCChannel p2;
|
|
p2.header = HEADER_GC_CHANNEL;
|
|
p2.channel = g_bChannel;
|
|
d->Packet(&p2, sizeof(p2));
|
|
|
|
_send_bonus_info(ch);
|
|
|
|
for (int i = 0; i <= PREMIUM_MAX_NUM; ++i)
|
|
{
|
|
int remain = ch->GetPremiumRemainSeconds(i);
|
|
|
|
if (remain <= 0)
|
|
continue;
|
|
|
|
ch->AddAffect(AFFECT_PREMIUM_START + i, POINT_NONE, 0, 0, remain, 0, true);
|
|
SPDLOG_DEBUG("PREMIUM: {} type {} {}min", ch->GetName(), i, remain);
|
|
}
|
|
|
|
if (LC_IsEurope())
|
|
{
|
|
if (g_bCheckClientVersion)
|
|
{
|
|
int version = atoi(g_stClientVersion.c_str());
|
|
int date = atoi(d->GetClientVersion());
|
|
|
|
SPDLOG_DEBUG("VERSION CHECK {} {} {} {}", version, date, g_stClientVersion.c_str(), d->GetClientVersion());
|
|
|
|
if (!d->GetClientVersion())
|
|
{
|
|
d->DelayedDisconnect(10);
|
|
}
|
|
else
|
|
{
|
|
//if (0 != g_stClientVersion.compare(d->GetClientVersion()))
|
|
if (version > date)
|
|
{
|
|
ch->ChatPacket(CHAT_TYPE_NOTICE, LC_TEXT("\xC5\xAC\xB6\xF3\xC0\xCC\xBE\xF0\xC6\xAE \xB9\xF6\xC0\xFC\xC0\xCC \xC6\xB2\xB7\xC1 \xB7\xCE\xB1\xD7\xBE\xC6\xBF\xF4 \xB5\xCB\xB4\xCF\xB4\xD9. \xC1\xA4\xBB\xF3\xC0\xFB\xC0\xB8\xB7\xCE \xC6\xD0\xC4\xA1 \xC8\xC4 \xC1\xA2\xBC\xD3\xC7\xCF\xBC\xBC\xBF\xE4."));
|
|
d->DelayedDisconnect(10);
|
|
LogManager::instance().HackLog("VERSION_CONFLICT", ch);
|
|
|
|
SPDLOG_WARN("VERSION : WRONG VERSION USER : account:{} name:{} hostName:{} server_version:{} client_version:{}",
|
|
d->GetAccountTable().login,
|
|
ch->GetName(),
|
|
d->GetHostName(),
|
|
g_stClientVersion.c_str(),
|
|
d->GetClientVersion());
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
SPDLOG_WARN("VERSION : NO CHECK");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
SPDLOG_WARN("VERSION : NO LOGIN");
|
|
}
|
|
|
|
if (LC_IsEurope() == true)
|
|
{
|
|
if (ch->IsGM() == true)
|
|
ch->ChatPacket(CHAT_TYPE_COMMAND, "ConsoleEnable");
|
|
}
|
|
|
|
if (ch->GetMapIndex() >= 10000)
|
|
{
|
|
if (CWarMapManager::instance().IsWarMap(ch->GetMapIndex()))
|
|
ch->SetWarMap(CWarMapManager::instance().Find(ch->GetMapIndex()));
|
|
else if (marriage::WeddingManager::instance().IsWeddingMap(ch->GetMapIndex()))
|
|
ch->SetWeddingMap(marriage::WeddingManager::instance().Find(ch->GetMapIndex()));
|
|
else {
|
|
ch->SetDungeon(CDungeonManager::instance().FindByMapIndex(ch->GetMapIndex()));
|
|
}
|
|
}
|
|
else if (CArenaManager::instance().IsArenaMap(ch->GetMapIndex()) == true)
|
|
{
|
|
int memberFlag = CArenaManager::instance().IsMember(ch->GetMapIndex(), ch->GetPlayerID());
|
|
if (memberFlag == MEMBER_OBSERVER)
|
|
{
|
|
ch->SetObserverMode(true);
|
|
ch->SetArenaObserverMode(true);
|
|
if (CArenaManager::instance().RegisterObserverPtr(ch, ch->GetMapIndex(), ch->GetX()/100, ch->GetY()/100))
|
|
{
|
|
SPDLOG_ERROR("ARENA : Observer add failed");
|
|
}
|
|
|
|
if (ch->IsHorseRiding() == true)
|
|
{
|
|
ch->StopRiding();
|
|
ch->HorseSummon(false);
|
|
}
|
|
}
|
|
else if (memberFlag == MEMBER_DUELIST)
|
|
{
|
|
TPacketGCDuelStart duelStart;
|
|
duelStart.header = HEADER_GC_DUEL_START;
|
|
duelStart.wSize = sizeof(TPacketGCDuelStart);
|
|
|
|
ch->GetDesc()->Packet(&duelStart, sizeof(TPacketGCDuelStart));
|
|
|
|
if (ch->IsHorseRiding() == true)
|
|
{
|
|
ch->StopRiding();
|
|
ch->HorseSummon(false);
|
|
}
|
|
|
|
LPPARTY pParty = ch->GetParty();
|
|
if (pParty != NULL)
|
|
{
|
|
if (pParty->GetMemberCount() == 2)
|
|
{
|
|
CPartyManager::instance().DeleteParty(pParty);
|
|
}
|
|
else
|
|
{
|
|
pParty->Quit(ch->GetPlayerID());
|
|
}
|
|
}
|
|
}
|
|
else if (memberFlag == MEMBER_NO)
|
|
{
|
|
if (ch->GetGMLevel() == GM_PLAYER)
|
|
ch->WarpSet(EMPIRE_START_X(ch->GetEmpire()), EMPIRE_START_Y(ch->GetEmpire()));
|
|
}
|
|
else
|
|
{
|
|
// wtf
|
|
}
|
|
}
|
|
else if (ch->GetMapIndex() == 113)
|
|
{
|
|
// ox 이벤트 맵
|
|
if (COXEventManager::instance().Enter(ch) == false)
|
|
{
|
|
// ox 맵 진입 허가가 나지 않음. 플레이어면 마을로 보내자
|
|
if (ch->GetGMLevel() == GM_PLAYER)
|
|
ch->WarpSet(EMPIRE_START_X(ch->GetEmpire()), EMPIRE_START_Y(ch->GetEmpire()));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (CWarMapManager::instance().IsWarMap(ch->GetMapIndex()) ||
|
|
marriage::WeddingManager::instance().IsWeddingMap(ch->GetMapIndex()))
|
|
{
|
|
if (!test_server)
|
|
ch->WarpSet(EMPIRE_START_X(ch->GetEmpire()), EMPIRE_START_Y(ch->GetEmpire()));
|
|
}
|
|
}
|
|
|
|
// 청소년 보호
|
|
if (g_TeenDesc) // RawPacket 사용 금지
|
|
{
|
|
TPacketGTLogin p;
|
|
|
|
p.header = HEADER_GT_LOGIN;
|
|
p.empty = 0;
|
|
p.id = d->GetAccountTable().id;
|
|
|
|
g_TeenDesc->Packet(&p, sizeof(p));
|
|
SPDLOG_DEBUG("TEEN_SEND: ({}, {})", d->GetAccountTable().id, ch->GetName());
|
|
}
|
|
|
|
if (ch->GetHorseLevel() > 0)
|
|
{
|
|
DWORD pid = ch->GetPlayerID();
|
|
|
|
if (pid != 0 && CHorseNameManager::instance().GetHorseName(pid) == NULL)
|
|
db_clientdesc->DBPacket(HEADER_GD_REQ_HORSE_NAME, 0, &pid, sizeof(DWORD));
|
|
}
|
|
|
|
// 중립맵에 들어갔을때 안내하기
|
|
if (g_noticeBattleZone)
|
|
{
|
|
if (FN_is_battle_zone(ch))
|
|
{
|
|
ch->ChatPacket(CHAT_TYPE_NOTICE, LC_TEXT("\xC0\xCC \xB8\xCA\xBF\xA1\xBC\xB1 \xB0\xAD\xC1\xA6\xC0\xFB\xC0\xCE \xB4\xEB\xC0\xFC\xC0\xCC \xC0\xD6\xC0\xBB\xBC\xF6 \xB5\xB5 \xC0\xD6\xBD\xC0\xB4\xCF\xB4\xD9."));
|
|
ch->ChatPacket(CHAT_TYPE_NOTICE, LC_TEXT("\xC0\xCC \xC1\xB6\xC7\xD7\xBF\xA1 \xB5\xBF\xC0\xC7\xC7\xCF\xC1\xF6 \xBE\xCA\xC0\xBB\xBD\xC3"));
|
|
ch->ChatPacket(CHAT_TYPE_NOTICE, LC_TEXT("\xBA\xBB\xC0\xCE\xC0\xC7 \xC1\xD6\xBC\xBA \xB9\xD7 \xBA\xCE\xBC\xBA\xC0\xB8\xB7\xCE \xB5\xB9\xBE\xC6\xB0\xA1\xBD\xC3\xB1\xE2 \xB9\xD9\xB6\xF8\xB4\xCF\xB4\xD9."));
|
|
}
|
|
}
|
|
}
|
|
|
|
void CInputLogin::Empire(LPDESC d, const char * c_pData)
|
|
{
|
|
const TPacketCGEmpire* p = reinterpret_cast<const TPacketCGEmpire*>(c_pData);
|
|
|
|
if (EMPIRE_MAX_NUM <= p->bEmpire)
|
|
{
|
|
d->SetPhase(PHASE_CLOSE);
|
|
return;
|
|
}
|
|
|
|
const TAccountTable& r = d->GetAccountTable();
|
|
|
|
if (r.bEmpire != 0)
|
|
{
|
|
for (int i = 0; i < PLAYER_PER_ACCOUNT; ++i)
|
|
{
|
|
if (0 != r.players[i].dwID)
|
|
{
|
|
SPDLOG_ERROR("EmpireSelectFailed {}", r.players[i].dwID);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
TEmpireSelectPacket pd;
|
|
|
|
pd.dwAccountID = r.id;
|
|
pd.bEmpire = p->bEmpire;
|
|
|
|
db_clientdesc->DBPacket(HEADER_GD_EMPIRE_SELECT, d->GetHandle(), &pd, sizeof(pd));
|
|
}
|
|
|
|
int CInputLogin::GuildSymbolUpload(LPDESC d, const char* c_pData, size_t uiBytes)
|
|
{
|
|
if (uiBytes < sizeof(TPacketCGGuildSymbolUpload))
|
|
return -1;
|
|
|
|
SPDLOG_DEBUG("GuildSymbolUpload uiBytes {}", uiBytes);
|
|
|
|
TPacketCGGuildSymbolUpload* p = (TPacketCGGuildSymbolUpload*) c_pData;
|
|
|
|
if (uiBytes < p->size)
|
|
return -1;
|
|
|
|
int iSymbolSize = p->size - sizeof(TPacketCGGuildSymbolUpload);
|
|
|
|
if (iSymbolSize <= 0 || iSymbolSize > 64 * 1024)
|
|
{
|
|
// 64k 보다 큰 길드 심볼은 올릴수없다
|
|
// 접속을 끊고 무시
|
|
d->SetPhase(PHASE_CLOSE);
|
|
return 0;
|
|
}
|
|
|
|
// 땅을 소유하지 않은 길드인 경우.
|
|
if (!test_server)
|
|
if (!building::CManager::instance().FindLandByGuild(p->guild_id))
|
|
{
|
|
d->SetPhase(PHASE_CLOSE);
|
|
return 0;
|
|
}
|
|
|
|
SPDLOG_DEBUG("GuildSymbolUpload Do Upload {:02X}{:02X}{:02X}{:02X} {}", c_pData[7], c_pData[8], c_pData[9], c_pData[10], sizeof(*p));
|
|
|
|
CGuildMarkManager::instance().UploadSymbol(p->guild_id, iSymbolSize, (const BYTE*)(c_pData + sizeof(*p)));
|
|
CGuildMarkManager::instance().SaveSymbol(GUILD_SYMBOL_FILENAME);
|
|
return iSymbolSize;
|
|
}
|
|
|
|
void CInputLogin::GuildSymbolCRC(LPDESC d, const char* c_pData)
|
|
{
|
|
const TPacketCGSymbolCRC & CGPacket = *((TPacketCGSymbolCRC *) c_pData);
|
|
|
|
SPDLOG_DEBUG("GuildSymbolCRC {} {} {}", CGPacket.guild_id, CGPacket.crc, CGPacket.size);
|
|
|
|
const CGuildMarkManager::TGuildSymbol * pkGS = CGuildMarkManager::instance().GetGuildSymbol(CGPacket.guild_id);
|
|
|
|
if (!pkGS)
|
|
return;
|
|
|
|
SPDLOG_DEBUG(" Server {} {}", pkGS->crc, pkGS->raw.size());
|
|
|
|
if (pkGS->raw.size() != CGPacket.size || pkGS->crc != CGPacket.crc)
|
|
{
|
|
TPacketGCGuildSymbolData GCPacket;
|
|
|
|
GCPacket.header = HEADER_GC_SYMBOL_DATA;
|
|
GCPacket.size = sizeof(GCPacket) + pkGS->raw.size();
|
|
GCPacket.guild_id = CGPacket.guild_id;
|
|
|
|
d->RawPacket(&GCPacket, sizeof(GCPacket));
|
|
d->Packet(&pkGS->raw[0], pkGS->raw.size());
|
|
|
|
SPDLOG_DEBUG("SendGuildSymbolHead {:02X}{:02X}{:02X}{:02X} Size {}",
|
|
pkGS->raw[0], pkGS->raw[1], pkGS->raw[2], pkGS->raw[3], pkGS->raw.size());
|
|
}
|
|
}
|
|
|
|
void CInputLogin::GuildMarkUpload(LPDESC d, const char* c_pData)
|
|
{
|
|
TPacketCGMarkUpload * p = (TPacketCGMarkUpload *) c_pData;
|
|
CGuildManager& rkGuildMgr = CGuildManager::instance();
|
|
CGuild * pkGuild;
|
|
|
|
if (!(pkGuild = rkGuildMgr.FindGuild(p->gid)))
|
|
{
|
|
SPDLOG_ERROR("MARK_SERVER: GuildMarkUpload: no guild. gid {}", p->gid);
|
|
return;
|
|
}
|
|
|
|
if (pkGuild->GetLevel() < guild_mark_min_level)
|
|
{
|
|
SPDLOG_DEBUG("MARK_SERVER: GuildMarkUpload: level < {} ({})", guild_mark_min_level, pkGuild->GetLevel());
|
|
return;
|
|
}
|
|
|
|
CGuildMarkManager & rkMarkMgr = CGuildMarkManager::instance();
|
|
|
|
SPDLOG_DEBUG("MARK_SERVER: GuildMarkUpload: gid {}", p->gid);
|
|
|
|
bool isEmpty = true;
|
|
|
|
for (DWORD iPixel = 0; iPixel < SGuildMark::SIZE; ++iPixel)
|
|
if (*((DWORD *) p->image + iPixel) != 0x00000000)
|
|
isEmpty = false;
|
|
|
|
if (isEmpty)
|
|
rkMarkMgr.DeleteMark(p->gid);
|
|
else
|
|
rkMarkMgr.SaveMark(p->gid, p->image);
|
|
}
|
|
|
|
void CInputLogin::GuildMarkIDXList(LPDESC d, const char* c_pData)
|
|
{
|
|
CGuildMarkManager & rkMarkMgr = CGuildMarkManager::instance();
|
|
|
|
DWORD bufSize = sizeof(WORD) * 2 * rkMarkMgr.GetMarkCount();
|
|
char * buf = NULL;
|
|
|
|
if (bufSize > 0)
|
|
{
|
|
buf = (char *) malloc(bufSize);
|
|
rkMarkMgr.CopyMarkIdx(buf);
|
|
}
|
|
|
|
TPacketGCMarkIDXList p;
|
|
p.header = HEADER_GC_MARK_IDXLIST;
|
|
p.bufSize = sizeof(p) + bufSize;
|
|
p.count = rkMarkMgr.GetMarkCount();
|
|
|
|
if (buf)
|
|
{
|
|
d->RawPacket(&p, sizeof(p));
|
|
d->Packet(buf, bufSize);
|
|
free(buf);
|
|
}
|
|
else
|
|
d->Packet(&p, sizeof(p));
|
|
|
|
SPDLOG_DEBUG("MARK_SERVER: GuildMarkIDXList {} bytes sent.", p.bufSize);
|
|
}
|
|
|
|
void CInputLogin::GuildMarkCRCList(LPDESC d, const char* c_pData)
|
|
{
|
|
TPacketCGMarkCRCList * pCG = (TPacketCGMarkCRCList *) c_pData;
|
|
|
|
std::map<BYTE, const SGuildMarkBlock *> mapDiffBlocks;
|
|
CGuildMarkManager::instance().GetDiffBlocks(pCG->imgIdx, pCG->crclist, mapDiffBlocks);
|
|
|
|
DWORD blockCount = 0;
|
|
TEMP_BUFFER buf(1024 * 1024); // 1M 버퍼
|
|
|
|
for (itertype(mapDiffBlocks) it = mapDiffBlocks.begin(); it != mapDiffBlocks.end(); ++it)
|
|
{
|
|
BYTE posBlock = it->first;
|
|
const SGuildMarkBlock & rkBlock = *it->second;
|
|
|
|
buf.write(&posBlock, sizeof(BYTE));
|
|
buf.write(&rkBlock.m_sizeCompBuf, sizeof(DWORD));
|
|
buf.write(rkBlock.m_abCompBuf, rkBlock.m_sizeCompBuf);
|
|
|
|
++blockCount;
|
|
}
|
|
|
|
TPacketGCMarkBlock pGC;
|
|
|
|
pGC.header = HEADER_GC_MARK_BLOCK;
|
|
pGC.imgIdx = pCG->imgIdx;
|
|
pGC.bufSize = buf.size() + sizeof(TPacketGCMarkBlock);
|
|
pGC.count = blockCount;
|
|
|
|
SPDLOG_DEBUG("MARK_SERVER: Sending blocks. (imgIdx {} diff {} size {})", pCG->imgIdx, mapDiffBlocks.size(), pGC.bufSize);
|
|
|
|
if (buf.size() > 0)
|
|
{
|
|
d->RawPacket(&pGC, sizeof(TPacketGCMarkBlock));
|
|
d->Packet(buf.read_peek(), buf.size());
|
|
}
|
|
else
|
|
d->Packet(&pGC, sizeof(TPacketGCMarkBlock));
|
|
}
|
|
|
|
int CInputLogin::Analyze(LPDESC d, BYTE bHeader, const char * c_pData)
|
|
{
|
|
int iExtraLen = 0;
|
|
|
|
switch (bHeader)
|
|
{
|
|
case HEADER_CG_PONG:
|
|
Pong(d);
|
|
break;
|
|
|
|
case HEADER_CG_TIME_SYNC:
|
|
Handshake(d, c_pData);
|
|
break;
|
|
|
|
case HEADER_CG_LOGIN:
|
|
Login(d, c_pData);
|
|
break;
|
|
|
|
case HEADER_CG_LOGIN2:
|
|
LoginByKey(d, c_pData);
|
|
break;
|
|
|
|
case HEADER_CG_CHARACTER_SELECT:
|
|
CharacterSelect(d, c_pData);
|
|
break;
|
|
|
|
case HEADER_CG_CHARACTER_CREATE:
|
|
CharacterCreate(d, c_pData);
|
|
break;
|
|
|
|
case HEADER_CG_CHARACTER_DELETE:
|
|
CharacterDelete(d, c_pData);
|
|
break;
|
|
|
|
case HEADER_CG_ENTERGAME:
|
|
Entergame(d, c_pData);
|
|
break;
|
|
|
|
case HEADER_CG_EMPIRE:
|
|
Empire(d, c_pData);
|
|
break;
|
|
|
|
case HEADER_CG_MOVE:
|
|
break;
|
|
|
|
///////////////////////////////////////
|
|
// Guild Mark
|
|
/////////////////////////////////////
|
|
case HEADER_CG_MARK_CRCLIST:
|
|
GuildMarkCRCList(d, c_pData);
|
|
break;
|
|
|
|
case HEADER_CG_MARK_IDXLIST:
|
|
GuildMarkIDXList(d, c_pData);
|
|
break;
|
|
|
|
case HEADER_CG_MARK_UPLOAD:
|
|
GuildMarkUpload(d, c_pData);
|
|
break;
|
|
|
|
//////////////////////////////////////
|
|
// Guild Symbol
|
|
/////////////////////////////////////
|
|
case HEADER_CG_GUILD_SYMBOL_UPLOAD:
|
|
if ((iExtraLen = GuildSymbolUpload(d, c_pData, m_iBufferLeft)) < 0)
|
|
return -1;
|
|
break;
|
|
|
|
case HEADER_CG_SYMBOL_CRC:
|
|
GuildSymbolCRC(d, c_pData);
|
|
break;
|
|
/////////////////////////////////////
|
|
|
|
case HEADER_CG_CHANGE_NAME:
|
|
ChangeName(d, c_pData);
|
|
break;
|
|
|
|
case HEADER_CG_CLIENT_VERSION:
|
|
Version(d->GetCharacter(), c_pData);
|
|
break;
|
|
|
|
case HEADER_CG_CLIENT_VERSION2:
|
|
Version(d->GetCharacter(), c_pData);
|
|
break;
|
|
|
|
default:
|
|
SPDLOG_ERROR("login phase does not handle this packet! header {}", bHeader);
|
|
//d->SetPhase(PHASE_CLOSE);
|
|
return (0);
|
|
}
|
|
|
|
return (iExtraLen);
|
|
}
|
|
|