1. #include "stdafx.h"
  2. #include "constants.h"
  3. #include "config.h"
  4. #include "utils.h"
  5. #include "desc_client.h"
  6. #include "desc_manager.h"
  7. #include "buffer_manager.h"
  8. #include "packet.h"
  9. #include "protocol.h"
  10. #include "char.h"
  11. #include "char_manager.h"
  12. #include "item.h"
  13. #include "item_manager.h"
  14. #include "cmd.h"
  15. #include "shop.h"
  16. #include "shop_manager.h"
  17. #include "safebox.h"
  18. #include "regen.h"
  19. #include "battle.h"
  20. #include "exchange.h"
  21. #include "questmanager.h"
  22. #include "profiler.h"
  23. #include "messenger_manager.h"
  24. #include "party.h"
  25. #include "p2p.h"
  26. #include "affect.h"
  27. #include "guild.h"
  28. #include "guild_manager.h"
  29. #include "log.h"
  30. #include "banword.h"
  31. #include "empire_text_convert.h"
  32. #include "unique_item.h"
  33. #include "building.h"
  34. #include "locale_service.h"
  35. #include "gm.h"
  36. #include "spam.h"
  37. #include "ani.h"
  38. #include "motion.h"
  39. #include "OXEvent.h"
  40. #include "locale_service.h"
  41. #include "HackShield.h"
  42. #include "XTrapManager.h"
  43. #include "DragonSoul.h"
  44. #include "belt_inventory_helper.h" // @fixme119
  45. #include "../../common/CommonDefines.h"
  46. #include "offline_shop.h"
  47. #include "offlineshop_manager.h"
  48. #include "input.h"
  49. #define ENABLE_CHAT_COLOR_SYSTEM
  50. #define ENABLE_CHAT_LOGGING
  51. #define ENABLE_CHAT_SPAMLIMIT
  52. #define ENABLE_WHISPER_CHAT_SPAMLIMIT
  53. #define ENABLE_CHECK_GHOSTMODE
  54. #ifdef ENABLE_CHAT_LOGGING
  55. static char __escape_string[1024];
  56. static char __escape_string2[1024];
  57. #endif
  58. static int __deposit_limit()
  59. {
  60. return (1000*10000); // 1천만
  61. }
  62. #ifdef __SEND_TARGET_INFO__
  63. void CInputMain::TargetInfoLoad(LPCHARACTER ch, const char* c_pData)
  64. {
  65. TPacketCGTargetInfoLoad* p = (TPacketCGTargetInfoLoad*)c_pData;
  66. TPacketGCTargetInfo pInfo;
  67. pInfo.header = HEADER_GC_TARGET_INFO;
  68. static std::vector<LPITEM> s_vec_item;
  69. s_vec_item.clear();
  70. LPITEM pkInfoItem;
  71. LPCHARACTER m_pkChrTarget = CHARACTER_MANAGER::instance().Find(p->dwVID);
  72. if (!ch || !m_pkChrTarget)
  73. return;
  74. if (ITEM_MANAGER::instance().CreateDropItemVector(m_pkChrTarget, ch, s_vec_item) && (m_pkChrTarget->IsMonster() || m_pkChrTarget->IsStone()))
  75. {
  76. if (s_vec_item.size() == 0);
  77. else if (s_vec_item.size() == 1)
  78. {
  79. pkInfoItem = s_vec_item[0];
  80. pInfo.dwVID = m_pkChrTarget->GetVID();
  81. pInfo.race = m_pkChrTarget->GetRaceNum();
  82. pInfo.dwVnum = pkInfoItem->GetVnum();
  83. pInfo.count = pkInfoItem->GetCount();
  84. ch->GetDesc()->Packet(&pInfo, sizeof(TPacketGCTargetInfo));
  85. }
  86. else
  87. {
  88. int iItemIdx = s_vec_item.size() - 1;
  89. while (iItemIdx >= 0)
  90. {
  91. pkInfoItem = s_vec_item[iItemIdx--];
  92. if (!pkInfoItem)
  93. {
  94. sys_err("pkInfoItem null in vector idx %d", iItemIdx + 1);
  95. continue;
  96. }
  97. pInfo.dwVID = m_pkChrTarget->GetVID();
  98. pInfo.race = m_pkChrTarget->GetRaceNum();
  99. pInfo.dwVnum = pkInfoItem->GetVnum();
  100. pInfo.count = pkInfoItem->GetCount();
  101. ch->GetDesc()->Packet(&pInfo, sizeof(TPacketGCTargetInfo));
  102. }
  103. }
  104. }
  105. }
  106. #endif
  107. void SendBlockChatInfo(LPCHARACTER ch, int sec)
  108. {
  109. if (sec <= 0)
  110. {
  111. ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("채팅 금지 상태입니다."));
  112. return;
  113. }
  114. long hour = sec / 3600;
  115. sec -= hour * 3600;
  116. long min = (sec / 60);
  117. sec -= min * 60;
  118. char buf[128+1];
  119. if (hour > 0 && min > 0)
  120. snprintf(buf, sizeof(buf), LC_TEXT("%d 시간 %d 분 %d 초 동안 채팅금지 상태입니다"), hour, min, sec);
  121. else if (hour > 0 && min == 0)
  122. snprintf(buf, sizeof(buf), LC_TEXT("%d 시간 %d 초 동안 채팅금지 상태입니다"), hour, sec);
  123. else if (hour == 0 && min > 0)
  124. snprintf(buf, sizeof(buf), LC_TEXT("%d 분 %d 초 동안 채팅금지 상태입니다"), min, sec);
  125. else
  126. snprintf(buf, sizeof(buf), LC_TEXT("%d 초 동안 채팅금지 상태입니다"), sec);
  127. ch->ChatPacket(CHAT_TYPE_INFO, buf);
  128. }
  129. EVENTINFO(spam_event_info)
  130. {
  131. char host[MAX_HOST_LENGTH+1];
  132. spam_event_info()
  133. {
  134. ::memset( host, 0, MAX_HOST_LENGTH+1 );
  135. }
  136. };
  137. typedef boost::unordered_map<std::string, std::pair<unsigned int, LPEVENT> > spam_score_of_ip_t;
  138. spam_score_of_ip_t spam_score_of_ip;
  139. EVENTFUNC(block_chat_by_ip_event)
  140. {
  141. spam_event_info* info = dynamic_cast<spam_event_info*>( event->info );
  142. if ( info == NULL )
  143. {
  144. sys_err( "block_chat_by_ip_event> <Factor> Null pointer" );
  145. return 0;
  146. }
  147. const char * host = info->host;
  148. spam_score_of_ip_t::iterator it = spam_score_of_ip.find(host);
  149. if (it != spam_score_of_ip.end())
  150. {
  151. it->second.first = 0;
  152. it->second.second = NULL;
  153. }
  154. return 0;
  155. }
  156. bool SpamBlockCheck(LPCHARACTER ch, const char* const buf, const size_t buflen)
  157. {
  158. if (ch->GetLevel() < g_iSpamBlockMaxLevel)
  159. {
  160. spam_score_of_ip_t::iterator it = spam_score_of_ip.find(ch->GetDesc()->GetHostName());
  161. if (it == spam_score_of_ip.end())
  162. {
  163. spam_score_of_ip.insert(std::make_pair(ch->GetDesc()->GetHostName(), std::make_pair(0, (LPEVENT) NULL)));
  164. it = spam_score_of_ip.find(ch->GetDesc()->GetHostName());
  165. }
  166. if (it->second.second)
  167. {
  168. SendBlockChatInfo(ch, event_time(it->second.second) / passes_per_sec);
  169. return true;
  170. }
  171. unsigned int score;
  172. const char * word = SpamManager::instance().GetSpamScore(buf, buflen, score);
  173. it->second.first += score;
  174. if (word)
  175. sys_log(0, "SPAM_SCORE: %s text: %s score: %u total: %u word: %s", ch->GetName(), buf, score, it->second.first, word);
  176. if (it->second.first >= g_uiSpamBlockScore)
  177. {
  178. spam_event_info* info = AllocEventInfo<spam_event_info>();
  179. strlcpy(info->host, ch->GetDesc()->GetHostName(), sizeof(info->host));
  180. it->second.second = event_create(block_chat_by_ip_event, info, PASSES_PER_SEC(g_uiSpamBlockDuration));
  181. sys_log(0, "SPAM_IP: %s for %u seconds", info->host, g_uiSpamBlockDuration);
  182. LogManager::instance().CharLog(ch, 0, "SPAM", word);
  183. SendBlockChatInfo(ch, event_time(it->second.second) / passes_per_sec);
  184. return true;
  185. }
  186. }
  187. return false;
  188. }
  189. enum
  190. {
  191. TEXT_TAG_PLAIN,
  192. TEXT_TAG_TAG, // ||
  193. TEXT_TAG_COLOR, // |cffffffff
  194. TEXT_TAG_HYPERLINK_START, // |H
  195. TEXT_TAG_HYPERLINK_END, // |h ex) |Hitem:1234:1:1:1|h
  196. TEXT_TAG_RESTORE_COLOR,
  197. };
  198. int GetTextTag(const char * src, int maxLen, int & tagLen, std::string & extraInfo)
  199. {
  200. tagLen = 1;
  201. if (maxLen < 2 || *src != '|')
  202. return TEXT_TAG_PLAIN;
  203. const char * cur = ++src;
  204. if (*cur == '|') // ||는 |로 표시한다.
  205. {
  206. tagLen = 2;
  207. return TEXT_TAG_TAG;
  208. }
  209. else if (*cur == 'c') // color |cffffffffblahblah|r
  210. {
  211. tagLen = 2;
  212. return TEXT_TAG_COLOR;
  213. }
  214. else if (*cur == 'H') // hyperlink |Hitem:10000:0:0:0:0|h[이름]|h
  215. {
  216. tagLen = 2;
  217. return TEXT_TAG_HYPERLINK_START;
  218. }
  219. else if (*cur == 'h') // end of hyperlink
  220. {
  221. tagLen = 2;
  222. return TEXT_TAG_HYPERLINK_END;
  223. }
  224. return TEXT_TAG_PLAIN;
  225. }
  226. void GetTextTagInfo(const char * src, int src_len, int & hyperlinks, bool & colored)
  227. {
  228. colored = false;
  229. hyperlinks = 0;
  230. int len;
  231. std::string extraInfo;
  232. for (int i = 0; i < src_len;)
  233. {
  234. int tag = GetTextTag(&src[i], src_len - i, len, extraInfo);
  235. if (tag == TEXT_TAG_HYPERLINK_START)
  236. ++hyperlinks;
  237. if (tag == TEXT_TAG_COLOR)
  238. colored = true;
  239. i += len;
  240. }
  241. }
  242. int ProcessTextTag(LPCHARACTER ch, const char * c_pszText, size_t len)
  243. {
  244. //개인상점중에 금강경을 사용할 경우
  245. //0 : 정상적으로 사용
  246. //1 : 금강경 부족
  247. //2 : 금강경이 있으나, 개인상점에서 사용중
  248. //3 : 기타
  249. //4 : 에러
  250. int hyperlinks;
  251. bool colored;
  252. GetTextTagInfo(c_pszText, len, hyperlinks, colored);
  253. if (colored == true && hyperlinks == 0)
  254. return 4;
  255. #ifdef ENABLE_NEWSTUFF
  256. if (g_bDisablePrismNeed)
  257. return 0;
  258. #endif
  259. int nPrismCount = ch->CountSpecifyItem(ITEM_PRISM);
  260. if (nPrismCount < hyperlinks)
  261. return 1;
  262. if (!ch->GetMyShop())
  263. {
  264. ch->RemoveSpecifyItem(ITEM_PRISM, hyperlinks);
  265. return 0;
  266. } else
  267. {
  268. int sellingNumber = ch->GetMyShop()->GetNumberByVnum(ITEM_PRISM);
  269. if(nPrismCount - sellingNumber < hyperlinks)
  270. {
  271. return 2;
  272. } else
  273. {
  274. ch->RemoveSpecifyItem(ITEM_PRISM, hyperlinks);
  275. return 0;
  276. }
  277. }
  278. return 4;
  279. }
  280. int CInputMain::Whisper(LPCHARACTER ch, const char * data, size_t uiBytes)
  281. {
  282. const TPacketCGWhisper* pinfo = reinterpret_cast<const TPacketCGWhisper*>(data);
  283. if (uiBytes < pinfo->wSize)
  284. return -1;
  285. int iExtraLen = pinfo->wSize - sizeof(TPacketCGWhisper);
  286. if (iExtraLen < 0)
  287. {
  288. sys_err("invalid packet length (len %d size %u buffer %u)", iExtraLen, pinfo->wSize, uiBytes);
  289. ch->GetDesc()->SetPhase(PHASE_CLOSE);
  290. return -1;
  291. }
  292. #ifdef ENABLE_WHISPER_CHAT_SPAMLIMIT
  293. if (ch->IncreaseChatCounter() >= 10)
  294. {
  295. ch->GetDesc()->DelayedDisconnect(0);
  296. return (iExtraLen);
  297. }
  298. #endif
  299. if (ch->FindAffect(AFFECT_BLOCK_CHAT))
  300. {
  301. ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("채팅 금지 상태입니다."));
  302. return (iExtraLen);
  303. }
  304. LPCHARACTER pkChr = CHARACTER_MANAGER::instance().FindPC(pinfo->szNameTo);
  305. if (pkChr == ch)
  306. return (iExtraLen);
  307. LPDESC pkDesc = NULL;
  308. BYTE bOpponentEmpire = 0;
  309. if (test_server)
  310. {
  311. if (!pkChr)
  312. sys_log(0, "Whisper to %s(%s) from %s", "Null", pinfo->szNameTo, ch->GetName());
  313. else
  314. sys_log(0, "Whisper to %s(%s) from %s", pkChr->GetName(), pinfo->szNameTo, ch->GetName());
  315. }
  316. if (ch->IsBlockMode(BLOCK_WHISPER))
  317. {
  318. if (ch->GetDesc())
  319. {
  320. TPacketGCWhisper pack;
  321. pack.bHeader = HEADER_GC_WHISPER;
  322. pack.bType = WHISPER_TYPE_SENDER_BLOCKED;
  323. pack.wSize = sizeof(TPacketGCWhisper);
  324. strlcpy(pack.szNameFrom, pinfo->szNameTo, sizeof(pack.szNameFrom));
  325. ch->GetDesc()->Packet(&pack, sizeof(pack));
  326. }
  327. return iExtraLen;
  328. }
  329. if (!pkChr)
  330. {
  331. CCI * pkCCI = P2P_MANAGER::instance().Find(pinfo->szNameTo);
  332. if (pkCCI)
  333. {
  334. pkDesc = pkCCI->pkDesc;
  335. pkDesc->SetRelay(pinfo->szNameTo);
  336. bOpponentEmpire = pkCCI->bEmpire;
  337. if (test_server)
  338. sys_log(0, "Whisper to %s from %s (Channel %d Mapindex %d)", "Null", ch->GetName(), pkCCI->bChannel, pkCCI->lMapIndex);
  339. }
  340. }
  341. else
  342. {
  343. pkDesc = pkChr->GetDesc();
  344. bOpponentEmpire = pkChr->GetEmpire();
  345. }
  346. if (!pkDesc)
  347. {
  348. if (ch->GetDesc())
  349. {
  350. TPacketGCWhisper pack;
  351. pack.bHeader = HEADER_GC_WHISPER;
  352. pack.bType = WHISPER_TYPE_NOT_EXIST;
  353. pack.wSize = sizeof(TPacketGCWhisper);
  354. strlcpy(pack.szNameFrom, pinfo->szNameTo, sizeof(pack.szNameFrom));
  355. ch->GetDesc()->Packet(&pack, sizeof(TPacketGCWhisper));
  356. sys_log(0, "WHISPER: no player");
  357. }
  358. }
  359. else
  360. {
  361. if (ch->IsBlockMode(BLOCK_WHISPER))
  362. {
  363. if (ch->GetDesc())
  364. {
  365. TPacketGCWhisper pack;
  366. pack.bHeader = HEADER_GC_WHISPER;
  367. pack.bType = WHISPER_TYPE_SENDER_BLOCKED;
  368. pack.wSize = sizeof(TPacketGCWhisper);
  369. strlcpy(pack.szNameFrom, pinfo->szNameTo, sizeof(pack.szNameFrom));
  370. ch->GetDesc()->Packet(&pack, sizeof(pack));
  371. }
  372. }
  373. else if (pkChr && pkChr->IsBlockMode(BLOCK_WHISPER))
  374. {
  375. if (ch->GetDesc())
  376. {
  377. TPacketGCWhisper pack;
  378. pack.bHeader = HEADER_GC_WHISPER;
  379. pack.bType = WHISPER_TYPE_TARGET_BLOCKED;
  380. pack.wSize = sizeof(TPacketGCWhisper);
  381. strlcpy(pack.szNameFrom, pinfo->szNameTo, sizeof(pack.szNameFrom));
  382. ch->GetDesc()->Packet(&pack, sizeof(pack));
  383. }
  384. }
  385. else
  386. {
  387. BYTE bType = WHISPER_TYPE_NORMAL;
  388. char buf[CHAT_MAX_LEN + 1];
  389. strlcpy(buf, data + sizeof(TPacketCGWhisper), MIN(iExtraLen + 1, sizeof(buf)));
  390. const size_t buflen = strlen(buf);
  391. if (true == SpamBlockCheck(ch, buf, buflen))
  392. {
  393. if (!pkChr)
  394. {
  395. CCI * pkCCI = P2P_MANAGER::instance().Find(pinfo->szNameTo);
  396. if (pkCCI)
  397. {
  398. pkDesc->SetRelay("");
  399. }
  400. }
  401. return iExtraLen;
  402. }
  403. CBanwordManager::instance().ConvertString(buf, buflen);
  404. if (g_bEmpireWhisper)
  405. if (!ch->IsEquipUniqueGroup(UNIQUE_GROUP_RING_OF_LANGUAGE))
  406. if (!(pkChr && pkChr->IsEquipUniqueGroup(UNIQUE_GROUP_RING_OF_LANGUAGE)))
  407. if (bOpponentEmpire != ch->GetEmpire() && ch->GetEmpire() && bOpponentEmpire // 서로 제국이 다르면서
  408. && ch->GetGMLevel() == GM_PLAYER && gm_get_level(pinfo->szNameTo) == GM_PLAYER) // 둘다 일반 플레이어이면
  409. // 이름 밖에 모르니 gm_get_level 함수를 사용
  410. {
  411. if (!pkChr)
  412. {
  413. // 다른 서버에 있으니 제국 표시만 한다. bType의 상위 4비트를 Empire번호로 사용한다.
  414. bType = ch->GetEmpire() << 4;
  415. }
  416. else
  417. {
  418. ConvertEmpireText(ch->GetEmpire(), buf, buflen, 10 + 2 * pkChr->GetSkillPower(SKILL_LANGUAGE1 + ch->GetEmpire() - 1)/*변환확률*/);
  419. }
  420. }
  421. int processReturn = ProcessTextTag(ch, buf, buflen);
  422. if (0!=processReturn)
  423. {
  424. if (ch->GetDesc())
  425. {
  426. TItemTable * pTable = ITEM_MANAGER::instance().GetTable(ITEM_PRISM);
  427. if (pTable)
  428. {
  429. char buf[128];
  430. int len;
  431. if (3==processReturn) //교환중
  432. len = snprintf(buf, sizeof(buf), LC_TEXT("사용할수 없습니다."), pTable->szLocaleName);
  433. else
  434. len = snprintf(buf, sizeof(buf), LC_TEXT("%s이 필요합니다."), pTable->szLocaleName);
  435. if (len < 0 || len >= (int) sizeof(buf))
  436. len = sizeof(buf) - 1;
  437. ++len; // \0 문자 포함
  438. TPacketGCWhisper pack;
  439. pack.bHeader = HEADER_GC_WHISPER;
  440. pack.bType = WHISPER_TYPE_ERROR;
  441. pack.wSize = sizeof(TPacketGCWhisper) + len;
  442. strlcpy(pack.szNameFrom, pinfo->szNameTo, sizeof(pack.szNameFrom));
  443. ch->GetDesc()->BufferedPacket(&pack, sizeof(pack));
  444. ch->GetDesc()->Packet(buf, len);
  445. sys_log(0, "WHISPER: not enough %s: char: %s", pTable->szLocaleName, ch->GetName());
  446. }
  447. }
  448. // 릴래이 상태일 수 있으므로 릴래이를 풀어준다.
  449. pkDesc->SetRelay("");
  450. return (iExtraLen);
  451. }
  452. if (ch->IsGM())
  453. bType = (bType & 0xF0) | WHISPER_TYPE_GM;
  454. if (buflen > 0)
  455. {
  456. TPacketGCWhisper pack;
  457. pack.bHeader = HEADER_GC_WHISPER;
  458. pack.wSize = sizeof(TPacketGCWhisper) + buflen;
  459. pack.bType = bType;
  460. strlcpy(pack.szNameFrom, ch->GetName(), sizeof(pack.szNameFrom));
  461. // desc->BufferedPacket을 하지 않고 버퍼에 써야하는 이유는
  462. // P2P relay되어 패킷이 캡슐화 될 수 있기 때문이다.
  463. TEMP_BUFFER tmpbuf;
  464. tmpbuf.write(&pack, sizeof(pack));
  465. tmpbuf.write(buf, buflen);
  466. pkDesc->Packet(tmpbuf.read_peek(), tmpbuf.size());
  467. // @warme006
  468. // sys_log(0, "WHISPER: %s -> %s : %s", ch->GetName(), pinfo->szNameTo, buf);
  469. #ifdef ENABLE_CHAT_LOGGING
  470. if (ch->IsGM())
  471. {
  472. LogManager::instance().EscapeString(__escape_string, sizeof(__escape_string), buf, buflen);
  473. LogManager::instance().EscapeString(__escape_string2, sizeof(__escape_string2), pinfo->szNameTo, sizeof(pack.szNameFrom));
  474. LogManager::instance().ChatLog(ch->GetMapIndex(), ch->GetPlayerID(), ch->GetName(), 0, __escape_string2, "WHISPER", __escape_string, ch->GetDesc() ? ch->GetDesc()->GetHostName() : "");
  475. }
  476. #endif
  477. }
  478. }
  479. }
  480. if(pkDesc)
  481. pkDesc->SetRelay("");
  482. return (iExtraLen);
  483. }
  484. struct RawPacketToCharacterFunc
  485. {
  486. const void * m_buf;
  487. int m_buf_len;
  488. RawPacketToCharacterFunc(const void * buf, int buf_len) : m_buf(buf), m_buf_len(buf_len)
  489. {
  490. }
  491. void operator () (LPCHARACTER c)
  492. {
  493. if (!c->GetDesc())
  494. return;
  495. c->GetDesc()->Packet(m_buf, m_buf_len);
  496. }
  497. };
  498. struct FEmpireChatPacket
  499. {
  500. packet_chat& p;
  501. const char* orig_msg;
  502. int orig_len;
  503. char converted_msg[CHAT_MAX_LEN+1];
  504. BYTE bEmpire;
  505. int iMapIndex;
  506. int namelen;
  507. FEmpireChatPacket(packet_chat& p, const char* chat_msg, int len, BYTE bEmpire, int iMapIndex, int iNameLen)
  508. : p(p), orig_msg(chat_msg), orig_len(len), bEmpire(bEmpire), iMapIndex(iMapIndex), namelen(iNameLen)
  509. {
  510. memset( converted_msg, 0, sizeof(converted_msg) );
  511. }
  512. void operator () (LPDESC d)
  513. {
  514. if (!d->GetCharacter())
  515. return;
  516. if (d->GetCharacter()->GetMapIndex() != iMapIndex)
  517. return;
  518. d->BufferedPacket(&p, sizeof(packet_chat));
  519. if (d->GetEmpire() == bEmpire ||
  520. bEmpire == 0 ||
  521. d->GetCharacter()->GetGMLevel() > GM_PLAYER ||
  522. d->GetCharacter()->IsEquipUniqueGroup(UNIQUE_GROUP_RING_OF_LANGUAGE))
  523. {
  524. d->Packet(orig_msg, orig_len);
  525. }
  526. else
  527. {
  528. // 사람마다 스킬레벨이 다르니 매번 해야합니다
  529. size_t len = strlcpy(converted_msg, orig_msg, sizeof(converted_msg));
  530. if (len >= sizeof(converted_msg))
  531. len = sizeof(converted_msg) - 1;
  532. ConvertEmpireText(bEmpire, converted_msg + namelen, len - namelen, 10 + 2 * d->GetCharacter()->GetSkillPower(SKILL_LANGUAGE1 + bEmpire - 1));
  533. d->Packet(converted_msg, orig_len);
  534. }
  535. }
  536. };
  537. struct FYmirChatPacket
  538. {
  539. packet_chat& packet;
  540. const char* m_szChat;
  541. size_t m_lenChat;
  542. const char* m_szName;
  543. int m_iMapIndex;
  544. BYTE m_bEmpire;
  545. bool m_ring;
  546. char m_orig_msg[CHAT_MAX_LEN+1];
  547. int m_len_orig_msg;
  548. char m_conv_msg[CHAT_MAX_LEN+1];
  549. int m_len_conv_msg;
  550. FYmirChatPacket(packet_chat& p, const char* chat, size_t len_chat, const char* name, size_t len_name, int iMapIndex, BYTE empire, bool ring)
  551. : packet(p),
  552. m_szChat(chat), m_lenChat(len_chat),
  553. m_szName(name),
  554. m_iMapIndex(iMapIndex), m_bEmpire(empire),
  555. m_ring(ring)
  556. {
  557. m_len_orig_msg = snprintf(m_orig_msg, sizeof(m_orig_msg), "%s : %s", m_szName, m_szChat) + 1; // 널 문자 포함
  558. if (m_len_orig_msg < 0 || m_len_orig_msg >= (int) sizeof(m_orig_msg))
  559. m_len_orig_msg = sizeof(m_orig_msg) - 1;
  560. m_len_conv_msg = snprintf(m_conv_msg, sizeof(m_conv_msg), "??? : %s", m_szChat) + 1; // 널 문자 미포함
  561. if (m_len_conv_msg < 0 || m_len_conv_msg >= (int) sizeof(m_conv_msg))
  562. m_len_conv_msg = sizeof(m_conv_msg) - 1;
  563. ConvertEmpireText(m_bEmpire, m_conv_msg + 6, m_len_conv_msg - 6, 10); // 6은 "??? : "의 길이
  564. }
  565. void operator() (LPDESC d)
  566. {
  567. if (!d->GetCharacter())
  568. return;
  569. if (d->GetCharacter()->GetMapIndex() != m_iMapIndex)
  570. return;
  571. if (m_ring ||
  572. d->GetEmpire() == m_bEmpire ||
  573. d->GetCharacter()->GetGMLevel() > GM_PLAYER ||
  574. d->GetCharacter()->IsEquipUniqueGroup(UNIQUE_GROUP_RING_OF_LANGUAGE))
  575. {
  576. packet.size = m_len_orig_msg + sizeof(TPacketGCChat);
  577. d->BufferedPacket(&packet, sizeof(packet_chat));
  578. d->Packet(m_orig_msg, m_len_orig_msg);
  579. }
  580. else
  581. {
  582. packet.size = m_len_conv_msg + sizeof(TPacketGCChat);
  583. d->BufferedPacket(&packet, sizeof(packet_chat));
  584. d->Packet(m_conv_msg, m_len_conv_msg);
  585. }
  586. }
  587. };
  588. int CInputMain::Chat(LPCHARACTER ch, const char * data, size_t uiBytes)
  589. {
  590. const TPacketCGChat* pinfo = reinterpret_cast<const TPacketCGChat*>(data);
  591. if (uiBytes < pinfo->size)
  592. return -1;
  593. const int iExtraLen = pinfo->size - sizeof(TPacketCGChat);
  594. if (iExtraLen < 0)
  595. {
  596. sys_err("invalid packet length (len %d size %u buffer %u)", iExtraLen, pinfo->size, uiBytes);
  597. ch->GetDesc()->SetPhase(PHASE_CLOSE);
  598. return -1;
  599. }
  600. char buf[CHAT_MAX_LEN - (CHARACTER_NAME_MAX_LEN + 3) + 1];
  601. strlcpy(buf, data + sizeof(TPacketCGChat), MIN(iExtraLen + 1, sizeof(buf)));
  602. const size_t buflen = strlen(buf);
  603. if (buflen > 1 && *buf == '/')
  604. {
  605. interpret_command(ch, buf + 1, buflen - 1);
  606. return iExtraLen;
  607. }
  608. #ifdef ENABLE_CHAT_SPAMLIMIT
  609. if (ch->IncreaseChatCounter() >= 4)
  610. {
  611. if (ch->GetChatCounter() == 10)
  612. ch->GetDesc()->DelayedDisconnect(0);
  613. return iExtraLen;
  614. }
  615. #else
  616. if (ch->IncreaseChatCounter() >= 10)
  617. {
  618. if (ch->GetChatCounter() == 10)
  619. {
  620. sys_log(0, "CHAT_HACK: %s", ch->GetName());
  621. ch->GetDesc()->DelayedDisconnect(5);
  622. }
  623. return iExtraLen;
  624. }
  625. #endif
  626. // 채팅 금지 Affect 처리
  627. const CAffect* pAffect = ch->FindAffect(AFFECT_BLOCK_CHAT);
  628. if (pAffect != NULL)
  629. {
  630. SendBlockChatInfo(ch, pAffect->lDuration);
  631. return iExtraLen;
  632. }
  633. if (true == SpamBlockCheck(ch, buf, buflen))
  634. {
  635. return iExtraLen;
  636. }
  637. // @fixme133 begin
  638. CBanwordManager::instance().ConvertString(buf, buflen);
  639. int processReturn = ProcessTextTag(ch, buf, buflen);
  640. if (0!=processReturn)
  641. {
  642. const TItemTable* pTable = ITEM_MANAGER::instance().GetTable(ITEM_PRISM);
  643. if (NULL != pTable)
  644. {
  645. if (3==processReturn) //교환중
  646. ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("사용할수 없습니다."), pTable->szLocaleName);
  647. else
  648. ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("%s이 필요합니다."), pTable->szLocaleName);
  649. }
  650. return iExtraLen;
  651. }
  652. // @fixme133 end
  653. char chatbuf[CHAT_MAX_LEN + 1];
  654. #ifdef ENABLE_CHAT_COLOR_SYSTEM
  655. // static const char* colorbuf[] = {" |h|r[寔햇?|cFFffa200|h", " |h|r[遁鞍?|cFFff0000|h", " |h|r[讀安?|cFFffc700|h", " |h|r[警仰]|cFF000bff|h"}; // Arab
  656. static const char* colorbuf[] = {"|cFFffa200|H|h[Staff]|h|r", "|cFFff0000|H|h[Shinsoo]|h|r", "|cFFffc700|H|h[Chunjo]|h|r", "|cFF000bff|H|h[Jinno]|h|r"};
  657. int len = snprintf(chatbuf, sizeof(chatbuf), "%s %s : %s", (ch->IsGM()?colorbuf[0]:colorbuf[MINMAX(0, ch->GetEmpire(), 3)]), ch->GetName(), buf);
  658. #else
  659. int len = snprintf(chatbuf, sizeof(chatbuf), "%s : %s", ch->GetName(), buf);
  660. #endif
  661. if (CHAT_TYPE_SHOUT == pinfo->type)
  662. {
  663. LogManager::instance().ShoutLog(g_bChannel, ch->GetEmpire(), chatbuf);
  664. }
  665. if (len < 0 || len >= (int) sizeof(chatbuf))
  666. len = sizeof(chatbuf) - 1;
  667. if (pinfo->type == CHAT_TYPE_SHOUT)
  668. {
  669. // const int SHOUT_LIMIT_LEVEL = 15;
  670. if (ch->GetLevel() < g_iShoutLimitLevel)
  671. {
  672. ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("외치기는 레벨 %d 이상만 사용 가능 합니다."), g_iShoutLimitLevel);
  673. return (iExtraLen);
  674. }
  675. // if (thecore_heart->pulse - (int) ch->GetLastShoutPulse() < passes_per_sec * g_iShoutLimitTime)
  676. if (thecore_heart->pulse - (int) ch->GetLastShoutPulse() < passes_per_sec * 15)
  677. return (iExtraLen);
  678. ch->SetLastShoutPulse(thecore_heart->pulse);
  679. TPacketGGShout p;
  680. p.bHeader = HEADER_GG_SHOUT;
  681. p.bEmpire = ch->GetEmpire();
  682. strlcpy(p.szText, chatbuf, sizeof(p.szText));
  683. P2P_MANAGER::instance().Send(&p, sizeof(TPacketGGShout));
  684. SendShout(chatbuf, ch->GetEmpire());
  685. return (iExtraLen);
  686. }
  687. TPacketGCChat pack_chat;
  688. pack_chat.header = HEADER_GC_CHAT;
  689. pack_chat.size = sizeof(TPacketGCChat) + len;
  690. pack_chat.type = pinfo->type;
  691. pack_chat.id = ch->GetVID();
  692. switch (pinfo->type)
  693. {
  694. case CHAT_TYPE_TALKING:
  695. {
  696. const DESC_MANAGER::DESC_SET & c_ref_set = DESC_MANAGER::instance().GetClientSet();
  697. if (false)
  698. {
  699. std::for_each(c_ref_set.begin(), c_ref_set.end(),
  700. FYmirChatPacket(pack_chat,
  701. buf,
  702. strlen(buf),
  703. ch->GetName(),
  704. strlen(ch->GetName()),
  705. ch->GetMapIndex(),
  706. ch->GetEmpire(),
  707. ch->IsEquipUniqueGroup(UNIQUE_GROUP_RING_OF_LANGUAGE)));
  708. }
  709. else
  710. {
  711. std::for_each(c_ref_set.begin(), c_ref_set.end(),
  712. FEmpireChatPacket(pack_chat,
  713. chatbuf,
  714. len,
  715. (ch->GetGMLevel() > GM_PLAYER ||
  716. ch->IsEquipUniqueGroup(UNIQUE_GROUP_RING_OF_LANGUAGE)) ? 0 : ch->GetEmpire(),
  717. ch->GetMapIndex(), strlen(ch->GetName())));
  718. #ifdef ENABLE_CHAT_LOGGING
  719. if (ch->IsGM())
  720. {
  721. LogManager::instance().EscapeString(__escape_string, sizeof(__escape_string), chatbuf, len);
  722. LogManager::instance().ChatLog(ch->GetMapIndex(), ch->GetPlayerID(), ch->GetName(), 0, "", "NORMAL", __escape_string, ch->GetDesc() ? ch->GetDesc()->GetHostName() : "");
  723. }
  724. #endif
  725. }
  726. }
  727. break;
  728. case CHAT_TYPE_PARTY:
  729. {
  730. if (!ch->GetParty())
  731. ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("파티 중이 아닙니다."));
  732. else
  733. {
  734. TEMP_BUFFER tbuf;
  735. tbuf.write(&pack_chat, sizeof(pack_chat));
  736. tbuf.write(chatbuf, len);
  737. RawPacketToCharacterFunc f(tbuf.read_peek(), tbuf.size());
  738. ch->GetParty()->ForEachOnlineMember(f);
  739. #ifdef ENABLE_CHAT_LOGGING
  740. if (ch->IsGM())
  741. {
  742. LogManager::instance().EscapeString(__escape_string, sizeof(__escape_string), chatbuf, len);
  743. LogManager::instance().ChatLog(ch->GetMapIndex(), ch->GetPlayerID(), ch->GetName(), ch->GetParty()->GetLeaderPID(), "", "PARTY", __escape_string, ch->GetDesc() ? ch->GetDesc()->GetHostName() : "");
  744. }
  745. #endif
  746. }
  747. }
  748. break;
  749. case CHAT_TYPE_GUILD:
  750. {
  751. if (!ch->GetGuild())
  752. ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("길드에 가입하지 않았습니다."));
  753. else
  754. {
  755. ch->GetGuild()->Chat(chatbuf);
  756. #ifdef ENABLE_CHAT_LOGGING
  757. if (ch->IsGM())
  758. {
  759. LogManager::instance().EscapeString(__escape_string, sizeof(__escape_string), chatbuf, len);
  760. LogManager::instance().ChatLog(ch->GetMapIndex(), ch->GetPlayerID(), ch->GetName(), ch->GetGuild()->GetID(), ch->GetGuild()->GetName(), "GUILD", __escape_string, ch->GetDesc() ? ch->GetDesc()->GetHostName() : "");
  761. }
  762. #endif
  763. }
  764. }
  765. break;
  766. default:
  767. sys_err("Unknown chat type %d", pinfo->type);
  768. break;
  769. }
  770. return (iExtraLen);
  771. }
  772. void CInputMain::ItemUse(LPCHARACTER ch, const char * data)
  773. {
  774. ch->UseItem(((struct command_item_use *) data)->Cell);
  775. }
  776. void CInputMain::ItemToItem(LPCHARACTER ch, const char * pcData)
  777. {
  778. TPacketCGItemUseToItem * p = (TPacketCGItemUseToItem *) pcData;
  779. if (ch)
  780. ch->UseItem(p->Cell, p->TargetCell);
  781. }
  782. void CInputMain::ItemDrop(LPCHARACTER ch, const char * data)
  783. {
  784. struct command_item_drop * pinfo = (struct command_item_drop *) data;
  785. //MONARCH_LIMIT
  786. //if (ch->IsMonarch())
  787. // return;
  788. //END_MONARCH_LIMIT
  789. if (!ch)
  790. return;
  791. // 엘크가 0보다 크면 엘크를 버리는 것 이다.
  792. if (pinfo->gold > 0)
  793. ch->DropGold(pinfo->gold);
  794. else
  795. ch->DropItem(pinfo->Cell);
  796. }
  797. void CInputMain::ItemDrop2(LPCHARACTER ch, const char * data)
  798. {
  799. //MONARCH_LIMIT
  800. //if (ch->IsMonarch())
  801. // return;
  802. //END_MONARCH_LIMIT
  803. TPacketCGItemDrop2 * pinfo = (TPacketCGItemDrop2 *) data;
  804. // 엘크가 0보다 크면 엘크를 버리는 것 이다.
  805. if (!ch)
  806. return;
  807. if (pinfo->gold > 0)
  808. ch->DropGold(pinfo->gold);
  809. else
  810. ch->DropItem(pinfo->Cell, pinfo->count);
  811. }
  812. #ifdef __DESTROYSELL_SYSTEM__
  813. void CInputMain::ItemDestroy(LPCHARACTER ch, const char * data)
  814. {
  815. struct command_item_destroy * pinfo = (struct command_item_destroy *) data;
  816. if (ch)
  817. ch->DestroyItem(pinfo->Cell);
  818. }
  819. void CInputMain::ItemSell(LPCHARACTER ch, const char * data)
  820. {
  821. struct command_item_sell * pinfo = (struct command_item_sell *) data;
  822. if (ch)
  823. ch->SellItem(pinfo->Cell);
  824. }
  825. #endif
  826. void CInputMain::ItemMove(LPCHARACTER ch, const char * data)
  827. {
  828. struct command_item_move * pinfo = (struct command_item_move *) data;
  829. if (ch)
  830. ch->MoveItem(pinfo->Cell, pinfo->CellTo, pinfo->count);
  831. }
  832. void CInputMain::ItemPickup(LPCHARACTER ch, const char * data)
  833. {
  834. struct command_item_pickup * pinfo = (struct command_item_pickup*) data;
  835. if (ch)
  836. ch->PickupItem(pinfo->vid);
  837. }
  838. void CInputMain::QuickslotAdd(LPCHARACTER ch, const char * data)
  839. {
  840. struct command_quickslot_add * pinfo = (struct command_quickslot_add *) data;
  841. ch->SetQuickslot(pinfo->pos, pinfo->slot);
  842. }
  843. void CInputMain::QuickslotDelete(LPCHARACTER ch, const char * data)
  844. {
  845. struct command_quickslot_del * pinfo = (struct command_quickslot_del *) data;
  846. ch->DelQuickslot(pinfo->pos);
  847. }
  848. void CInputMain::QuickslotSwap(LPCHARACTER ch, const char * data)
  849. {
  850. struct command_quickslot_swap * pinfo = (struct command_quickslot_swap *) data;
  851. ch->SwapQuickslot(pinfo->pos, pinfo->change_pos);
  852. }
  853. int CInputMain::Messenger(LPCHARACTER ch, const char* c_pData, size_t uiBytes)
  854. {
  855. TPacketCGMessenger* p = (TPacketCGMessenger*) c_pData;
  856. if (uiBytes < sizeof(TPacketCGMessenger))
  857. return -1;
  858. c_pData += sizeof(TPacketCGMessenger);
  859. uiBytes -= sizeof(TPacketCGMessenger);
  860. switch (p->subheader)
  861. {
  862. case MESSENGER_SUBHEADER_CG_ADD_BY_VID:
  863. {
  864. if (uiBytes < sizeof(TPacketCGMessengerAddByVID))
  865. return -1;
  866. TPacketCGMessengerAddByVID * p2 = (TPacketCGMessengerAddByVID *) c_pData;
  867. LPCHARACTER ch_companion = CHARACTER_MANAGER::instance().Find(p2->vid);
  868. if (!ch_companion)
  869. return sizeof(TPacketCGMessengerAddByVID);
  870. if (ch->IsObserverMode())
  871. return sizeof(TPacketCGMessengerAddByVID);
  872. if (ch_companion->IsBlockMode(BLOCK_MESSENGER_INVITE))
  873. {
  874. ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("상대방이 메신져 추가 거부 상태입니다."));
  875. return sizeof(TPacketCGMessengerAddByVID);
  876. }
  877. LPDESC d = ch_companion->GetDesc();
  878. if (!d)
  879. return sizeof(TPacketCGMessengerAddByVID);
  880. if (ch->GetGMLevel() == GM_PLAYER && ch_companion->GetGMLevel() != GM_PLAYER)
  881. {
  882. ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<메신져> 운영자는 메신져에 추가할 수 없습니다."));
  883. return sizeof(TPacketCGMessengerAddByVID);
  884. }
  885. if (ch->GetDesc() == d) // 자신은 추가할 수 없다.
  886. return sizeof(TPacketCGMessengerAddByVID);
  887. MessengerManager::instance().RequestToAdd(ch, ch_companion);
  888. //MessengerManager::instance().AddToList(ch->GetName(), ch_companion->GetName());
  889. }
  890. return sizeof(TPacketCGMessengerAddByVID);
  891. case MESSENGER_SUBHEADER_CG_ADD_BY_NAME:
  892. {
  893. if (uiBytes < CHARACTER_NAME_MAX_LEN)
  894. return -1;
  895. char name[CHARACTER_NAME_MAX_LEN + 1];
  896. strlcpy(name, c_pData, sizeof(name));
  897. if (ch->GetGMLevel() == GM_PLAYER && gm_get_level(name) != GM_PLAYER)
  898. {
  899. ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<메신져> 운영자는 메신져에 추가할 수 없습니다."));
  900. return CHARACTER_NAME_MAX_LEN;
  901. }
  902. LPCHARACTER tch = CHARACTER_MANAGER::instance().FindPC(name);
  903. if (!tch)
  904. ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("%s 님은 접속되 있지 않습니다."), name);
  905. else
  906. {
  907. if (tch == ch) // 자신은 추가할 수 없다.
  908. return CHARACTER_NAME_MAX_LEN;
  909. if (tch->IsBlockMode(BLOCK_MESSENGER_INVITE) == true)
  910. {
  911. ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("상대방이 메신져 추가 거부 상태입니다."));
  912. }
  913. else
  914. {
  915. // 메신저가 캐릭터단위가 되면서 변경
  916. MessengerManager::instance().RequestToAdd(ch, tch);
  917. //MessengerManager::instance().AddToList(ch->GetName(), tch->GetName());
  918. }
  919. }
  920. }
  921. return CHARACTER_NAME_MAX_LEN;
  922. case MESSENGER_SUBHEADER_CG_REMOVE:
  923. {
  924. if (uiBytes < CHARACTER_NAME_MAX_LEN)
  925. return -1;
  926. char char_name[CHARACTER_NAME_MAX_LEN + 1];
  927. strlcpy(char_name, c_pData, sizeof(char_name));
  928. MessengerManager::instance().RemoveFromList(ch->GetName(), char_name);
  929. }
  930. return CHARACTER_NAME_MAX_LEN;
  931. default:
  932. sys_err("CInputMain::Messenger : Unknown subheader %d : %s", p->subheader, ch->GetName());
  933. break;
  934. }
  935. return 0;
  936. }
  937. int CInputMain::Shop(LPCHARACTER ch, const char * data, size_t uiBytes)
  938. {
  939. TPacketCGShop * p = (TPacketCGShop *) data;
  940. if (uiBytes < sizeof(TPacketCGShop))
  941. return -1;
  942. if (test_server)
  943. sys_log(0, "CInputMain::Shop() ==> SubHeader %d", p->subheader);
  944. const char * c_pData = data + sizeof(TPacketCGShop);
  945. uiBytes -= sizeof(TPacketCGShop);
  946. switch (p->subheader)
  947. {
  948. case SHOP_SUBHEADER_CG_END:
  949. sys_log(1, "INPUT: %s SHOP: END", ch->GetName());
  950. CShopManager::instance().StopShopping(ch);
  951. return 0;
  952. case SHOP_SUBHEADER_CG_BUY:
  953. {
  954. if (uiBytes < sizeof(BYTE) + sizeof(BYTE))
  955. return -1;
  956. BYTE bPos = *(c_pData + 1);
  957. sys_log(1, "INPUT: %s SHOP: BUY %d", ch->GetName(), bPos);
  958. CShopManager::instance().Buy(ch, bPos);
  959. return (sizeof(BYTE) + sizeof(BYTE));
  960. }
  961. case SHOP_SUBHEADER_CG_SELL:
  962. {
  963. if (uiBytes < sizeof(BYTE))
  964. return -1;
  965. BYTE pos = *c_pData;
  966. sys_log(0, "INPUT: %s SHOP: SELL", ch->GetName());
  967. CShopManager::instance().Sell(ch, pos);
  968. return sizeof(BYTE);
  969. }
  970. case SHOP_SUBHEADER_CG_SELL2:
  971. {
  972. if (uiBytes < sizeof(BYTE) + sizeof(BYTE))
  973. return -1;
  974. BYTE pos = *(c_pData++);
  975. BYTE count = *(c_pData);
  976. sys_log(0, "INPUT: %s SHOP: SELL2", ch->GetName());
  977. CShopManager::instance().Sell(ch, pos, count);
  978. return sizeof(BYTE) + sizeof(BYTE);
  979. }
  980. default:
  981. sys_err("CInputMain::Shop : Unknown subheader %d : %s", p->subheader, ch->GetName());
  982. break;
  983. }
  984. return 0;
  985. }
  986. int CInputMain::OfflineShop(LPCHARACTER ch, const char * data, size_t uiBytes)
  987. {
  988. TPacketCGShop * p = (TPacketCGShop *)data;
  989. if (uiBytes < sizeof(TPacketCGShop))
  990. return -1;
  991. if (test_server)
  992. sys_log(0, "CInputMain::OfflineShop ==> SubHeader %d", p->subheader);
  993. const char * c_pData = data + sizeof(TPacketCGShop);
  994. uiBytes -= sizeof(TPacketCGShop);
  995. switch (p->subheader)
  996. {
  997. case SHOP_SUBHEADER_CG_END:
  998. sys_log(1, "INPUT: %s OFFLINE_SHOP: END", ch->GetName());
  999. COfflineShopManager::instance().StopShopping(ch);
  1000. return 0;
  1001. case SHOP_SUBHEADER_CG_BUY:
  1002. {
  1003. if (uiBytes < sizeof(BYTE) + sizeof(BYTE))
  1004. return -1;
  1005. BYTE bPos = *(c_pData + 1);
  1006. sys_log(1, "INPUT: %s OFFLINE_SHOP: BUY %d", ch->GetName(), bPos);
  1007. COfflineShopManager::instance().Buy(ch, bPos);
  1008. return (sizeof(BYTE) + sizeof(BYTE));
  1009. }
  1010. case SHOP_SUBHEADER_CG_CHANGE_EDIT_TIME:
  1011. {
  1012. if (uiBytes < sizeof(BYTE))
  1013. return -1;
  1014. BYTE bTime = *c_pData;
  1015. sys_log(0, "INPUT: %s EDIT_OFFLINE_SHOP_TIME", ch->GetName());
  1016. COfflineShopManager::instance().ChangeOfflineShopTime(ch, bTime);
  1017. return sizeof(BYTE);
  1018. }
  1019. case SHOP_SUBHEADER_CG_DESTROY_OFFLINE_SHOP:
  1020. sys_log(1, "INPUT: %s OFFLINE_SHOP_DESTROY", ch->GetName());
  1021. COfflineShopManager::instance().DestroyOfflineShop(ch, ch->GetOfflineShopVID(), true);
  1022. return 0;
  1023. case SHOP_SUBHEADER_CG_ADD_ITEM:
  1024. {
  1025. ch->ChatPacket(CHAT_TYPE_INFO, "Add Item %d", ch->GetOfflineShopStatus());
  1026. // If the user is not update the client, give a message ;)
  1027. if (ch->GetOfflineShopStatus() == 0 || ch->GetOfflineShopStatus() == 1)
  1028. {
  1029. ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("You can't use this window now!"));
  1030. return 0;
  1031. }
  1032. if (uiBytes < sizeof(TOfflineShopItemTable2))
  1033. return -1;
  1034. TOfflineShopItemTable2 * pTable = (TOfflineShopItemTable2 *)(c_pData);
  1035. #ifdef ENABLE_MAXIMUM_YANG_FOR_OFFLINE_SHOP
  1036. COfflineShopManager::instance().AddItem(ch, pTable->bDisplayPos, pTable->bPos, pTable->llPrice);
  1037. #else
  1038. COfflineShopManager::instance().AddItem(ch, pTable->bDisplayPos, pTable->bPos, pTable->lPrice);
  1039. #endif
  1040. return (sizeof(TOfflineShopItemTable2));
  1041. }
  1042. case SHOP_SUBHEADER_CG_REMOVE_ITEM:
  1043. {
  1044. ch->ChatPacket(CHAT_TYPE_INFO, "Remove Item %d", ch->GetOfflineShopStatus());
  1045. // If the user is not update the client, give a message ;)
  1046. if (ch->GetOfflineShopStatus() == 0 || ch->GetOfflineShopStatus() == 1)
  1047. {
  1048. ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("You can't use this window now!"));
  1049. return 0;
  1050. }
  1051. if (uiBytes < sizeof(BYTE))
  1052. return -1;
  1053. BYTE bPos = *c_pData;
  1054. sys_log(0, "INPUT: %s REMOVE_ITEM : %d", ch->GetName(), bPos);
  1055. COfflineShopManager::instance().RemoveItem(ch, bPos);
  1056. return (sizeof(BYTE));
  1057. }
  1058. case SHOP_SUBHEADER_CG_CHANGE_PRICE:
  1059. {
  1060. // If the user is not update the client, give a message ;)
  1061. if (ch->GetOfflineShopStatus() == 0 || ch->GetOfflineShopStatus() == 1)
  1062. {
  1063. ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("You can't use this window now!"));
  1064. return 0;
  1065. }
  1066. if (uiBytes < sizeof(TOfflineShopItemTable2))
  1067. return -1;
  1068. TOfflineShopItemTable2 * pTable = (TOfflineShopItemTable2 *)(c_pData);
  1069. #ifdef ENABLE_MAXIMUM_YANG_FOR_OFFLINE_SHOP
  1070. sys_log(0, "INPUT: %s OFFLINE_CHANGE_PRICE(%lld)", ch->GetName(), pTable->bDisplayPos);
  1071. COfflineShopManager::instance().ChangePrice(ch, pTable->bDisplayPos, pTable->llPrice);
  1072. #else
  1073. sys_log(0, "INPUT: %s OFFLINE_CHANGE_PRICE (%d)", ch->GetName(), pTable->bDisplayPos);
  1074. COfflineShopManager::instance().ChangePrice(ch, pTable->bDisplayPos, pTable->lPrice);
  1075. #endif
  1076. return (sizeof(TOfflineShopItemTable2));
  1077. }
  1078. case SHOP_SUBHEADER_CG_REFRESH:
  1079. ch->ChatPacket(CHAT_TYPE_INFO, "Refresh %d", ch->GetOfflineShopStatus());
  1080. if (ch->GetOfflineShopStatus() == 0 || ch->GetOfflineShopStatus() == 1)
  1081. {
  1082. ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("You can't use this window now!"));
  1083. return 0;
  1084. }
  1085. sys_log(0, "INPUT: %s OFFLINE_SHOP_REFRESH_ITEM", ch->GetName());
  1086. COfflineShopManager::instance().Refresh(ch);
  1087. return 0;
  1088. case SHOP_SUBHEADER_CG_REFRESH_MONEY:
  1089. {
  1090. sys_log(0, "INPUT: %s OFFLINE_SHOP_REFRESH_MONEY", ch->GetName());
  1091. COfflineShopManager::instance().RefreshMoney(ch);
  1092. return 0;
  1093. }
  1094. case SHOP_SUBHEADER_CG_WITHDRAW_MONEY:
  1095. {
  1096. if (uiBytes < sizeof(long long))
  1097. return -1;
  1098. const long long gold = *reinterpret_cast<const long long*>(c_pData);
  1099. sys_log(0, "INPUT: %s(%lld) OFFLINE_SHOP_WITHDRAW_MONEY", ch->GetName(), gold);
  1100. COfflineShopManager::instance().WithdrawMoney(ch, gold);
  1101. return (sizeof(long long));
  1102. }
  1103. case SHOP_SUBHEADER_CG_REFRESH_UNSOLD_ITEMS:
  1104. {
  1105. sys_log(0, "INPUT: %s OFFLINE_SHOP_REFRESH_UNSOLD_ITEMS", ch->GetName());
  1106. COfflineShopManager::instance().RefreshUnsoldItems(ch);
  1107. return 0;
  1108. }
  1109. case SHOP_SUBHEADER_CG_TAKE_ITEM:
  1110. {
  1111. if (uiBytes < sizeof(BYTE))
  1112. return -1;
  1113. BYTE bPos = *c_pData;
  1114. sys_log(0, "INPUT: %s OFFLINE_SHOP_TAKE_ITEM", ch->GetName());
  1115. COfflineShopManager::instance().TakeItem(ch, bPos);
  1116. return (sizeof(BYTE));
  1117. }
  1118. case SHOP_SUBHEADER_CG_CHECK:
  1119. COfflineShopManager::instance().HasOfflineShop(ch);
  1120. return 0;
  1121. default:
  1122. sys_err("CInputMain::OfflineShop : Unknown subheader %d : %s", p->subheader, ch->GetName());
  1123. break;
  1124. }
  1125. return 0;
  1126. }
  1127. void CInputMain::OnClick(LPCHARACTER ch, const char * data)
  1128. {
  1129. struct command_on_click * pinfo = (struct command_on_click *) data;
  1130. LPCHARACTER victim;
  1131. if ((victim = CHARACTER_MANAGER::instance().Find(pinfo->vid)))
  1132. victim->OnClick(ch);
  1133. else if (test_server)
  1134. {
  1135. sys_err("CInputMain::OnClick %s.Click.NOT_EXIST_VID[%d]", ch->GetName(), pinfo->vid);
  1136. }
  1137. }
  1138. void CInputMain::Exchange(LPCHARACTER ch, const char * data)
  1139. {
  1140. struct command_exchange * pinfo = (struct command_exchange *) data;
  1141. LPCHARACTER to_ch = NULL;
  1142. if (!ch->CanHandleItem())
  1143. return;
  1144. int iPulse = thecore_pulse();
  1145. if ((to_ch = CHARACTER_MANAGER::instance().Find(pinfo->arg1)))
  1146. {
  1147. if (iPulse - to_ch->GetSafeboxLoadTime() < PASSES_PER_SEC(g_nPortalLimitTime))
  1148. {
  1149. to_ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("거래 후 %d초 이내에 창고를 열수 없습니다."), g_nPortalLimitTime);
  1150. return;
  1151. }
  1152. if( true == to_ch->IsDead() )
  1153. {
  1154. return;
  1155. }
  1156. }
  1157. sys_log(0, "CInputMain()::Exchange() SubHeader %d ", pinfo->sub_header);
  1158. if (iPulse - ch->GetSafeboxLoadTime() < PASSES_PER_SEC(g_nPortalLimitTime))
  1159. {
  1160. ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("거래 후 %d초 이내에 창고를 열수 없습니다."), g_nPortalLimitTime);
  1161. return;
  1162. }
  1163. switch (pinfo->sub_header)
  1164. {
  1165. case EXCHANGE_SUBHEADER_CG_START: // arg1 == vid of target character
  1166. if (!ch->GetExchange())
  1167. {
  1168. if ((to_ch = CHARACTER_MANAGER::instance().Find(pinfo->arg1)))
  1169. {
  1170. //MONARCH_LIMIT
  1171. /*
  1172. if (to_ch->IsMonarch() || ch->IsMonarch())
  1173. {
  1174. ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("군주와는 거래를 할수가 없습니다"), g_nPortalLimitTime);
  1175. return;
  1176. }
  1177. //END_MONARCH_LIMIT
  1178. */
  1179. if (iPulse - ch->GetSafeboxLoadTime() < PASSES_PER_SEC(g_nPortalLimitTime))
  1180. {
  1181. ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("창고를 연후 %d초 이내에는 거래를 할수 없습니다."), g_nPortalLimitTime);
  1182. if (test_server)
  1183. ch->ChatPacket(CHAT_TYPE_INFO, "[TestOnly][Safebox]Pulse %d LoadTime %d PASS %d", iPulse, ch->GetSafeboxLoadTime(), PASSES_PER_SEC(g_nPortalLimitTime));
  1184. return;
  1185. }
  1186. if (iPulse - to_ch->GetSafeboxLoadTime() < PASSES_PER_SEC(g_nPortalLimitTime))
  1187. {
  1188. to_ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("창고를 연후 %d초 이내에는 거래를 할수 없습니다."), g_nPortalLimitTime);
  1189. if (test_server)
  1190. to_ch->ChatPacket(CHAT_TYPE_INFO, "[TestOnly][Safebox]Pulse %d LoadTime %d PASS %d", iPulse, to_ch->GetSafeboxLoadTime(), PASSES_PER_SEC(g_nPortalLimitTime));
  1191. return;
  1192. }
  1193. if (ch->GetGold() >= GOLD_MAX)
  1194. {
  1195. ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("액수가 20억 냥을 초과하여 거래를 할수가 없습니다.."));
  1196. sys_err("[OVERFLOG_GOLD] START (%u) id %u name %s ", ch->GetGold(), ch->GetPlayerID(), ch->GetName());
  1197. return;
  1198. }
  1199. if (to_ch->IsPC())
  1200. {
  1201. if (quest::CQuestManager::instance().GiveItemToPC(ch->GetPlayerID(), to_ch))
  1202. {
  1203. sys_log(0, "Exchange canceled by quest %s %s", ch->GetName(), to_ch->GetName());
  1204. return;
  1205. }
  1206. }
  1207. #ifdef ENABLE_OFFLINE_SHOP_SYSTEM
  1208. if (ch->GetMyShop() || ch->IsOpenSafebox() || ch->GetShopOwner() || ch->IsCubeOpen() || ch->GetOfflineShopOwner())
  1209. {
  1210. ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("다른 거래중일경우 개인상점을 열수가 없습니다."));
  1211. return;
  1212. }
  1213. #else
  1214. if (ch->GetMyShop() || ch->IsOpenSafebox() || ch->GetShopOwner() || ch->IsCubeOpen())
  1215. {
  1216. ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("다른 거래중일경우 개인상점을 열수가 없습니다."));
  1217. return;
  1218. }
  1219. #endif
  1220. ch->ExchangeStart(to_ch);
  1221. }
  1222. }
  1223. break;
  1224. case EXCHANGE_SUBHEADER_CG_ITEM_ADD: // arg1 == position of item, arg2 == position in exchange window
  1225. if (ch->GetExchange())
  1226. {
  1227. if (ch->GetExchange()->GetCompany()->GetAcceptStatus() != true)
  1228. ch->GetExchange()->AddItem(pinfo->Pos, pinfo->arg2);
  1229. }
  1230. break;
  1231. case EXCHANGE_SUBHEADER_CG_ITEM_DEL: // arg1 == position of item
  1232. if (ch->GetExchange())
  1233. {
  1234. if (ch->GetExchange()->GetCompany()->GetAcceptStatus() != true)
  1235. ch->GetExchange()->RemoveItem(pinfo->arg1);
  1236. }
  1237. break;
  1238. case EXCHANGE_SUBHEADER_CG_ELK_ADD: // arg1 == amount of gold
  1239. if (ch->GetExchange())
  1240. {
  1241. const int64_t nTotalGold = static_cast<int64_t>(ch->GetExchange()->GetCompany()->GetOwner()->GetGold()) + static_cast<int64_t>(pinfo->arg1);
  1242. if (GOLD_MAX <= nTotalGold)
  1243. {
  1244. ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("상대방의 총금액이 20억 냥을 초과하여 거래를 할수가 없습니다.."));
  1245. sys_err("[OVERFLOW_GOLD] ELK_ADD (%u) id %u name %s ",
  1246. ch->GetExchange()->GetCompany()->GetOwner()->GetGold(),
  1247. ch->GetExchange()->GetCompany()->GetOwner()->GetPlayerID(),
  1248. ch->GetExchange()->GetCompany()->GetOwner()->GetName());
  1249. return;
  1250. }
  1251. if (ch->GetExchange()->GetCompany()->GetAcceptStatus() != true)
  1252. ch->GetExchange()->AddGold(pinfo->arg1);
  1253. }
  1254. break;
  1255. case EXCHANGE_SUBHEADER_CG_ACCEPT: // arg1 == not used
  1256. if (ch->GetExchange())
  1257. {
  1258. sys_log(0, "CInputMain()::Exchange() ==> ACCEPT ");
  1259. ch->GetExchange()->Accept(true);
  1260. }
  1261. break;
  1262. case EXCHANGE_SUBHEADER_CG_CANCEL: // arg1 == not used
  1263. if (ch->GetExchange())
  1264. ch->GetExchange()->Cancel();
  1265. break;
  1266. }
  1267. }
  1268. void CInputMain::Position(LPCHARACTER ch, const char * data)
  1269. {
  1270. struct command_position * pinfo = (struct command_position *) data;
  1271. switch (pinfo->position)
  1272. {
  1273. case POSITION_GENERAL:
  1274. ch->Standup();
  1275. break;
  1276. case POSITION_SITTING_CHAIR:
  1277. ch->Sitdown(0);
  1278. break;
  1279. case POSITION_SITTING_GROUND:
  1280. ch->Sitdown(1);
  1281. break;
  1282. }
  1283. }
  1284. static const int ComboSequenceBySkillLevel[3][8] =
  1285. {
  1286. // 0 1 2 3 4 5 6 7
  1287. { 14, 15, 16, 17, 0, 0, 0, 0 },
  1288. { 14, 15, 16, 18, 20, 0, 0, 0 },
  1289. { 14, 15, 16, 18, 19, 17, 0, 0 },
  1290. };
  1291. #define COMBO_HACK_ALLOWABLE_MS 100
  1292. bool CheckComboHack(LPCHARACTER ch, BYTE bArg, DWORD dwTime, bool CheckSpeedHack)
  1293. {
  1294. if(!gHackCheckEnable) return false;
  1295. // 죽거나 기절 상태에서는 공격할 수 없으므로, skip한다.
  1296. // 이렇게 하지 말고, CHRACTER::CanMove()에
  1297. // if (IsStun() || IsDead()) return false;
  1298. // 를 추가하는게 맞다고 생각하나,
  1299. // 이미 다른 부분에서 CanMove()는 IsStun(), IsDead()과
  1300. // 독립적으로 체크하고 있기 때문에 수정에 의한 영향을
  1301. // 최소화하기 위해 이렇게 땜빵 코드를 써놓는다.
  1302. if (ch->IsStun() || ch->IsDead())
  1303. return false;
  1304. int ComboInterval = dwTime - ch->GetLastComboTime();
  1305. int HackScalar = 0; // 기본 스칼라 단위 1
  1306. #if 0
  1307. sys_log(0, "COMBO: %s arg:%u seq:%u delta:%d checkspeedhack:%d",
  1308. ch->GetName(), bArg, ch->GetComboSequence(), ComboInterval - ch->GetValidComboInterval(), CheckSpeedHack);
  1309. #endif
  1310. // bArg 14 ~ 21번 까지 총 8콤보 가능
  1311. // 1. 첫 콤보(14)는 일정 시간 이후에 반복 가능
  1312. // 2. 15 ~ 21번은 반복 불가능
  1313. // 3. 차례대로 증가한다.
  1314. if (bArg == 14)
  1315. {
  1316. if (CheckSpeedHack && ComboInterval > 0 && ComboInterval < ch->GetValidComboInterval() - COMBO_HACK_ALLOWABLE_MS)
  1317. {
  1318. // FIXME 첫번째 콤보는 이상하게 빨리 올 수가 있어서 300으로 나눔 -_-;
  1319. // 다수의 몬스터에 의해 다운되는 상황에서 공격을 하면
  1320. // 첫번째 콤보가 매우 적은 인터벌로 들어오는 상황 발생.
  1321. // 이로 인해 콤보핵으로 튕기는 경우가 있어 다음 코드 비 활성화.
  1322. //HackScalar = 1 + (ch->GetValidComboInterval() - ComboInterval) / 300;
  1323. //sys_log(0, "COMBO_HACK: 2 %s arg:%u interval:%d valid:%u atkspd:%u riding:%s",
  1324. // ch->GetName(),
  1325. // bArg,
  1326. // ComboInterval,
  1327. // ch->GetValidComboInterval(),
  1328. // ch->GetPoint(POINT_ATT_SPEED),
  1329. // ch->IsRiding() ? "yes" : "no");
  1330. }
  1331. ch->SetComboSequence(1);
  1332. ch->SetValidComboInterval((int) (ani_combo_speed(ch, 1) / (ch->GetPoint(POINT_ATT_SPEED) / 100.f)));
  1333. ch->SetLastComboTime(dwTime);
  1334. }
  1335. else if (bArg > 14 && bArg < 22)
  1336. {
  1337. int idx = MIN(2, ch->GetComboIndex());
  1338. if (ch->GetComboSequence() > 5) // 현재 6콤보 이상은 없다.
  1339. {
  1340. HackScalar = 1;
  1341. ch->SetValidComboInterval(300);
  1342. sys_log(0, "COMBO_HACK: 5 %s combo_seq:%d", ch->GetName(), ch->GetComboSequence());
  1343. }
  1344. // 자객 쌍수 콤보 예외처리
  1345. else if (bArg == 21 &&
  1346. idx == 2 &&
  1347. ch->GetComboSequence() == 5 &&
  1348. ch->GetJob() == JOB_ASSASSIN &&
  1349. ch->GetWear(WEAR_WEAPON) &&
  1350. ch->GetWear(WEAR_WEAPON)->GetSubType() == WEAPON_DAGGER)
  1351. ch->SetValidComboInterval(300);
  1352. #ifdef ENABLE_WOLFMAN_CHARACTER
  1353. else if (bArg == 21 && idx == 2 && ch->GetComboSequence() == 5 && ch->GetJob() == JOB_WOLFMAN && ch->GetWear(WEAR_WEAPON) && ch->GetWear(WEAR_WEAPON)->GetSubType() == WEAPON_CLAW)
  1354. ch->SetValidComboInterval(300);
  1355. #endif
  1356. else if (ComboSequenceBySkillLevel[idx][ch->GetComboSequence()] != bArg)
  1357. {
  1358. HackScalar = 1;
  1359. ch->SetValidComboInterval(300);
  1360. sys_log(0, "COMBO_HACK: 3 %s arg:%u valid:%u combo_idx:%d combo_seq:%d",
  1361. ch->GetName(),
  1362. bArg,
  1363. ComboSequenceBySkillLevel[idx][ch->GetComboSequence()],
  1364. idx,
  1365. ch->GetComboSequence());
  1366. }
  1367. else
  1368. {
  1369. if (CheckSpeedHack && ComboInterval < ch->GetValidComboInterval() - COMBO_HACK_ALLOWABLE_MS)
  1370. {
  1371. HackScalar = 1 + (ch->GetValidComboInterval() - ComboInterval) / 100;
  1372. sys_log(0, "COMBO_HACK: 2 %s arg:%u interval:%d valid:%u atkspd:%u riding:%s",
  1373. ch->GetName(),
  1374. bArg,
  1375. ComboInterval,
  1376. ch->GetValidComboInterval(),
  1377. ch->GetPoint(POINT_ATT_SPEED),
  1378. ch->IsRiding() ? "yes" : "no");
  1379. }
  1380. // 말을 탔을 때는 15번 ~ 16번을 반복한다
  1381. //if (ch->IsHorseRiding())
  1382. if (ch->IsRiding())
  1383. ch->SetComboSequence(ch->GetComboSequence() == 1 ? 2 : 1);
  1384. else
  1385. ch->SetComboSequence(ch->GetComboSequence() + 1);
  1386. ch->SetValidComboInterval((int) (ani_combo_speed(ch, bArg - 13) / (ch->GetPoint(POINT_ATT_SPEED) / 100.f)));
  1387. ch->SetLastComboTime(dwTime);
  1388. }
  1389. }
  1390. else if (bArg == 13) // 기본 공격 (둔갑(Polymorph)했을 때 온다)
  1391. {
  1392. if (CheckSpeedHack && ComboInterval > 0 && ComboInterval < ch->GetValidComboInterval() - COMBO_HACK_ALLOWABLE_MS)
  1393. {
  1394. // 다수의 몬스터에 의해 다운되는 상황에서 공격을 하면
  1395. // 첫번째 콤보가 매우 적은 인터벌로 들어오는 상황 발생.
  1396. // 이로 인해 콤보핵으로 튕기는 경우가 있어 다음 코드 비 활성화.
  1397. //HackScalar = 1 + (ch->GetValidComboInterval() - ComboInterval) / 100;
  1398. //sys_log(0, "COMBO_HACK: 6 %s arg:%u interval:%d valid:%u atkspd:%u",
  1399. // ch->GetName(),
  1400. // bArg,
  1401. // ComboInterval,
  1402. // ch->GetValidComboInterval(),
  1403. // ch->GetPoint(POINT_ATT_SPEED));
  1404. }
  1405. if (ch->GetRaceNum() >= MAIN_RACE_MAX_NUM)
  1406. {
  1407. // POLYMORPH_BUG_FIX
  1408. // DELETEME
  1409. /*
  1410. const CMotion * pkMotion = CMotionManager::instance().GetMotion(ch->GetRaceNum(), MAKE_MOTION_KEY(MOTION_MODE_GENERAL, MOTION_NORMAL_ATTACK));
  1411. if (!pkMotion)
  1412. sys_err("cannot find motion by race %u", ch->GetRaceNum());
  1413. else
  1414. {
  1415. // 정상적 계산이라면 1000.f를 곱해야 하지만 클라이언트가 애니메이션 속도의 90%에서
  1416. // 다음 애니메이션 블렌딩을 허용하므로 900.f를 곱한다.
  1417. int k = (int) (pkMotion->GetDuration() / ((float) ch->GetPoint(POINT_ATT_SPEED) / 100.f) * 900.f);
  1418. ch->SetValidComboInterval(k);
  1419. ch->SetLastComboTime(dwTime);
  1420. }
  1421. */
  1422. float normalAttackDuration = CMotionManager::instance().GetNormalAttackDuration(ch->GetRaceNum());
  1423. int k = (int) (normalAttackDuration / ((float) ch->GetPoint(POINT_ATT_SPEED) / 100.f) * 900.f);
  1424. ch->SetValidComboInterval(k);
  1425. ch->SetLastComboTime(dwTime);
  1426. // END_OF_POLYMORPH_BUG_FIX
  1427. }
  1428. else
  1429. {
  1430. // 말이 안되는 콤보가 왔다 해커일 가능성?
  1431. //if (ch->GetDesc()->DelayedDisconnect(number(2, 9)))
  1432. //{
  1433. // LogManager::instance().HackLog("Hacker", ch);
  1434. // sys_log(0, "HACKER: %s arg %u", ch->GetName(), bArg);
  1435. //}
  1436. // 위 코드로 인해, 폴리모프를 푸는 중에 공격 하면,
  1437. // 가끔 핵으로 인식하는 경우가 있다.
  1438. // 자세히 말혀면,
  1439. // 서버에서 poly 0를 처리했지만,
  1440. // 클라에서 그 패킷을 받기 전에, 몹을 공격. <- 즉, 몹인 상태에서 공격.
  1441. //
  1442. // 그러면 클라에서는 서버에 몹 상태로 공격했다는 커맨드를 보내고 (arg == 13)
  1443. //
  1444. // 서버에서는 race는 인간인데 공격형태는 몹인 놈이다! 라고 하여 핵체크를 했다.
  1445. // 사실 공격 패턴에 대한 것은 클라이언트에서 판단해서 보낼 것이 아니라,
  1446. // 서버에서 판단해야 할 것인데... 왜 이렇게 해놨을까...
  1447. // by rtsummit
  1448. }
  1449. }
  1450. else
  1451. {
  1452. // 말이 안되는 콤보가 왔다 해커일 가능성?
  1453. if (ch->GetDesc()->DelayedDisconnect(number(2, 9)))
  1454. {
  1455. LogManager::instance().HackLog("Hacker", ch);
  1456. sys_log(0, "HACKER: %s arg %u", ch->GetName(), bArg);
  1457. }
  1458. HackScalar = 10;
  1459. ch->SetValidComboInterval(300);
  1460. }
  1461. if (HackScalar)
  1462. {
  1463. // 말에 타거나 내렸을 때 1.5초간 공격은 핵으로 간주하지 않되 공격력은 없게 하는 처리
  1464. if (get_dword_time() - ch->GetLastMountTime() > 1500)
  1465. ch->IncreaseComboHackCount(1 + HackScalar);
  1466. ch->SkipComboAttackByTime(ch->GetValidComboInterval());
  1467. }
  1468. return HackScalar;
  1469. }
  1470. void CInputMain::Move(LPCHARACTER ch, const char * data)
  1471. {
  1472. if (!ch->CanMove())
  1473. return;
  1474. struct command_move * pinfo = (struct command_move *) data;
  1475. if (pinfo->bFunc >= FUNC_MAX_NUM && !(pinfo->bFunc & 0x80))
  1476. {
  1477. sys_err("invalid move type: %s", ch->GetName());
  1478. return;
  1479. }
  1480. //enum EMoveFuncType
  1481. //{
  1482. // FUNC_WAIT,
  1483. // FUNC_MOVE,
  1484. // FUNC_ATTACK,
  1485. // FUNC_COMBO,
  1486. // FUNC_MOB_SKILL,
  1487. // _FUNC_SKILL,
  1488. // FUNC_MAX_NUM,
  1489. // FUNC_SKILL = 0x80,
  1490. //};
  1491. // 텔레포트 핵 체크
  1492. // if (!test_server)
  1493. {
  1494. const float fDist = DISTANCE_SQRT((ch->GetX() - pinfo->lX) / 100, (ch->GetY() - pinfo->lY) / 100);
  1495. // @fixme106 (changed 40 to 60)
  1496. if (((false == ch->IsRiding() && fDist > 25) || fDist > 60) && OXEVENT_MAP_INDEX != ch->GetMapIndex())
  1497. {
  1498. #ifdef ENABLE_HACK_TELEPORT_LOG // @warme006
  1499. {
  1500. const PIXEL_POSITION & warpPos = ch->GetWarpPosition();
  1501. if (warpPos.x == 0 && warpPos.y == 0)
  1502. LogManager::instance().HackLog("Teleport", ch); // 부정확할 수 있음
  1503. }
  1504. #endif
  1505. sys_log(0, "MOVE: %s trying to move too far (dist: %.1fm) Riding(%d)", ch->GetName(), fDist, ch->IsRiding());
  1506. ch->Show(ch->GetMapIndex(), ch->GetX(), ch->GetY(), ch->GetZ());
  1507. ch->Stop();
  1508. return;
  1509. }
  1510. #ifdef ENABLE_CHECK_GHOSTMODE
  1511. if (ch->IsPC() && ch->IsDead())
  1512. {
  1513. sys_log(0, "MOVE: %s trying to move as dead", ch->GetName());
  1514. ch->Show(ch->GetMapIndex(), ch->GetX(), ch->GetY(), ch->GetZ());
  1515. ch->Stop();
  1516. return;
  1517. }
  1518. #endif
  1519. //
  1520. // 스피드핵(SPEEDHACK) Check
  1521. //
  1522. DWORD dwCurTime = get_dword_time();
  1523. // 시간을 Sync하고 7초 후 부터 검사한다. (20090702 이전엔 5초였음)
  1524. bool CheckSpeedHack = (false == ch->GetDesc()->IsHandshaking() && dwCurTime - ch->GetDesc()->GetClientTime() > 7000);
  1525. if (CheckSpeedHack)
  1526. {
  1527. int iDelta = (int) (pinfo->dwTime - ch->GetDesc()->GetClientTime());
  1528. int iServerDelta = (int) (dwCurTime - ch->GetDesc()->GetClientTime());
  1529. iDelta = (int) (dwCurTime - pinfo->dwTime);
  1530. // 시간이 늦게간다. 일단 로그만 해둔다. 진짜 이런 사람들이 많은지 체크해야함. TODO
  1531. if (iDelta >= 30000)
  1532. {
  1533. sys_log(0, "SPEEDHACK: slow timer name %s delta %d", ch->GetName(), iDelta);
  1534. ch->GetDesc()->DelayedDisconnect(3);
  1535. }
  1536. // 1초에 20msec 빨리 가는거 까지는 이해한다.
  1537. else if (iDelta < -(iServerDelta / 50))
  1538. {
  1539. sys_log(0, "SPEEDHACK: DETECTED! %s (delta %d %d)", ch->GetName(), iDelta, iServerDelta);
  1540. ch->GetDesc()->DelayedDisconnect(3);
  1541. }
  1542. }
  1543. //
  1544. // 콤보핵 및 스피드핵 체크
  1545. //
  1546. if (pinfo->bFunc == FUNC_COMBO && g_bCheckMultiHack)
  1547. {
  1548. CheckComboHack(ch, pinfo->bArg, pinfo->dwTime, CheckSpeedHack); // 콤보 체크
  1549. }
  1550. }
  1551. if (pinfo->bFunc == FUNC_MOVE)
  1552. {
  1553. if (ch->GetLimitPoint(POINT_MOV_SPEED) == 0)
  1554. return;
  1555. ch->SetRotation(pinfo->bRot * 5); // 중복 코드
  1556. ch->ResetStopTime(); // ""
  1557. ch->Goto(pinfo->lX, pinfo->lY);
  1558. }
  1559. else
  1560. {
  1561. if (pinfo->bFunc == FUNC_ATTACK || pinfo->bFunc == FUNC_COMBO)
  1562. ch->OnMove(true);
  1563. else if (pinfo->bFunc & FUNC_SKILL)
  1564. {
  1565. const int MASK_SKILL_MOTION = 0x7F;
  1566. unsigned int motion = pinfo->bFunc & MASK_SKILL_MOTION;
  1567. if (!ch->IsUsableSkillMotion(motion))
  1568. {
  1569. const char* name = ch->GetName();
  1570. unsigned int job = ch->GetJob();
  1571. unsigned int group = ch->GetSkillGroup();
  1572. char szBuf[256];
  1573. snprintf(szBuf, sizeof(szBuf), "SKILL_HACK: name=%s, job=%d, group=%d, motion=%d", name, job, group, motion);
  1574. LogManager::instance().HackLog(szBuf, ch->GetDesc()->GetAccountTable().login, ch->GetName(), ch->GetDesc()->GetHostName());
  1575. sys_log(0, "%s", szBuf);
  1576. if (test_server)
  1577. {
  1578. ch->GetDesc()->DelayedDisconnect(number(2, 8));
  1579. ch->ChatPacket(CHAT_TYPE_INFO, szBuf);
  1580. }
  1581. else
  1582. {
  1583. ch->GetDesc()->DelayedDisconnect(number(150, 500));
  1584. }
  1585. }
  1586. ch->OnMove();
  1587. }
  1588. ch->SetRotation(pinfo->bRot * 5); // 중복 코드
  1589. ch->ResetStopTime(); // ""
  1590. ch->Move(pinfo->lX, pinfo->lY);
  1591. ch->Stop();
  1592. ch->StopStaminaConsume();
  1593. }
  1594. TPacketGCMove pack;
  1595. pack.bHeader = HEADER_GC_MOVE;
  1596. pack.bFunc = pinfo->bFunc;
  1597. pack.bArg = pinfo->bArg;
  1598. pack.bRot = pinfo->bRot;
  1599. pack.dwVID = ch->GetVID();
  1600. pack.lX = pinfo->lX;
  1601. pack.lY = pinfo->lY;
  1602. pack.dwTime = pinfo->dwTime;
  1603. pack.dwDuration = (pinfo->bFunc == FUNC_MOVE) ? ch->GetCurrentMoveDuration() : 0;
  1604. ch->PacketAround(&pack, sizeof(TPacketGCMove), ch);
  1605. /*
  1606. if (pinfo->dwTime == 10653691) // 디버거 발견
  1607. {
  1608. if (ch->GetDesc()->DelayedDisconnect(number(15, 30)))
  1609. LogManager::instance().HackLog("Debugger", ch);
  1610. }
  1611. else if (pinfo->dwTime == 10653971) // Softice 발견
  1612. {
  1613. if (ch->GetDesc()->DelayedDisconnect(number(15, 30)))
  1614. LogManager::instance().HackLog("Softice", ch);
  1615. }
  1616. */
  1617. /*
  1618. sys_log(0,
  1619. "MOVE: %s Func:%u Arg:%u Pos:%dx%d Time:%u Dist:%.1f",
  1620. ch->GetName(),
  1621. pinfo->bFunc,
  1622. pinfo->bArg,
  1623. pinfo->lX / 100,
  1624. pinfo->lY / 100,
  1625. pinfo->dwTime,
  1626. fDist);
  1627. */
  1628. }
  1629. void CInputMain::Attack(LPCHARACTER ch, const BYTE header, const char* data)
  1630. {
  1631. if (NULL == ch)
  1632. return;
  1633. struct type_identifier
  1634. {
  1635. BYTE header;
  1636. BYTE type;
  1637. };
  1638. const struct type_identifier* const type = reinterpret_cast<const struct type_identifier*>(data);
  1639. if (type->type > 0)
  1640. {
  1641. if (false == ch->CanUseSkill(type->type))
  1642. {
  1643. return;
  1644. }
  1645. switch (type->type)
  1646. {
  1647. case SKILL_GEOMPUNG:
  1648. case SKILL_SANGONG:
  1649. case SKILL_YEONSA:
  1650. case SKILL_KWANKYEOK:
  1651. case SKILL_HWAJO:
  1652. case SKILL_GIGUNG:
  1653. case SKILL_PABEOB:
  1654. case SKILL_MARYUNG:
  1655. case SKILL_TUSOK:
  1656. case SKILL_MAHWAN:
  1657. case SKILL_BIPABU:
  1658. case SKILL_NOEJEON:
  1659. case SKILL_CHAIN:
  1660. case SKILL_HORSE_WILDATTACK_RANGE:
  1661. if (HEADER_CG_SHOOT != type->header)
  1662. {
  1663. if (test_server)
  1664. ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("Attack :name[%s] Vnum[%d] can't use skill by attack(warning)"), type->type);
  1665. return;
  1666. }
  1667. break;
  1668. }
  1669. }
  1670. switch (header)
  1671. {
  1672. case HEADER_CG_ATTACK:
  1673. {
  1674. if (NULL == ch->GetDesc())
  1675. return;
  1676. const TPacketCGAttack* const packMelee = reinterpret_cast<const TPacketCGAttack*>(data);
  1677. ch->GetDesc()->AssembleCRCMagicCube(packMelee->bCRCMagicCubeProcPiece, packMelee->bCRCMagicCubeFilePiece);
  1678. LPCHARACTER victim = CHARACTER_MANAGER::instance().Find(packMelee->dwVID);
  1679. if (NULL == victim || ch == victim)
  1680. return;
  1681. switch (victim->GetCharType())
  1682. {
  1683. case CHAR_TYPE_NPC:
  1684. case CHAR_TYPE_WARP:
  1685. case CHAR_TYPE_GOTO:
  1686. return;
  1687. }
  1688. if (packMelee->bType > 0)
  1689. {
  1690. if (false == ch->CheckSkillHitCount(packMelee->bType, victim->GetVID()))
  1691. {
  1692. return;
  1693. }
  1694. }
  1695. ch->Attack(victim, packMelee->bType);
  1696. }
  1697. break;
  1698. case HEADER_CG_SHOOT:
  1699. {
  1700. const TPacketCGShoot* const packShoot = reinterpret_cast<const TPacketCGShoot*>(data);
  1701. ch->Shoot(packShoot->bType);
  1702. }
  1703. break;
  1704. }
  1705. }
  1706. int CInputMain::SyncPosition(LPCHARACTER ch, const char * c_pcData, size_t uiBytes)
  1707. {
  1708. const TPacketCGSyncPosition* pinfo = reinterpret_cast<const TPacketCGSyncPosition*>( c_pcData );
  1709. if (uiBytes < pinfo->wSize)
  1710. return -1;
  1711. int iExtraLen = pinfo->wSize - sizeof(TPacketCGSyncPosition);
  1712. if (iExtraLen < 0)
  1713. {
  1714. sys_err("invalid packet length (len %d size %u buffer %u)", iExtraLen, pinfo->wSize, uiBytes);
  1715. ch->GetDesc()->SetPhase(PHASE_CLOSE);
  1716. return -1;
  1717. }
  1718. if (0 != (iExtraLen % sizeof(TPacketCGSyncPositionElement)))
  1719. {
  1720. sys_err("invalid packet length %d (name: %s)", pinfo->wSize, ch->GetName());
  1721. return iExtraLen;
  1722. }
  1723. int iCount = iExtraLen / sizeof(TPacketCGSyncPositionElement);
  1724. if (iCount <= 0)
  1725. return iExtraLen;
  1726. static const int nCountLimit = 16;
  1727. if( iCount > nCountLimit )
  1728. {
  1729. //LogManager::instance().HackLog( "SYNC_POSITION_HACK", ch );
  1730. sys_err( "Too many SyncPosition Count(%d) from Name(%s)", iCount, ch->GetName() );
  1731. //ch->GetDesc()->SetPhase(PHASE_CLOSE);
  1732. //return -1;
  1733. iCount = nCountLimit;
  1734. }
  1735. TEMP_BUFFER tbuf;
  1736. LPBUFFER lpBuf = tbuf.getptr();
  1737. TPacketGCSyncPosition * pHeader = (TPacketGCSyncPosition *) buffer_write_peek(lpBuf);
  1738. buffer_write_proceed(lpBuf, sizeof(TPacketGCSyncPosition));
  1739. const TPacketCGSyncPositionElement* e =
  1740. reinterpret_cast<const TPacketCGSyncPositionElement*>(c_pcData + sizeof(TPacketCGSyncPosition));
  1741. timeval tvCurTime;
  1742. gettimeofday(&tvCurTime, NULL);
  1743. for (int i = 0; i < iCount; ++i, ++e)
  1744. {
  1745. LPCHARACTER victim = CHARACTER_MANAGER::instance().Find(e->dwVID);
  1746. if (!victim)
  1747. continue;
  1748. switch (victim->GetCharType())
  1749. {
  1750. case CHAR_TYPE_NPC:
  1751. case CHAR_TYPE_WARP:
  1752. case CHAR_TYPE_GOTO:
  1753. continue;
  1754. }
  1755. // 소유권 검사
  1756. if (!victim->SetSyncOwner(ch))
  1757. continue;
  1758. const float fDistWithSyncOwner = DISTANCE_SQRT( (victim->GetX() - ch->GetX()) / 100, (victim->GetY() - ch->GetY()) / 100 );
  1759. static const float fLimitDistWithSyncOwner = 2500.f + 1000.f;
  1760. // victim과의 거리가 2500 + a 이상이면 핵으로 간주.
  1761. // 거리 참조 : 클라이언트의 __GetSkillTargetRange, __GetBowRange 함수
  1762. // 2500 : 스킬 proto에서 가장 사거리가 긴 스킬의 사거리, 또는 활의 사거리
  1763. // a = POINT_BOW_DISTANCE 값... 인데 실제로 사용하는 값인지는 잘 모르겠음. 아이템이나 포션, 스킬, 퀘스트에는 없는데...
  1764. // 그래도 혹시나 하는 마음에 버퍼로 사용할 겸해서 1000.f 로 둠...
  1765. if (fDistWithSyncOwner > fLimitDistWithSyncOwner)
  1766. {
  1767. // g_iSyncHackLimitCount번 까지는 봐줌.
  1768. if (ch->GetSyncHackCount() < g_iSyncHackLimitCount)
  1769. {
  1770. ch->SetSyncHackCount(ch->GetSyncHackCount() + 1);
  1771. continue;
  1772. }
  1773. else
  1774. {
  1775. LogManager::instance().HackLog( "SYNC_POSITION_HACK", ch );
  1776. sys_err( "Too far SyncPosition DistanceWithSyncOwner(%f)(%s) from Name(%s) CH(%d,%d) VICTIM(%d,%d) SYNC(%d,%d)",
  1777. fDistWithSyncOwner, victim->GetName(), ch->GetName(), ch->GetX(), ch->GetY(), victim->GetX(), victim->GetY(),
  1778. e->lX, e->lY );
  1779. ch->GetDesc()->SetPhase(PHASE_CLOSE);
  1780. return -1;
  1781. }
  1782. }
  1783. const float fDist = DISTANCE_SQRT( (victim->GetX() - e->lX) / 100, (victim->GetY() - e->lY) / 100 );
  1784. static const long g_lValidSyncInterval = 100 * 1000; // 100ms
  1785. const timeval &tvLastSyncTime = victim->GetLastSyncTime();
  1786. timeval *tvDiff = timediff(&tvCurTime, &tvLastSyncTime);
  1787. // SyncPosition을 악용하여 타유저를 이상한 곳으로 보내는 핵 방어하기 위하여,
  1788. // 같은 유저를 g_lValidSyncInterval ms 이내에 다시 SyncPosition하려고 하면 핵으로 간주.
  1789. if (tvDiff->tv_sec == 0 && tvDiff->tv_usec < g_lValidSyncInterval)
  1790. {
  1791. // g_iSyncHackLimitCount번 까지는 봐줌.
  1792. if (ch->GetSyncHackCount() < g_iSyncHackLimitCount)
  1793. {
  1794. ch->SetSyncHackCount(ch->GetSyncHackCount() + 1);
  1795. continue;
  1796. }
  1797. else
  1798. {
  1799. LogManager::instance().HackLog( "SYNC_POSITION_HACK", ch );
  1800. sys_err( "Too often SyncPosition Interval(%ldms)(%s) from Name(%s) VICTIM(%d,%d) SYNC(%d,%d)",
  1801. tvDiff->tv_sec * 1000 + tvDiff->tv_usec / 1000, victim->GetName(), ch->GetName(), victim->GetX(), victim->GetY(),
  1802. e->lX, e->lY );
  1803. ch->GetDesc()->SetPhase(PHASE_CLOSE);
  1804. return -1;
  1805. }
  1806. }
  1807. else if( fDist > 25.0f )
  1808. {
  1809. LogManager::instance().HackLog( "SYNC_POSITION_HACK", ch );
  1810. sys_err( "Too far SyncPosition Distance(%f)(%s) from Name(%s) CH(%d,%d) VICTIM(%d,%d) SYNC(%d,%d)",
  1811. fDist, victim->GetName(), ch->GetName(), ch->GetX(), ch->GetY(), victim->GetX(), victim->GetY(),
  1812. e->lX, e->lY );
  1813. ch->GetDesc()->SetPhase(PHASE_CLOSE);
  1814. return -1;
  1815. }
  1816. else
  1817. {
  1818. victim->SetLastSyncTime(tvCurTime);
  1819. victim->Sync(e->lX, e->lY);
  1820. buffer_write(lpBuf, e, sizeof(TPacketCGSyncPositionElement));
  1821. }
  1822. }
  1823. if (buffer_size(lpBuf) != sizeof(TPacketGCSyncPosition))
  1824. {
  1825. pHeader->bHeader = HEADER_GC_SYNC_POSITION;
  1826. pHeader->wSize = buffer_size(lpBuf);
  1827. ch->PacketAround(buffer_read_peek(lpBuf), buffer_size(lpBuf), ch);
  1828. }
  1829. return iExtraLen;
  1830. }
  1831. void CInputMain::FlyTarget(LPCHARACTER ch, const char * pcData, BYTE bHeader)
  1832. {
  1833. TPacketCGFlyTargeting * p = (TPacketCGFlyTargeting *) pcData;
  1834. ch->FlyTarget(p->dwTargetVID, p->x, p->y, bHeader);
  1835. }
  1836. void CInputMain::UseSkill(LPCHARACTER ch, const char * pcData)
  1837. {
  1838. TPacketCGUseSkill * p = (TPacketCGUseSkill *) pcData;
  1839. ch->UseSkill(p->dwVnum, CHARACTER_MANAGER::instance().Find(p->dwVID));
  1840. }
  1841. void CInputMain::ScriptButton(LPCHARACTER ch, const void* c_pData)
  1842. {
  1843. TPacketCGScriptButton * p = (TPacketCGScriptButton *) c_pData;
  1844. sys_log(0, "QUEST ScriptButton pid %d idx %u", ch->GetPlayerID(), p->idx);
  1845. quest::PC* pc = quest::CQuestManager::instance().GetPCForce(ch->GetPlayerID());
  1846. if (pc && pc->IsConfirmWait())
  1847. {
  1848. quest::CQuestManager::instance().Confirm(ch->GetPlayerID(), quest::CONFIRM_TIMEOUT);
  1849. }
  1850. else if (p->idx & 0x80000000)
  1851. {
  1852. //퀘스트 창에서 클릭시(__SelectQuest) 여기로
  1853. quest::CQuestManager::Instance().QuestInfo(ch->GetPlayerID(), p->idx & 0x7fffffff);
  1854. }
  1855. else
  1856. {
  1857. quest::CQuestManager::Instance().QuestButton(ch->GetPlayerID(), p->idx);
  1858. }
  1859. }
  1860. void CInputMain::ScriptAnswer(LPCHARACTER ch, const void* c_pData)
  1861. {
  1862. TPacketCGScriptAnswer * p = (TPacketCGScriptAnswer *) c_pData;
  1863. sys_log(0, "QUEST ScriptAnswer pid %d answer %d", ch->GetPlayerID(), p->answer);
  1864. if (p->answer > 250) // 다음 버튼에 대한 응답으로 온 패킷인 경우
  1865. {
  1866. quest::CQuestManager::Instance().Resume(ch->GetPlayerID());
  1867. }
  1868. else // 선택 버튼을 골라서 온 패킷인 경우
  1869. {
  1870. quest::CQuestManager::Instance().Select(ch->GetPlayerID(), p->answer);
  1871. }
  1872. }
  1873. // SCRIPT_SELECT_ITEM
  1874. void CInputMain::ScriptSelectItem(LPCHARACTER ch, const void* c_pData)
  1875. {
  1876. TPacketCGScriptSelectItem* p = (TPacketCGScriptSelectItem*) c_pData;
  1877. sys_log(0, "QUEST ScriptSelectItem pid %d answer %d", ch->GetPlayerID(), p->selection);
  1878. quest::CQuestManager::Instance().SelectItem(ch->GetPlayerID(), p->selection);
  1879. }
  1880. // END_OF_SCRIPT_SELECT_ITEM
  1881. void CInputMain::QuestInputString(LPCHARACTER ch, const void* c_pData)
  1882. {
  1883. TPacketCGQuestInputString * p = (TPacketCGQuestInputString*) c_pData;
  1884. char msg[65];
  1885. strlcpy(msg, p->msg, sizeof(msg));
  1886. sys_log(0, "QUEST InputString pid %u msg %s", ch->GetPlayerID(), msg);
  1887. quest::CQuestManager::Instance().Input(ch->GetPlayerID(), msg);
  1888. }
  1889. void CInputMain::QuestConfirm(LPCHARACTER ch, const void* c_pData)
  1890. {
  1891. TPacketCGQuestConfirm* p = (TPacketCGQuestConfirm*) c_pData;
  1892. LPCHARACTER ch_wait = CHARACTER_MANAGER::instance().FindByPID(p->requestPID);
  1893. if (p->answer)
  1894. p->answer = quest::CONFIRM_YES;
  1895. sys_log(0, "QuestConfirm from %s pid %u name %s answer %d", ch->GetName(), p->requestPID, (ch_wait)?ch_wait->GetName():"", p->answer);
  1896. if (ch_wait)
  1897. {
  1898. quest::CQuestManager::Instance().Confirm(ch_wait->GetPlayerID(), (quest::EQuestConfirmType) p->answer, ch->GetPlayerID());
  1899. }
  1900. }
  1901. void CInputMain::Target(LPCHARACTER ch, const char * pcData)
  1902. {
  1903. TPacketCGTarget * p = (TPacketCGTarget *) pcData;
  1904. building::LPOBJECT pkObj = building::CManager::instance().FindObjectByVID(p->dwVID);
  1905. if (pkObj)
  1906. {
  1907. TPacketGCTarget pckTarget;
  1908. pckTarget.header = HEADER_GC_TARGET;
  1909. pckTarget.dwVID = p->dwVID;
  1910. ch->GetDesc()->Packet(&pckTarget, sizeof(TPacketGCTarget));
  1911. }
  1912. else
  1913. ch->SetTarget(CHARACTER_MANAGER::instance().Find(p->dwVID));
  1914. }
  1915. void CInputMain::Warp(LPCHARACTER ch, const char * pcData)
  1916. {
  1917. ch->WarpEnd();
  1918. }
  1919. void CInputMain::SafeboxCheckin(LPCHARACTER ch, const char * c_pData)
  1920. {
  1921. if (quest::CQuestManager::instance().GetPCForce(ch->GetPlayerID())->IsRunning() == true)
  1922. return;
  1923. TPacketCGSafeboxCheckin * p = (TPacketCGSafeboxCheckin *) c_pData;
  1924. if (!ch->CanHandleItem())
  1925. return;
  1926. {
  1927. CSafebox * pkSafebox = ch->GetSafebox();
  1928. LPITEM pkItem = ch->GetItem(p->ItemPos);
  1929. if (!pkSafebox || !pkItem)
  1930. LPITEM pkItemNew = ch->GetWear(WEAR_COSTUME_WEAPON);
  1931. return;
  1932. {
  1933. if (pkItem->GetCell() >= INVENTORY_MAX_NUM && IS_SET(pkItem->GetFlag(), ITEM_FLAG_IRREMOVABLE))
  1934. {
  1935. ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<창고> 창고로 옮길 수 없는 아이템 입니다."));
  1936. return;
  1937. }
  1938. {
  1939. if (pkItem->GetType() == ITEM_BELT && pkItem->IsEquipped())
  1940. ch->ChatPacket(CHAT_TYPE_INFO, "Once kemer Envanterini bosalt!");
  1941. return;
  1942. }
  1943. if (!pkSafebox->IsEmpty(p->bSafePos, pkItem->GetSize()))
  1944. {
  1945. ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<창고> 옮길 수 없는 위치입니다."));
  1946. return;
  1947. }
  1948. if (pkItem->GetVnum() == UNIQUE_ITEM_SAFEBOX_EXPAND)
  1949. {
  1950. ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<창고> 이 아이템은 넣을 수 없습니다."));
  1951. return;
  1952. }
  1953. if( IS_SET(pkItem->GetAntiFlag(), ITEM_ANTIFLAG_SAFEBOX) )
  1954. {
  1955. ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<창고> 이 아이템은 넣을 수 없습니다."));
  1956. return;
  1957. }
  1958. if (true == pkItem->isLocked())
  1959. {
  1960. ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<창고> 이 아이템은 넣을 수 없습니다."));
  1961. if (pkItemNew)
  1962. {
  1963. ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("UYARI_KOSTUM"));
  1964. return;
  1965. }
  1966. pkItem->RemoveFromCharacter();
  1967. if (!pkItem->IsDragonSoul())
  1968. ch->SyncQuickslot(QUICKSLOT_TYPE_ITEM, p->ItemPos.cell, 255);
  1969. pkSafebox->Add(p->bSafePos, pkItem);
  1970. char szHint[128];
  1971. snprintf(szHint, sizeof(szHint), "%s %u", pkItem->GetName(), pkItem->GetCount());
  1972. LogManager::instance().ItemLog(ch, pkItem, "SAFEBOX PUT", szHint);
  1973. }
  1974. void CInputMain::SafeboxCheckout(LPCHARACTER ch, const char * c_pData, bool bMall)
  1975. {
  1976. TPacketCGSafeboxCheckout * p = (TPacketCGSafeboxCheckout *) c_pData;
  1977. if (!ch->CanHandleItem())
  1978. return;
  1979. CSafebox * pkSafebox;
  1980. if (bMall)
  1981. pkSafebox = ch->GetMall();
  1982. else
  1983. pkSafebox = ch->GetSafebox();
  1984. if (!pkSafebox)
  1985. return;
  1986. LPITEM pkItem = pkSafebox->Get(p->bSafePos);
  1987. if (!pkItem)
  1988. return;
  1989. if (!ch->IsEmptyItemGrid(p->ItemPos, pkItem->GetSize()))
  1990. return;
  1991. // 아이템 몰에서 인벤으로 옮기는 부분에서 용혼석 특수 처리
  1992. // (몰에서 만드는 아이템은 item_proto에 정의된대로 속성이 붙기 때문에,
  1993. // 용혼석의 경우, 이 처리를 하지 않으면 속성이 하나도 붙지 않게 된다.)
  1994. if (pkItem->IsDragonSoul())
  1995. {
  1996. if (bMall)
  1997. {
  1998. DSManager::instance().DragonSoulItemInitialize(pkItem);
  1999. }
  2000. if (DRAGON_SOUL_INVENTORY != p->ItemPos.window_type)
  2001. {
  2002. ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<창고> 옮길 수 없는 위치입니다."));
  2003. return;
  2004. }
  2005. TItemPos DestPos = p->ItemPos;
  2006. if (!DSManager::instance().IsValidCellForThisItem(pkItem, DestPos))
  2007. {
  2008. int iCell = ch->GetEmptyDragonSoulInventory(pkItem);
  2009. if (iCell < 0)
  2010. {
  2011. ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<창고> 옮길 수 없는 위치입니다."));
  2012. return ;
  2013. }
  2014. DestPos = TItemPos (DRAGON_SOUL_INVENTORY, iCell);
  2015. }
  2016. pkSafebox->Remove(p->bSafePos);
  2017. pkItem->AddToCharacter(ch, DestPos);
  2018. ITEM_MANAGER::instance().FlushDelayedSave(pkItem);
  2019. }
  2020. else
  2021. {
  2022. if (DRAGON_SOUL_INVENTORY == p->ItemPos.window_type)
  2023. {
  2024. ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<창고> 옮길 수 없는 위치입니다."));
  2025. return;
  2026. }
  2027. // @fixme119
  2028. if (p->ItemPos.IsBeltInventoryPosition() && false == CBeltInventoryHelper::CanMoveIntoBeltInventory(pkItem))
  2029. {
  2030. ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("이 아이템은 벨트 인벤토리로 옮길 수 없습니다."));
  2031. return;
  2032. }
  2033. pkSafebox->Remove(p->bSafePos);
  2034. pkItem->AddToCharacter(ch, p->ItemPos);
  2035. ITEM_MANAGER::instance().FlushDelayedSave(pkItem);
  2036. }
  2037. DWORD dwID = pkItem->GetID();
  2038. db_clientdesc->DBPacketHeader(HEADER_GD_ITEM_FLUSH, 0, sizeof(DWORD));
  2039. db_clientdesc->Packet(&dwID, sizeof(DWORD));
  2040. char szHint[128];
  2041. snprintf(szHint, sizeof(szHint), "%s %u", pkItem->GetName(), pkItem->GetCount());
  2042. if (bMall)
  2043. LogManager::instance().ItemLog(ch, pkItem, "MALL GET", szHint);
  2044. else
  2045. LogManager::instance().ItemLog(ch, pkItem, "SAFEBOX GET", szHint);
  2046. }
  2047. void CInputMain::SafeboxItemMove(LPCHARACTER ch, const char * data)
  2048. {
  2049. struct command_item_move * pinfo = (struct command_item_move *) data;
  2050. if (!ch->CanHandleItem())
  2051. return;
  2052. if (!ch->GetSafebox())
  2053. return;
  2054. ch->GetSafebox()->MoveItem(pinfo->Cell.cell, pinfo->CellTo.cell, pinfo->count);
  2055. }
  2056. // PARTY_JOIN_BUG_FIX
  2057. void CInputMain::PartyInvite(LPCHARACTER ch, const char * c_pData)
  2058. {
  2059. if (ch->GetArena())
  2060. {
  2061. ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("대련장에서 사용하실 수 없습니다."));
  2062. return;
  2063. }
  2064. TPacketCGPartyInvite * p = (TPacketCGPartyInvite*) c_pData;
  2065. LPCHARACTER pInvitee = CHARACTER_MANAGER::instance().Find(p->vid);
  2066. if (!pInvitee || !ch->GetDesc() || !pInvitee->GetDesc())
  2067. {
  2068. sys_err("PARTY Cannot find invited character");
  2069. return;
  2070. }
  2071. ch->PartyInvite(pInvitee);
  2072. }
  2073. void CInputMain::PartyInviteAnswer(LPCHARACTER ch, const char * c_pData)
  2074. {
  2075. if (ch->GetArena())
  2076. {
  2077. ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("대련장에서 사용하실 수 없습니다."));
  2078. return;
  2079. }
  2080. TPacketCGPartyInviteAnswer * p = (TPacketCGPartyInviteAnswer*) c_pData;
  2081. LPCHARACTER pInviter = CHARACTER_MANAGER::instance().Find(p->leader_vid);
  2082. // pInviter 가 ch 에게 파티 요청을 했었다.
  2083. if (!pInviter)
  2084. ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<파티> 파티요청을 한 캐릭터를 찾을수 없습니다."));
  2085. else if (!p->accept)
  2086. pInviter->PartyInviteDeny(ch->GetPlayerID());
  2087. else
  2088. pInviter->PartyInviteAccept(ch);
  2089. }
  2090. // END_OF_PARTY_JOIN_BUG_FIX
  2091. void CInputMain::PartySetState(LPCHARACTER ch, const char* c_pData)
  2092. {
  2093. if (!CPartyManager::instance().IsEnablePCParty())
  2094. {
  2095. ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<파티> 서버 문제로 파티 관련 처리를 할 수 없습니다."));
  2096. return;
  2097. }
  2098. TPacketCGPartySetState* p = (TPacketCGPartySetState*) c_pData;
  2099. if (!ch->GetParty())
  2100. return;
  2101. if (ch->GetParty()->GetLeaderPID() != ch->GetPlayerID())
  2102. {
  2103. ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<파티> 리더만 변경할 수 있습니다."));
  2104. return;
  2105. }
  2106. if (!ch->GetParty()->IsMember(p->pid))
  2107. {
  2108. ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<파티> 상태를 변경하려는 사람이 파티원이 아닙니다."));
  2109. return;
  2110. }
  2111. DWORD pid = p->pid;
  2112. sys_log(0, "PARTY SetRole pid %d to role %d state %s", pid, p->byRole, p->flag ? "on" : "off");
  2113. switch (p->byRole)
  2114. {
  2115. case PARTY_ROLE_NORMAL:
  2116. break;
  2117. case PARTY_ROLE_ATTACKER:
  2118. case PARTY_ROLE_TANKER:
  2119. case PARTY_ROLE_BUFFER:
  2120. case PARTY_ROLE_SKILL_MASTER:
  2121. case PARTY_ROLE_HASTE:
  2122. case PARTY_ROLE_DEFENDER:
  2123. if (ch->GetParty()->SetRole(pid, p->byRole, p->flag))
  2124. {
  2125. TPacketPartyStateChange pack;
  2126. pack.dwLeaderPID = ch->GetPlayerID();
  2127. pack.dwPID = p->pid;
  2128. pack.bRole = p->byRole;
  2129. pack.bFlag = p->flag;
  2130. db_clientdesc->DBPacket(HEADER_GD_PARTY_STATE_CHANGE, 0, &pack, sizeof(pack));
  2131. }
  2132. /* else
  2133. ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<파티> 어태커 설정에 실패하였습니다.")); */
  2134. break;
  2135. default:
  2136. sys_err("wrong byRole in PartySetState Packet name %s state %d", ch->GetName(), p->byRole);
  2137. break;
  2138. }
  2139. }
  2140. void CInputMain::PartyRemove(LPCHARACTER ch, const char* c_pData)
  2141. {
  2142. if (ch->GetArena())
  2143. {
  2144. ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("대련장에서 사용하실 수 없습니다."));
  2145. return;
  2146. }
  2147. if (!CPartyManager::instance().IsEnablePCParty())
  2148. {
  2149. ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<파티> 서버 문제로 파티 관련 처리를 할 수 없습니다."));
  2150. return;
  2151. }
  2152. if (ch->GetDungeon())
  2153. {
  2154. ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<파티> 던전 안에서는 파티에서 추방할 수 없습니다."));
  2155. return;
  2156. }
  2157. TPacketCGPartyRemove* p = (TPacketCGPartyRemove*) c_pData;
  2158. if (!ch->GetParty())
  2159. return;
  2160. LPPARTY pParty = ch->GetParty();
  2161. if (pParty->GetLeaderPID() == ch->GetPlayerID())
  2162. {
  2163. if (ch->GetDungeon())
  2164. {
  2165. ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<파티> 던젼내에서는 파티원을 추방할 수 없습니다."));
  2166. }
  2167. else
  2168. {
  2169. // 적룡성에서 파티장이 던젼 밖에서 파티 해산 못하게 막자
  2170. if(pParty->IsPartyInDungeon(351))
  2171. {
  2172. ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<파티>던전 안에 파티원이 있어 파티를 해산 할 수 없습니다."));
  2173. return;
  2174. }
  2175. // leader can remove any member
  2176. if (p->pid == ch->GetPlayerID() || pParty->GetMemberCount() == 2)
  2177. {
  2178. // party disband
  2179. CPartyManager::instance().DeleteParty(pParty);
  2180. }
  2181. else
  2182. {
  2183. LPCHARACTER B = CHARACTER_MANAGER::instance().FindByPID(p->pid);
  2184. if (B)
  2185. {
  2186. //pParty->SendPartyRemoveOneToAll(B);
  2187. B->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<파티> 파티에서 추방당하셨습니다."));
  2188. //pParty->Unlink(B);
  2189. //CPartyManager::instance().SetPartyMember(B->GetPlayerID(), NULL);
  2190. }
  2191. pParty->Quit(p->pid);
  2192. }
  2193. }
  2194. }
  2195. else
  2196. {
  2197. // otherwise, only remove itself
  2198. if (p->pid == ch->GetPlayerID())
  2199. {
  2200. if (ch->GetDungeon())
  2201. {
  2202. ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<파티> 던젼내에서는 파티를 나갈 수 없습니다."));
  2203. }
  2204. else
  2205. {
  2206. if (pParty->GetMemberCount() == 2)
  2207. {
  2208. // party disband
  2209. CPartyManager::instance().DeleteParty(pParty);
  2210. }
  2211. else
  2212. {
  2213. ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<파티> 파티에서 나가셨습니다."));
  2214. //pParty->SendPartyRemoveOneToAll(ch);
  2215. pParty->Quit(ch->GetPlayerID());
  2216. //pParty->SendPartyRemoveAllToOne(ch);
  2217. //CPartyManager::instance().SetPartyMember(ch->GetPlayerID(), NULL);
  2218. }
  2219. }
  2220. }
  2221. else
  2222. {
  2223. ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<파티> 다른 파티원을 탈퇴시킬 수 없습니다."));
  2224. }
  2225. }
  2226. }
  2227. void CInputMain::AnswerMakeGuild(LPCHARACTER ch, const char* c_pData)
  2228. {
  2229. TPacketCGAnswerMakeGuild* p = (TPacketCGAnswerMakeGuild*) c_pData;
  2230. if (ch->GetGold() < 200000)
  2231. return;
  2232. if (get_global_time() - ch->GetQuestFlag("guild_manage.new_disband_time") <
  2233. CGuildManager::instance().GetDisbandDelay())
  2234. {
  2235. ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<길드> 해산한 후 %d일 이내에는 길드를 만들 수 없습니다."),
  2236. quest::CQuestManager::instance().GetEventFlag("guild_disband_delay"));
  2237. return;
  2238. }
  2239. if (get_global_time() - ch->GetQuestFlag("guild_manage.new_withdraw_time") <
  2240. CGuildManager::instance().GetWithdrawDelay())
  2241. {
  2242. ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<길드> 탈퇴한 후 %d일 이내에는 길드를 만들 수 없습니다."),
  2243. quest::CQuestManager::instance().GetEventFlag("guild_withdraw_delay"));
  2244. return;
  2245. }
  2246. if (ch->GetGuild())
  2247. return;
  2248. CGuildManager& gm = CGuildManager::instance();
  2249. TGuildCreateParameter cp;
  2250. memset(&cp, 0, sizeof(cp));
  2251. cp.master = ch;
  2252. strlcpy(cp.name, p->guild_name, sizeof(cp.name));
  2253. if (cp.name[0] == 0 || !check_name(cp.name))
  2254. {
  2255. ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("적합하지 않은 길드 이름 입니다."));
  2256. return;
  2257. }
  2258. DWORD dwGuildID = gm.CreateGuild(cp);
  2259. if (dwGuildID)
  2260. {
  2261. ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<길드> [%s] 길드가 생성되었습니다."), cp.name);
  2262. int GuildCreateFee = 200000;
  2263. ch->PointChange(POINT_GOLD, -GuildCreateFee);
  2264. DBManager::instance().SendMoneyLog(MONEY_LOG_GUILD, ch->GetPlayerID(), -GuildCreateFee);
  2265. char Log[128];
  2266. snprintf(Log, sizeof(Log), "GUILD_NAME %s MASTER %s", cp.name, ch->GetName());
  2267. LogManager::instance().CharLog(ch, 0, "MAKE_GUILD", Log);
  2268. ch->RemoveSpecifyItem(GUILD_CREATE_ITEM_VNUM, 1);
  2269. //ch->SendGuildName(dwGuildID);
  2270. }
  2271. else
  2272. ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<길드> 길드 생성에 실패하였습니다."));
  2273. }
  2274. void CInputMain::PartyUseSkill(LPCHARACTER ch, const char* c_pData)
  2275. {
  2276. TPacketCGPartyUseSkill* p = (TPacketCGPartyUseSkill*) c_pData;
  2277. if (!ch->GetParty())
  2278. return;
  2279. if (ch->GetPlayerID() != ch->GetParty()->GetLeaderPID())
  2280. {
  2281. ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<파티> 파티 기술은 파티장만 사용할 수 있습니다."));
  2282. return;
  2283. }
  2284. switch (p->bySkillIndex)
  2285. {
  2286. case PARTY_SKILL_HEAL:
  2287. ch->GetParty()->HealParty();
  2288. break;
  2289. case PARTY_SKILL_WARP:
  2290. {
  2291. LPCHARACTER pch = CHARACTER_MANAGER::instance().Find(p->vid);
  2292. if (pch)
  2293. ch->GetParty()->SummonToLeader(pch->GetPlayerID());
  2294. else
  2295. ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<파티> 소환하려는 대상을 찾을 수 없습니다."));
  2296. }
  2297. break;
  2298. }
  2299. }
  2300. void CInputMain::PartyParameter(LPCHARACTER ch, const char * c_pData)
  2301. {
  2302. TPacketCGPartyParameter * p = (TPacketCGPartyParameter *) c_pData;
  2303. if (ch->GetParty())
  2304. ch->GetParty()->SetParameter(p->bDistributeMode);
  2305. }
  2306. size_t GetSubPacketSize(const GUILD_SUBHEADER_CG& header)
  2307. {
  2308. switch (header)
  2309. {
  2310. case GUILD_SUBHEADER_CG_DEPOSIT_MONEY: return sizeof(int);
  2311. case GUILD_SUBHEADER_CG_WITHDRAW_MONEY: return sizeof(int);
  2312. case GUILD_SUBHEADER_CG_ADD_MEMBER: return sizeof(DWORD);
  2313. case GUILD_SUBHEADER_CG_REMOVE_MEMBER: return sizeof(DWORD);
  2314. case GUILD_SUBHEADER_CG_CHANGE_GRADE_NAME: return 10;
  2315. case GUILD_SUBHEADER_CG_CHANGE_GRADE_AUTHORITY: return sizeof(BYTE) + sizeof(BYTE);
  2316. case GUILD_SUBHEADER_CG_OFFER: return sizeof(DWORD);
  2317. case GUILD_SUBHEADER_CG_CHARGE_GSP: return sizeof(int);
  2318. case GUILD_SUBHEADER_CG_POST_COMMENT: return 1;
  2319. case GUILD_SUBHEADER_CG_DELETE_COMMENT: return sizeof(DWORD);
  2320. case GUILD_SUBHEADER_CG_REFRESH_COMMENT: return 0;
  2321. case GUILD_SUBHEADER_CG_CHANGE_MEMBER_GRADE: return sizeof(DWORD) + sizeof(BYTE);
  2322. case GUILD_SUBHEADER_CG_USE_SKILL: return sizeof(TPacketCGGuildUseSkill);
  2323. case GUILD_SUBHEADER_CG_CHANGE_MEMBER_GENERAL: return sizeof(DWORD) + sizeof(BYTE);
  2324. case GUILD_SUBHEADER_CG_GUILD_INVITE_ANSWER: return sizeof(DWORD) + sizeof(BYTE);
  2325. }
  2326. return 0;
  2327. }
  2328. int CInputMain::Guild(LPCHARACTER ch, const char * data, size_t uiBytes)
  2329. {
  2330. if (uiBytes < sizeof(TPacketCGGuild))
  2331. return -1;
  2332. const TPacketCGGuild* p = reinterpret_cast<const TPacketCGGuild*>(data);
  2333. const char* c_pData = data + sizeof(TPacketCGGuild);
  2334. uiBytes -= sizeof(TPacketCGGuild);
  2335. const GUILD_SUBHEADER_CG SubHeader = static_cast<GUILD_SUBHEADER_CG>(p->subheader);
  2336. const size_t SubPacketLen = GetSubPacketSize(SubHeader);
  2337. if (uiBytes < SubPacketLen)
  2338. {
  2339. return -1;
  2340. }
  2341. CGuild* pGuild = ch->GetGuild();
  2342. if (NULL == pGuild)
  2343. {
  2344. if (SubHeader != GUILD_SUBHEADER_CG_GUILD_INVITE_ANSWER)
  2345. {
  2346. ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<길드> 길드에 속해있지 않습니다."));
  2347. return SubPacketLen;
  2348. }
  2349. }
  2350. switch (SubHeader)
  2351. {
  2352. case GUILD_SUBHEADER_CG_DEPOSIT_MONEY:
  2353. {
  2354. // by mhh : 길드자금은 당분간 넣을 수 없다.
  2355. return SubPacketLen;
  2356. const int gold = MIN(*reinterpret_cast<const int*>(c_pData), __deposit_limit());
  2357. if (gold < 0)
  2358. {
  2359. ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<길드> 잘못된 금액입니다."));
  2360. return SubPacketLen;
  2361. }
  2362. if (ch->GetGold() < gold)
  2363. {
  2364. ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<길드> 가지고 있는 돈이 부족합니다."));
  2365. return SubPacketLen;
  2366. }
  2367. pGuild->RequestDepositMoney(ch, gold);
  2368. }
  2369. return SubPacketLen;
  2370. case GUILD_SUBHEADER_CG_WITHDRAW_MONEY:
  2371. {
  2372. // by mhh : 길드자금은 당분간 뺄 수 없다.
  2373. return SubPacketLen;
  2374. const int gold = MIN(*reinterpret_cast<const int*>(c_pData), 500000);
  2375. if (gold < 0)
  2376. {
  2377. ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<길드> 잘못된 금액입니다."));
  2378. return SubPacketLen;
  2379. }
  2380. pGuild->RequestWithdrawMoney(ch, gold);
  2381. }
  2382. return SubPacketLen;
  2383. case GUILD_SUBHEADER_CG_ADD_MEMBER:
  2384. {
  2385. const DWORD vid = *reinterpret_cast<const DWORD*>(c_pData);
  2386. LPCHARACTER newmember = CHARACTER_MANAGER::instance().Find(vid);
  2387. if (!newmember)
  2388. {
  2389. ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<길드> 그러한 사람을 찾을 수 없습니다."));
  2390. return SubPacketLen;
  2391. }
  2392. if (!ch->IsPC())
  2393. return SubPacketLen;
  2394. pGuild->Invite(ch, newmember);
  2395. }
  2396. return SubPacketLen;
  2397. case GUILD_SUBHEADER_CG_REMOVE_MEMBER:
  2398. {
  2399. if (pGuild->UnderAnyWar() != 0)
  2400. {
  2401. ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<길드> 길드전 중에는 길드원을 탈퇴시킬 수 없습니다."));
  2402. return SubPacketLen;
  2403. }
  2404. const DWORD pid = *reinterpret_cast<const DWORD*>(c_pData);
  2405. const TGuildMember* m = pGuild->GetMember(ch->GetPlayerID());
  2406. if (NULL == m)
  2407. return -1;
  2408. LPCHARACTER member = CHARACTER_MANAGER::instance().FindByPID(pid);
  2409. if (member)
  2410. {
  2411. if (member->GetGuild() != pGuild)
  2412. {
  2413. ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<길드> 상대방이 같은 길드가 아닙니다."));
  2414. return SubPacketLen;
  2415. }
  2416. if (!pGuild->HasGradeAuth(m->grade, GUILD_AUTH_REMOVE_MEMBER))
  2417. {
  2418. ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<길드> 길드원을 강제 탈퇴 시킬 권한이 없습니다."));
  2419. return SubPacketLen;
  2420. }
  2421. member->SetQuestFlag("guild_manage.new_withdraw_time", get_global_time());
  2422. pGuild->RequestRemoveMember(member->GetPlayerID());
  2423. if (g_bGuildInviteLimit)
  2424. {
  2425. DBManager::instance().Query("REPLACE INTO guild_invite_limit VALUES(%d, %d)", pGuild->GetID(), get_global_time());
  2426. }
  2427. }
  2428. else
  2429. {
  2430. if (!pGuild->HasGradeAuth(m->grade, GUILD_AUTH_REMOVE_MEMBER))
  2431. {
  2432. ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<길드> 길드원을 강제 탈퇴 시킬 권한이 없습니다."));
  2433. return SubPacketLen;
  2434. }
  2435. if (pGuild->RequestRemoveMember(pid))
  2436. ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<길드> 길드원을 강제 탈퇴 시켰습니다."));
  2437. else
  2438. ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<길드> 그러한 사람을 찾을 수 없습니다."));
  2439. }
  2440. }
  2441. return SubPacketLen;
  2442. case GUILD_SUBHEADER_CG_CHANGE_GRADE_NAME:
  2443. {
  2444. char gradename[GUILD_GRADE_NAME_MAX_LEN + 1];
  2445. strlcpy(gradename, c_pData + 1, sizeof(gradename));
  2446. const TGuildMember * m = pGuild->GetMember(ch->GetPlayerID());
  2447. if (NULL == m)
  2448. return -1;
  2449. if (m->grade != GUILD_LEADER_GRADE)
  2450. {
  2451. ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<길드> 직위 이름을 변경할 권한이 없습니다."));
  2452. }
  2453. else if (*c_pData == GUILD_LEADER_GRADE)
  2454. {
  2455. ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<길드> 길드장의 직위 이름은 변경할 수 없습니다."));
  2456. }
  2457. else if (!check_name(gradename))
  2458. {
  2459. ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<길드> 적합하지 않은 직위 이름 입니다."));
  2460. }
  2461. else
  2462. {
  2463. pGuild->ChangeGradeName(*c_pData, gradename);
  2464. }
  2465. }
  2466. return SubPacketLen;
  2467. case GUILD_SUBHEADER_CG_CHANGE_GRADE_AUTHORITY:
  2468. {
  2469. const TGuildMember* m = pGuild->GetMember(ch->GetPlayerID());
  2470. if (NULL == m)
  2471. return -1;
  2472. if (m->grade != GUILD_LEADER_GRADE)
  2473. {
  2474. ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<길드> 직위 권한을 변경할 권한이 없습니다."));
  2475. }
  2476. else if (*c_pData == GUILD_LEADER_GRADE)
  2477. {
  2478. ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<길드> 길드장의 권한은 변경할 수 없습니다."));
  2479. }
  2480. else
  2481. {
  2482. pGuild->ChangeGradeAuth(*c_pData, *(c_pData + 1));
  2483. }
  2484. }
  2485. return SubPacketLen;
  2486. case GUILD_SUBHEADER_CG_OFFER:
  2487. {
  2488. DWORD offer = *reinterpret_cast<const DWORD*>(c_pData);
  2489. if (pGuild->GetLevel() >= GUILD_MAX_LEVEL)
  2490. {
  2491. ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<길드> 길드가 이미 최고 레벨입니다."));
  2492. }
  2493. else
  2494. {
  2495. offer /= 100;
  2496. offer *= 100;
  2497. if (pGuild->OfferExp(ch, offer))
  2498. {
  2499. ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<길드> %u의 경험치를 투자하였습니다."), offer);
  2500. }
  2501. else
  2502. {
  2503. ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<길드> 경험치 투자에 실패하였습니다."));
  2504. }
  2505. }
  2506. }
  2507. return SubPacketLen;
  2508. case GUILD_SUBHEADER_CG_CHARGE_GSP:
  2509. {
  2510. const int offer = *reinterpret_cast<const int*>(c_pData);
  2511. const int gold = offer * 100;
  2512. if (offer < 0 || gold < offer || gold < 0 || ch->GetGold() < gold)
  2513. {
  2514. ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<길드> 돈이 부족합니다."));
  2515. return SubPacketLen;
  2516. }
  2517. if (!pGuild->ChargeSP(ch, offer))
  2518. {
  2519. ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<길드> 용신력 회복에 실패하였습니다."));
  2520. }
  2521. }
  2522. return SubPacketLen;
  2523. case GUILD_SUBHEADER_CG_POST_COMMENT:
  2524. {
  2525. const size_t length = *c_pData;
  2526. if (length > GUILD_COMMENT_MAX_LEN)
  2527. {
  2528. // 잘못된 길이.. 끊어주자.
  2529. sys_err("POST_COMMENT: %s comment too long (length: %u)", ch->GetName(), length);
  2530. ch->GetDesc()->SetPhase(PHASE_CLOSE);
  2531. return -1;
  2532. }
  2533. if (uiBytes < 1 + length)
  2534. return -1;
  2535. const TGuildMember* m = pGuild->GetMember(ch->GetPlayerID());
  2536. if (NULL == m)
  2537. return -1;
  2538. if (length && !pGuild->HasGradeAuth(m->grade, GUILD_AUTH_NOTICE) && *(c_pData + 1) == '!')
  2539. {
  2540. ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<길드> 공지글을 작성할 권한이 없습니다."));
  2541. }
  2542. else
  2543. {
  2544. std::string str(c_pData + 1, length);
  2545. pGuild->AddComment(ch, str);
  2546. }
  2547. return (1 + length);
  2548. }
  2549. case GUILD_SUBHEADER_CG_DELETE_COMMENT:
  2550. {
  2551. const DWORD comment_id = *reinterpret_cast<const DWORD*>(c_pData);
  2552. pGuild->DeleteComment(ch, comment_id);
  2553. }
  2554. return SubPacketLen;
  2555. case GUILD_SUBHEADER_CG_REFRESH_COMMENT:
  2556. pGuild->RefreshComment(ch);
  2557. return SubPacketLen;
  2558. case GUILD_SUBHEADER_CG_CHANGE_MEMBER_GRADE:
  2559. {
  2560. const DWORD pid = *reinterpret_cast<const DWORD*>(c_pData);
  2561. const BYTE grade = *(c_pData + sizeof(DWORD));
  2562. const TGuildMember* m = pGuild->GetMember(ch->GetPlayerID());
  2563. if (NULL == m)
  2564. return -1;
  2565. if (m->grade != GUILD_LEADER_GRADE)
  2566. ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<길드> 직위를 변경할 권한이 없습니다."));
  2567. else if (ch->GetPlayerID() == pid)
  2568. ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<길드> 길드장의 직위는 변경할 수 없습니다."));
  2569. else if (grade == 1)
  2570. ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<길드> 길드장으로 직위를 변경할 수 없습니다."));
  2571. else
  2572. pGuild->ChangeMemberGrade(pid, grade);
  2573. }
  2574. return SubPacketLen;
  2575. case GUILD_SUBHEADER_CG_USE_SKILL:
  2576. {
  2577. const TPacketCGGuildUseSkill* p = reinterpret_cast<const TPacketCGGuildUseSkill*>(c_pData);
  2578. pGuild->UseSkill(p->dwVnum, ch, p->dwPID);
  2579. }
  2580. return SubPacketLen;
  2581. case GUILD_SUBHEADER_CG_CHANGE_MEMBER_GENERAL:
  2582. {
  2583. const DWORD pid = *reinterpret_cast<const DWORD*>(c_pData);
  2584. const BYTE is_general = *(c_pData + sizeof(DWORD));
  2585. const TGuildMember* m = pGuild->GetMember(ch->GetPlayerID());
  2586. if (NULL == m)
  2587. return -1;
  2588. if (m->grade != GUILD_LEADER_GRADE)
  2589. {
  2590. ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<길드> 장군을 지정할 권한이 없습니다."));
  2591. }
  2592. else
  2593. {
  2594. if (!pGuild->ChangeMemberGeneral(pid, is_general))
  2595. {
  2596. ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<길드> 더이상 장수를 지정할 수 없습니다."));
  2597. }
  2598. }
  2599. }
  2600. return SubPacketLen;
  2601. case GUILD_SUBHEADER_CG_GUILD_INVITE_ANSWER:
  2602. {
  2603. const DWORD guild_id = *reinterpret_cast<const DWORD*>(c_pData);
  2604. const BYTE accept = *(c_pData + sizeof(DWORD));
  2605. CGuild * g = CGuildManager::instance().FindGuild(guild_id);
  2606. if (g)
  2607. {
  2608. if (accept)
  2609. g->InviteAccept(ch);
  2610. else
  2611. g->InviteDeny(ch->GetPlayerID());
  2612. }
  2613. }
  2614. return SubPacketLen;
  2615. }
  2616. return 0;
  2617. }
  2618. void CInputMain::Fishing(LPCHARACTER ch, const char* c_pData)
  2619. {
  2620. TPacketCGFishing* p = (TPacketCGFishing*)c_pData;
  2621. ch->SetRotation(p->dir * 5);
  2622. ch->fishing();
  2623. return;
  2624. }
  2625. void CInputMain::ItemGive(LPCHARACTER ch, const char* c_pData)
  2626. {
  2627. TPacketCGGiveItem* p = (TPacketCGGiveItem*) c_pData;
  2628. LPCHARACTER to_ch = CHARACTER_MANAGER::instance().Find(p->dwTargetVID);
  2629. if (to_ch)
  2630. ch->GiveItem(to_ch, p->ItemPos);
  2631. else
  2632. ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("아이템을 건네줄 수 없습니다."));
  2633. }
  2634. void CInputMain::Hack(LPCHARACTER ch, const char * c_pData)
  2635. {
  2636. TPacketCGHack * p = (TPacketCGHack *) c_pData;
  2637. char buf[sizeof(p->szBuf)];
  2638. strlcpy(buf, p->szBuf, sizeof(buf));
  2639. sys_err("HACK_DETECT: %s %s", ch->GetName(), buf);
  2640. // 현재 클라이언트에서 이 패킷을 보내는 경우가 없으므로 무조건 끊도록 한다
  2641. ch->GetDesc()->SetPhase(PHASE_CLOSE);
  2642. }
  2643. int CInputMain::MyShop(LPCHARACTER ch, const char * c_pData, size_t uiBytes)
  2644. {
  2645. TPacketCGMyShop * p = (TPacketCGMyShop *) c_pData;
  2646. int iExtraLen = p->bCount * sizeof(TShopItemTable);
  2647. if (uiBytes < sizeof(TPacketCGMyShop) + iExtraLen)
  2648. return -1;
  2649. if (ch->GetGold() >= GOLD_MAX)
  2650. {
  2651. ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("소유 돈이 20억냥을 넘어 거래를 핼수가 없습니다."));
  2652. sys_log(0, "MyShop ==> OverFlow Gold id %u name %s ", ch->GetPlayerID(), ch->GetName());
  2653. return (iExtraLen);
  2654. }
  2655. if (ch->IsStun() || ch->IsDead())
  2656. return (iExtraLen);
  2657. #ifdef ENABLE_OFFLINE_SHOP_SYSTEM
  2658. if (ch->GetExchange() || ch->IsOpenSafebox() || ch->GetShopOwner() || ch->IsCubeOpen() || ch->GetOfflineShopOwner())
  2659. {
  2660. ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("다른 거래중일경우 개인상점을 열수가 없습니다."));
  2661. return (iExtraLen);
  2662. }
  2663. #else
  2664. if (ch->GetExchange() || ch->IsOpenSafebox() || ch->GetShopOwner() || ch->IsCubeOpen())
  2665. {
  2666. ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("다른 거래중일경우 개인상점을 열수가 없습니다."));
  2667. return (iExtraLen);
  2668. }
  2669. #endif
  2670. sys_log(0, "MyShop count %d", p->bCount);
  2671. ch->OpenMyShop(p->szSign, (TShopItemTable *) (c_pData + sizeof(TPacketCGMyShop)), p->bCount);
  2672. return (iExtraLen);
  2673. }
  2674. int CInputMain::MyOfflineShop(LPCHARACTER ch, const char * c_pData, size_t uiBytes)
  2675. {
  2676. TPacketCGMyOfflineShop * p = (TPacketCGMyOfflineShop *)c_pData;
  2677. #ifdef ENABLE_MAXIMUM_YANG_FOR_OFFLINE_SHOP
  2678. int iExtraLen = p->bCount * sizeof(TOfflineShopItemTable);
  2679. #else
  2680. int iExtraLen = p->bCount * sizeof(TShopItemTable);
  2681. #endif
  2682. if (uiBytes < sizeof(TPacketCGMyOfflineShop) + iExtraLen)
  2683. return -1;
  2684. if (ch->IsStun() || ch->IsDead())
  2685. return (iExtraLen);
  2686. if (ch->GetExchange() || ch->IsOpenSafebox() || ch->GetShopOwner() || ch->IsCubeOpen() || ch->GetOfflineShopOwner())
  2687. {
  2688. ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("다른 거래중일경우 개인상점을 열수가 없습니다."));
  2689. return (iExtraLen);
  2690. }
  2691. sys_log(0, "MyOfflineShop count %d", p->bCount);
  2692. #ifdef ENABLE_MAXIMUM_YANG_FOR_OFFLINE_SHOP
  2693. ch->OpenMyOfflineShop(p->szSign, (TOfflineShopItemTable *)(c_pData + sizeof(TPacketCGMyOfflineShop)), p->bCount, p->bTime);
  2694. #else
  2695. ch->OpenMyOfflineShop(p->szSign, (TShopItemTable *)(c_pData + sizeof(TPacketCGMyOfflineShop)), p->bCount, p->bTime);
  2696. #endif
  2697. return (iExtraLen);
  2698. }
  2699. void CInputMain::Refine(LPCHARACTER ch, const char* c_pData)
  2700. {
  2701. const TPacketCGRefine* p = reinterpret_cast<const TPacketCGRefine*>(c_pData);
  2702. #ifdef ENABLE_OFFLINE_SHOP_SYSTEM
  2703. if (ch->GetExchange() || ch->IsOpenSafebox() || ch->GetShopOwner() || ch->GetMyShop() || ch->IsCubeOpen() || ch->GetOfflineShopOwner())
  2704. {
  2705. ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("창고,거래창등이 열린 상태에서는 개량을 할수가 없습니다"));
  2706. ch->ClearRefineMode();
  2707. return;
  2708. }
  2709. #else
  2710. if (ch->GetExchange() || ch->IsOpenSafebox() || ch->GetShopOwner() || ch->GetMyShop() || ch->IsCubeOpen())
  2711. {
  2712. ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("창고,거래창등이 열린 상태에서는 개량을 할수가 없습니다"));
  2713. ch->ClearRefineMode();
  2714. return;
  2715. }
  2716. #endif
  2717. if (p->type == 255)
  2718. {
  2719. // DoRefine Cancel
  2720. ch->ClearRefineMode();
  2721. return;
  2722. }
  2723. if (p->pos >= INVENTORY_MAX_NUM)
  2724. {
  2725. ch->ClearRefineMode();
  2726. return;
  2727. }
  2728. LPITEM item = ch->GetInventoryItem(p->pos);
  2729. if (!item)
  2730. {
  2731. ch->ClearRefineMode();
  2732. return;
  2733. }
  2734. #ifdef __SOULBINDING_SYSTEM__
  2735. if (item->IsBind() || item->IsUntilBind())
  2736. {
  2737. ch->ClearRefineMode();
  2738. ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("You can't refine this item because is binded!"));
  2739. return;
  2740. }
  2741. #endif
  2742. ch->SetRefineTime();
  2743. if (p->type == REFINE_TYPE_NORMAL)
  2744. {
  2745. sys_log (0, "refine_type_noraml");
  2746. ch->DoRefine(item);
  2747. }
  2748. else if (p->type == REFINE_TYPE_SCROLL || p->type == REFINE_TYPE_HYUNIRON || p->type == REFINE_TYPE_MUSIN || p->type == REFINE_TYPE_BDRAGON)
  2749. {
  2750. sys_log (0, "refine_type_scroll, ...");
  2751. ch->DoRefineWithScroll(item);
  2752. }
  2753. else if (p->type == REFINE_TYPE_MONEY_ONLY)
  2754. {
  2755. const LPITEM item = ch->GetInventoryItem(p->pos);
  2756. if (NULL != item)
  2757. {
  2758. if (500 <= item->GetRefineSet())
  2759. {
  2760. LogManager::instance().HackLog("DEVIL_TOWER_REFINE_HACK", ch);
  2761. }
  2762. else
  2763. {
  2764. if (ch->GetQuestFlag("deviltower_zone.can_refine"))
  2765. {
  2766. ch->DoRefine(item, true);
  2767. ch->SetQuestFlag("deviltower_zone.can_refine", 0);
  2768. }
  2769. else
  2770. {
  2771. ch->ChatPacket(CHAT_TYPE_INFO, "사귀 타워 완료 보상은 한번까지 사용가능합니다.");
  2772. }
  2773. }
  2774. }
  2775. }
  2776. ch->ClearRefineMode();
  2777. }
  2778. #ifdef __SASH_SYSTEM__
  2779. void CInputMain::Sash(LPCHARACTER pkChar, const char* c_pData)
  2780. {
  2781. quest::PC * pPC = quest::CQuestManager::instance().GetPCForce(pkChar->GetPlayerID());
  2782. if (pPC->IsRunning())
  2783. return;
  2784. TPacketSash * sPacket = (TPacketSash*) c_pData;
  2785. switch (sPacket->subheader)
  2786. {
  2787. case SASH_SUBHEADER_CG_CLOSE:
  2788. {
  2789. pkChar->CloseSash();
  2790. }
  2791. break;
  2792. case SASH_SUBHEADER_CG_ADD:
  2793. {
  2794. pkChar->AddSashMaterial(sPacket->tPos, sPacket->bPos);
  2795. }
  2796. break;
  2797. case SASH_SUBHEADER_CG_REMOVE:
  2798. {
  2799. pkChar->RemoveSashMaterial(sPacket->bPos);
  2800. }
  2801. break;
  2802. case SASH_SUBHEADER_CG_REFINE:
  2803. {
  2804. pkChar->RefineSashMaterials();
  2805. }
  2806. break;
  2807. default:
  2808. break;
  2809. }
  2810. }
  2811. #endif
  2812. #ifdef __CHANGELOOK_SYSTEM__
  2813. void CInputMain::ChangeLook(LPCHARACTER pkChar, const char* c_pData)
  2814. {
  2815. quest::PC * pPC = quest::CQuestManager::instance().GetPCForce(pkChar->GetPlayerID());
  2816. if (pPC->IsRunning())
  2817. return;
  2818. TPacketChangeLook * sPacket = (TPacketChangeLook*) c_pData;
  2819. switch (sPacket->subheader)
  2820. {
  2821. case CL_SUBHEADER_CLOSE:
  2822. {
  2823. pkChar->ChangeLookWindow(false);
  2824. }
  2825. break;
  2826. case CL_SUBHEADER_ADD:
  2827. {
  2828. pkChar->AddClMaterial(sPacket->tPos, sPacket->bPos);
  2829. }
  2830. break;
  2831. case CL_SUBHEADER_REMOVE:
  2832. {
  2833. pkChar->RemoveClMaterial(sPacket->bPos);
  2834. }
  2835. break;
  2836. case CL_SUBHEADER_REFINE:
  2837. {
  2838. pkChar->RefineClMaterials();
  2839. }
  2840. break;
  2841. default:
  2842. break;
  2843. }
  2844. }
  2845. #endif
  2846. int CInputMain::Analyze(LPDESC d, BYTE bHeader, const char * c_pData)
  2847. {
  2848. LPCHARACTER ch;
  2849. if (!(ch = d->GetCharacter()))
  2850. {
  2851. sys_err("no character on desc");
  2852. d->SetPhase(PHASE_CLOSE);
  2853. return (0);
  2854. }
  2855. int iExtraLen = 0;
  2856. if (test_server && bHeader != HEADER_CG_MOVE)
  2857. sys_log(0, "CInputMain::Analyze() ==> Header [%d] ", bHeader);
  2858. switch (bHeader)
  2859. {
  2860. case HEADER_CG_PONG:
  2861. Pong(d);
  2862. break;
  2863. case HEADER_CG_TIME_SYNC:
  2864. Handshake(d, c_pData);
  2865. break;
  2866. case HEADER_CG_CHAT:
  2867. if (test_server)
  2868. {
  2869. char* pBuf = (char*)c_pData;
  2870. sys_log(0, "%s", pBuf + sizeof(TPacketCGChat));
  2871. }
  2872. if ((iExtraLen = Chat(ch, c_pData, m_iBufferLeft)) < 0)
  2873. return -1;
  2874. break;
  2875. case HEADER_CG_WHISPER:
  2876. if ((iExtraLen = Whisper(ch, c_pData, m_iBufferLeft)) < 0)
  2877. return -1;
  2878. break;
  2879. case HEADER_CG_MOVE:
  2880. Move(ch, c_pData);
  2881. if (g_bCheckClientVersion)
  2882. {
  2883. if (0 != g_stClientVersion.compare(d->GetClientVersion())) // @fixme103 (version > date)
  2884. {
  2885. ch->ChatPacket(CHAT_TYPE_NOTICE, LC_TEXT("클라이언트 버전이 틀려 로그아웃 됩니다. 정상적으로 패치 후 접속하세요."));
  2886. d->DelayedDisconnect(0); // @fixme103 (10);
  2887. LogManager::instance().HackLog("VERSION_CONFLICT", d->GetAccountTable().login, ch->GetName(), d->GetHostName());
  2888. }
  2889. }
  2890. break;
  2891. case HEADER_CG_CHARACTER_POSITION:
  2892. Position(ch, c_pData);
  2893. break;
  2894. case HEADER_CG_ITEM_USE:
  2895. if (!ch->IsObserverMode())
  2896. ItemUse(ch, c_pData);
  2897. break;
  2898. case HEADER_CG_ITEM_DROP:
  2899. if (!ch->IsObserverMode())
  2900. {
  2901. ItemDrop(ch, c_pData);
  2902. }
  2903. break;
  2904. case HEADER_CG_ITEM_DROP2:
  2905. if (!ch->IsObserverMode())
  2906. ItemDrop2(ch, c_pData);
  2907. break;
  2908. #ifdef __DESTROYSELL_SYSTEM__
  2909. case HEADER_CG_ITEM_DESTROY:
  2910. if (!ch->IsObserverMode())
  2911. ItemDestroy(ch, c_pData);
  2912. break;
  2913. case HEADER_CG_ITEM_SELL:
  2914. if (!ch->IsObserverMode())
  2915. ItemSell(ch, c_pData);
  2916. break;
  2917. #endif
  2918. case HEADER_CG_ITEM_MOVE:
  2919. if (!ch->IsObserverMode())
  2920. ItemMove(ch, c_pData);
  2921. break;
  2922. case HEADER_CG_ITEM_PICKUP:
  2923. if (!ch->IsObserverMode())
  2924. ItemPickup(ch, c_pData);
  2925. break;
  2926. case HEADER_CG_ITEM_USE_TO_ITEM:
  2927. if (!ch->IsObserverMode())
  2928. ItemToItem(ch, c_pData);
  2929. break;
  2930. case HEADER_CG_ITEM_GIVE:
  2931. if (!ch->IsObserverMode())
  2932. ItemGive(ch, c_pData);
  2933. break;
  2934. case HEADER_CG_EXCHANGE:
  2935. if (!ch->IsObserverMode())
  2936. Exchange(ch, c_pData);
  2937. break;
  2938. case HEADER_CG_ATTACK:
  2939. case HEADER_CG_SHOOT:
  2940. if (!ch->IsObserverMode())
  2941. {
  2942. Attack(ch, bHeader, c_pData);
  2943. }
  2944. break;
  2945. case HEADER_CG_USE_SKILL:
  2946. if (!ch->IsObserverMode())
  2947. UseSkill(ch, c_pData);
  2948. break;
  2949. case HEADER_CG_QUICKSLOT_ADD:
  2950. QuickslotAdd(ch, c_pData);
  2951. break;
  2952. case HEADER_CG_QUICKSLOT_DEL:
  2953. QuickslotDelete(ch, c_pData);
  2954. break;
  2955. case HEADER_CG_QUICKSLOT_SWAP:
  2956. QuickslotSwap(ch, c_pData);
  2957. break;
  2958. case HEADER_CG_SHOP:
  2959. if ((iExtraLen = Shop(ch, c_pData, m_iBufferLeft)) < 0)
  2960. return -1;
  2961. break;
  2962. #ifdef ENABLE_OFFLINE_SHOP_SYSTEM
  2963. case HEADER_CG_OFFLINE_SHOP:
  2964. if ((iExtraLen = OfflineShop(ch, c_pData, m_iBufferLeft)) < 0)
  2965. return -1;
  2966. break;
  2967. #endif
  2968. case HEADER_CG_MESSENGER:
  2969. if ((iExtraLen = Messenger(ch, c_pData, m_iBufferLeft))<0)
  2970. return -1;
  2971. break;
  2972. case HEADER_CG_ON_CLICK:
  2973. OnClick(ch, c_pData);
  2974. break;
  2975. case HEADER_CG_SYNC_POSITION:
  2976. if ((iExtraLen = SyncPosition(ch, c_pData, m_iBufferLeft)) < 0)
  2977. return -1;
  2978. break;
  2979. case HEADER_CG_ADD_FLY_TARGETING:
  2980. case HEADER_CG_FLY_TARGETING:
  2981. FlyTarget(ch, c_pData, bHeader);
  2982. break;
  2983. case HEADER_CG_SCRIPT_BUTTON:
  2984. ScriptButton(ch, c_pData);
  2985. break;
  2986. // SCRIPT_SELECT_ITEM
  2987. case HEADER_CG_SCRIPT_SELECT_ITEM:
  2988. ScriptSelectItem(ch, c_pData);
  2989. break;
  2990. // END_OF_SCRIPT_SELECT_ITEM
  2991. case HEADER_CG_SCRIPT_ANSWER:
  2992. ScriptAnswer(ch, c_pData);
  2993. break;
  2994. case HEADER_CG_QUEST_INPUT_STRING:
  2995. QuestInputString(ch, c_pData);
  2996. break;
  2997. case HEADER_CG_QUEST_CONFIRM:
  2998. QuestConfirm(ch, c_pData);
  2999. break;
  3000. case HEADER_CG_TARGET:
  3001. Target(ch, c_pData);
  3002. break;
  3003. case HEADER_CG_WARP:
  3004. Warp(ch, c_pData);
  3005. break;
  3006. case HEADER_CG_SAFEBOX_CHECKIN:
  3007. SafeboxCheckin(ch, c_pData);
  3008. break;
  3009. case HEADER_CG_SAFEBOX_CHECKOUT:
  3010. SafeboxCheckout(ch, c_pData, false);
  3011. break;
  3012. case HEADER_CG_SAFEBOX_ITEM_MOVE:
  3013. SafeboxItemMove(ch, c_pData);
  3014. break;
  3015. case HEADER_CG_MALL_CHECKOUT:
  3016. SafeboxCheckout(ch, c_pData, true);
  3017. break;
  3018. case HEADER_CG_PARTY_INVITE:
  3019. PartyInvite(ch, c_pData);
  3020. break;
  3021. case HEADER_CG_PARTY_REMOVE:
  3022. PartyRemove(ch, c_pData);
  3023. break;
  3024. case HEADER_CG_PARTY_INVITE_ANSWER:
  3025. PartyInviteAnswer(ch, c_pData);
  3026. break;
  3027. case HEADER_CG_PARTY_SET_STATE:
  3028. PartySetState(ch, c_pData);
  3029. break;
  3030. case HEADER_CG_PARTY_USE_SKILL:
  3031. PartyUseSkill(ch, c_pData);
  3032. break;
  3033. case HEADER_CG_PARTY_PARAMETER:
  3034. PartyParameter(ch, c_pData);
  3035. break;
  3036. case HEADER_CG_ANSWER_MAKE_GUILD:
  3037. AnswerMakeGuild(ch, c_pData);
  3038. break;
  3039. case HEADER_CG_GUILD:
  3040. if ((iExtraLen = Guild(ch, c_pData, m_iBufferLeft)) < 0)
  3041. return -1;
  3042. break;
  3043. case HEADER_CG_FISHING:
  3044. Fishing(ch, c_pData);
  3045. break;
  3046. case HEADER_CG_HACK:
  3047. Hack(ch, c_pData);
  3048. break;
  3049. case HEADER_CG_MYSHOP:
  3050. if ((iExtraLen = MyShop(ch, c_pData, m_iBufferLeft)) < 0)
  3051. return -1;
  3052. break;
  3053. #ifdef ENABLE_OFFLINE_SHOP_SYSTEM
  3054. case HEADER_CG_MY_OFFLINE_SHOP:
  3055. if ((iExtraLen = MyOfflineShop(ch, c_pData, m_iBufferLeft)) < 0)
  3056. return -1;
  3057. break;
  3058. #endif
  3059. case HEADER_CG_REFINE:
  3060. Refine(ch, c_pData);
  3061. break;
  3062. #ifdef __SASH_SYSTEM__
  3063. case HEADER_CG_SASH:
  3064. {
  3065. Sash(ch, c_pData);
  3066. }
  3067. break;
  3068. #endif
  3069. #ifdef __CHANGELOOK_SYSTEM__
  3070. case HEADER_CG_CL:
  3071. {
  3072. ChangeLook(ch, c_pData);
  3073. }
  3074. break;
  3075. #endif
  3076. case HEADER_CG_CLIENT_VERSION:
  3077. Version(ch, c_pData);
  3078. break;
  3079. case HEADER_CG_HS_ACK:
  3080. if (isHackShieldEnable)
  3081. {
  3082. CHackShieldManager::instance().VerifyAck(d->GetCharacter(), c_pData);
  3083. }
  3084. break;
  3085. case HEADER_CG_XTRAP_ACK:
  3086. {
  3087. TPacketXTrapCSVerify* p = reinterpret_cast<TPacketXTrapCSVerify*>((void*)c_pData);
  3088. CXTrapManager::instance().Verify_CSStep3(d->GetCharacter(), p->bPacketData);
  3089. }
  3090. break;
  3091. #ifdef __SEND_TARGET_INFO__
  3092. case HEADER_CG_TARGET_INFO_LOAD:
  3093. {
  3094. TargetInfoLoad(ch, c_pData);
  3095. }
  3096. break;
  3097. #endif
  3098. case HEADER_CG_DRAGON_SOUL_REFINE:
  3099. {
  3100. TPacketCGDragonSoulRefine* p = reinterpret_cast <TPacketCGDragonSoulRefine*>((void*)c_pData);
  3101. switch(p->bSubType)
  3102. {
  3103. case DS_SUB_HEADER_CLOSE:
  3104. ch->DragonSoul_RefineWindow_Close();
  3105. break;
  3106. case DS_SUB_HEADER_DO_REFINE_GRADE:
  3107. {
  3108. DSManager::instance().DoRefineGrade(ch, p->ItemGrid);
  3109. }
  3110. break;
  3111. case DS_SUB_HEADER_DO_REFINE_STEP:
  3112. {
  3113. DSManager::instance().DoRefineStep(ch, p->ItemGrid);
  3114. }
  3115. break;
  3116. case DS_SUB_HEADER_DO_REFINE_STRENGTH:
  3117. {
  3118. DSManager::instance().DoRefineStrength(ch, p->ItemGrid);
  3119. }
  3120. break;
  3121. }
  3122. }
  3123. break;
  3124. }
  3125. return (iExtraLen);
  3126. }
  3127. int CInputDead::Analyze(LPDESC d, BYTE bHeader, const char * c_pData)
  3128. {
  3129. LPCHARACTER ch;
  3130. if (!(ch = d->GetCharacter()))
  3131. {
  3132. sys_err("no character on desc");
  3133. return 0;
  3134. }
  3135. int iExtraLen = 0;
  3136. switch (bHeader)
  3137. {
  3138. case HEADER_CG_PONG:
  3139. Pong(d);
  3140. break;
  3141. case HEADER_CG_TIME_SYNC:
  3142. Handshake(d, c_pData);
  3143. break;
  3144. case HEADER_CG_CHAT:
  3145. if ((iExtraLen = Chat(ch, c_pData, m_iBufferLeft)) < 0)
  3146. return -1;
  3147. break;
  3148. case HEADER_CG_WHISPER:
  3149. if ((iExtraLen = Whisper(ch, c_pData, m_iBufferLeft)) < 0)
  3150. return -1;
  3151. break;
  3152. case HEADER_CG_HACK:
  3153. Hack(ch, c_pData);
  3154. break;
  3155. default:
  3156. return (0);
  3157. }
  3158. return (iExtraLen);
  3159. }