#include "stdafx.h" #include "constants.h" #include "gm.h" #include "messenger_manager.h" #include "buffer_manager.h" #include "desc_client.h" #include "log.h" #include "config.h" #include "p2p.h" #include "crc32.h" #include "char.h" #include "char_manager.h" #include "questmanager.h" static char __account[CHARACTER_NAME_MAX_LEN * 2 + 1]; static char __companion[CHARACTER_NAME_MAX_LEN * 2 + 1]; MessengerManager::MessengerManager() { } MessengerManager::~MessengerManager() { } void MessengerManager::Initialize() { } void MessengerManager::Destroy() { } void MessengerManager::P2PLogin(MessengerManager::keyA account) { Login(account); } void MessengerManager::P2PLogout(MessengerManager::keyA account) { Logout(account); } void MessengerManager::Login(MessengerManager::keyA account) { if (m_set_loginAccount.find(account) != m_set_loginAccount.end()) return; DBManager::instance().EscapeString(__account, sizeof(__account), account.c_str(), account.size()); if (account.compare(__account)) return; DBManager::instance().FuncQuery(std::bind(&MessengerManager::LoadList, this, std::placeholders::_1), "SELECT account, companion FROM messenger_list%s WHERE account='%s'", get_table_postfix(), account.c_str()); m_set_loginAccount.insert(account); } void MessengerManager::LoadList(SQLMsg * msg) { if (NULL == msg) return; if (NULL == msg->Get()) return; if (msg->Get()->uiNumRows == 0) return; std::string account; sys_log(1, "Messenger::LoadList"); for (uint i = 0; i < msg->Get()->uiNumRows; ++i) { MYSQL_ROW row = mysql_fetch_row(msg->Get()->pSQLResult); if (row[0] && row[1]) { if (account.length() == 0) account = row[0]; m_Relation[row[0]].insert(row[1]); m_InverseRelation[row[1]].insert(row[0]); } } SendList(account); std::set::iterator it; for (it = m_InverseRelation[account].begin(); it != m_InverseRelation[account].end(); ++it) SendLogin(*it, account); } void MessengerManager::Logout(MessengerManager::keyA account) { if (m_set_loginAccount.find(account) == m_set_loginAccount.end()) return; m_set_loginAccount.erase(account); std::set::iterator it; for (it = m_InverseRelation[account].begin(); it != m_InverseRelation[account].end(); ++it) { SendLogout(*it, account); } std::map >::iterator it2 = m_Relation.begin(); while (it2 != m_Relation.end()) { it2->second.erase(account); ++it2; } m_Relation.erase(account); //m_map_stMobile.erase(account); // remove any pending requests from/to this account so they don't get stuck EraseRequestsForAccount(account); } void MessengerManager::RegisterRequestToAdd(const char* name, const char* targetName) { uint32_t dw1 = GetCRC32(name, strlen(name)); uint32_t dw2 = GetCRC32(targetName, strlen(targetName)); char buf[64]{ 0, }; snprintf(buf, sizeof(buf), "%u:%u", dw1, dw2); buf[63] = '\0'; uint32_t dwComplex = GetCRC32(buf, strlen(buf)); // Check if this requester already sent the same request if (m_set_requestToAdd.find(dwComplex) != m_set_requestToAdd.end()) { // Send P2P response back to requester's core TPacketGGMessengerResponse p2pResp{}; p2pResp.bHeader = HEADER_GG_MESSENGER_RESPONSE; strlcpy(p2pResp.szRequester, name, sizeof(p2pResp.szRequester)); strlcpy(p2pResp.szTarget, targetName, sizeof(p2pResp.szTarget)); p2pResp.bResponseType = 0; // already_sent P2P_MANAGER::Instance().Send(&p2pResp, sizeof(TPacketGGMessengerResponse)); return; } // Clear any old incoming requests for this target before adding new one EraseIncomingRequestsForTarget(targetName); RegisterRequestComplex(dw1, dw2, dwComplex); } // stage 1: starts on the core where "ch" resides. Validate ch and move to stage 2 void MessengerManager::P2PRequestToAdd_Stage1(LPCHARACTER ch, const char* targetName) { LPCHARACTER pkTarget = CHARACTER_MANAGER::Instance().FindPC(targetName); uint32_t dw1 = GetCRC32(ch->GetName(), strlen(ch->GetName())); uint32_t dw2 = GetCRC32(targetName, strlen(targetName)); char buf[64]{ 0, }; snprintf(buf, sizeof(buf), "%u:%u", dw2, dw1); buf[63] = '\0'; uint32_t dwComplex = GetCRC32(buf, strlen(buf)); // Check if target already sent a request to requester (reverse) if (m_set_requestToAdd.find(dwComplex) != m_set_requestToAdd.end()) { ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("[Friends] %s has already sent you a friend request."), targetName); return; } if (!pkTarget) { if (!ch || !ch->IsPC()) return; if (quest::CQuestManager::instance().GetPCForce(ch->GetPlayerID())->IsRunning() == true) { ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("상대방이 친구 추가를 받을 수 없는 상태입니다.")); return; } TPacketGGMessengerRequest p2pp{}; p2pp.header = HEADER_GG_MESSENGER_REQUEST_ADD; strlcpy(p2pp.account, ch->GetName(), CHARACTER_NAME_MAX_LEN + 1); strlcpy(p2pp.target, targetName, CHARACTER_NAME_MAX_LEN + 1); P2P_MANAGER::Instance().Send(&p2pp, sizeof(TPacketGGMessengerRequest)); } else // if we have both, just continue normally RequestToAdd(ch, pkTarget); } // stage 2: ends up on the core where the target resides void MessengerManager::P2PRequestToAdd_Stage2(const char* characterName, LPCHARACTER target) { LPCHARACTER ch = CHARACTER_MANAGER::Instance().FindPC(characterName); if (!target || !target->IsPC()) return; if (quest::CQuestManager::instance().GetPCForce(target->GetPlayerID())->IsRunning()) { // Send response back to requester's core TPacketGGMessengerResponse p2pResp{}; p2pResp.bHeader = HEADER_GG_MESSENGER_RESPONSE; strlcpy(p2pResp.szRequester, characterName, sizeof(p2pResp.szRequester)); strlcpy(p2pResp.szTarget, target->GetName(), sizeof(p2pResp.szTarget)); p2pResp.bResponseType = 2; // quest_running P2P_MANAGER::Instance().Send(&p2pResp, sizeof(TPacketGGMessengerResponse)); return; } if (target->IsBlockMode(BLOCK_MESSENGER_INVITE)) { // Send response back to requester's core TPacketGGMessengerResponse p2pResp{}; p2pResp.bHeader = HEADER_GG_MESSENGER_RESPONSE; strlcpy(p2pResp.szRequester, characterName, sizeof(p2pResp.szRequester)); strlcpy(p2pResp.szTarget, target->GetName(), sizeof(p2pResp.szTarget)); p2pResp.bResponseType = 3; // blocking_requests P2P_MANAGER::Instance().Send(&p2pResp, sizeof(TPacketGGMessengerResponse)); return; } MessengerManager::Instance().RegisterRequestToAdd(characterName, target->GetName()); target->ChatPacket(CHAT_TYPE_COMMAND, "messenger_auth %s", characterName); } void MessengerManager::RequestToAdd(LPCHARACTER ch, LPCHARACTER target) { if (!ch->IsPC() || !target->IsPC()) return; if (quest::CQuestManager::instance().GetPCForce(ch->GetPlayerID())->IsRunning() == true) { ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("상대방이 친구 추가를 받을 수 없는 상태입니다.")); return; } if (quest::CQuestManager::instance().GetPCForce(target->GetPlayerID())->IsRunning() == true) return; DWORD dw1 = GetCRC32(ch->GetName(), strlen(ch->GetName())); DWORD dw2 = GetCRC32(target->GetName(), strlen(target->GetName())); char buf[64]; snprintf(buf, sizeof(buf), "%u:%u", dw1, dw2); DWORD dwComplex = GetCRC32(buf, strlen(buf)); std::string requester = ch->GetName(); std::string companion = target->GetName(); char buf2[64]; snprintf(buf2, sizeof(buf2), "%u:%u", dw2, dw1); DWORD dwComplexRev = GetCRC32(buf2, strlen(buf2)); // In-memory quick check (fast, works if lists are loaded) if (IsInList(requester, companion) || IsInList(companion, requester)) { ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("[Friends] You are already friends with %s."), companion.c_str()); return; } // Check if this requester already sent the same request if (m_set_requestToAdd.find(dwComplex) != m_set_requestToAdd.end()) { ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("[Friends] You already sent a friend request to %s."), companion.c_str()); return; } // Check if target already sent a request to requester (reverse) if (m_set_requestToAdd.find(dwComplexRev) != m_set_requestToAdd.end()) { ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("[Friends] %s has already sent you a friend request."), companion.c_str()); return; } // Clear any old incoming requests for this target before adding new one EraseIncomingRequestsForTarget(target->GetName()); // register complex indexed mappings so we can erase them on disconnect RegisterRequestComplex(dw1, dw2, dwComplex); target->ChatPacket(CHAT_TYPE_COMMAND, "messenger_auth %s", ch->GetName()); } // void MessengerManager::AuthToAdd(MessengerManager::keyA account, MessengerManager::keyA companion, bool bDeny) // { // DWORD dw1 = GetCRC32(companion.c_str(), companion.length()); // DWORD dw2 = GetCRC32(account.c_str(), account.length()); // char buf[64]; // snprintf(buf, sizeof(buf), "%u:%u", dw1, dw2); // DWORD dwComplex = GetCRC32(buf, strlen(buf)); // if (m_set_requestToAdd.find(dwComplex) == m_set_requestToAdd.end()) // { // sys_log(0, "MessengerManager::AuthToAdd : request not exist %s -> %s", companion.c_str(), account.c_str()); // return; // } // m_set_requestToAdd.erase(dwComplex); // if (!bDeny) // { // AddToList(companion, account); // AddToList(account, companion); // } // } bool MessengerManager::AuthToAdd(MessengerManager::keyA account, MessengerManager::keyA companion, bool bDeny) { DWORD dw1 = GetCRC32(companion.c_str(), companion.length()); DWORD dw2 = GetCRC32(account.c_str(), account.length()); char buf[64]; snprintf(buf, sizeof(buf), "%u:%u", dw1, dw2); DWORD dwComplex = GetCRC32(buf, strlen(buf)); if (m_set_requestToAdd.find(dwComplex) == m_set_requestToAdd.end()) { sys_log(0, "MessengerManager::AuthToAdd : request not exist %s -> %s", companion.c_str(), account.c_str()); return false; } RemoveComplex(dwComplex); // In-memory quick check (fast, works if lists are loaded) if (IsInList(account, companion) || IsInList(companion, account)) { LPCHARACTER acc_ch = CHARACTER_MANAGER::instance().FindPC(account.c_str()); if (acc_ch) acc_ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("[Friends] You are already friends with %s."), companion.c_str()); return false; } if (!bDeny) { AddToList(companion, account); AddToList(account, companion); } return true; } void MessengerManager::RegisterRequestComplex(DWORD dw1, DWORD dw2, DWORD dwComplex) { // avoid duplicates if (m_set_requestToAdd.find(dwComplex) != m_set_requestToAdd.end()) return; m_set_requestToAdd.insert(dwComplex); m_map_requestComplex[dwComplex] = std::make_pair(dw1, dw2); m_map_requestsFrom[dw1].insert(dwComplex); m_map_requestsTo[dw2].insert(dwComplex); } void MessengerManager::RemoveComplex(DWORD dwComplex) { auto it = m_map_requestComplex.find(dwComplex); if (it == m_map_requestComplex.end()) { m_set_requestToAdd.erase(dwComplex); return; } DWORD dw1 = it->second.first; DWORD dw2 = it->second.second; // erase complex mapping m_map_requestComplex.erase(it); // remove from requester index auto itFrom = m_map_requestsFrom.find(dw1); if (itFrom != m_map_requestsFrom.end()) { itFrom->second.erase(dwComplex); if (itFrom->second.empty()) m_map_requestsFrom.erase(itFrom); } // remove from target index auto itTo = m_map_requestsTo.find(dw2); if (itTo != m_map_requestsTo.end()) { itTo->second.erase(dwComplex); if (itTo->second.empty()) m_map_requestsTo.erase(itTo); } // remove from master set m_set_requestToAdd.erase(dwComplex); } void MessengerManager::EraseRequestsForAccount(keyA account) { DWORD dw = GetCRC32(account.c_str(), account.length()); std::vector toRemove; // auto itFrom = m_map_requestsFrom.find(dw); // if (itFrom != m_map_requestsFrom.end()) // { // for (DWORD c : itFrom->second) // toRemove.push_back(c); // } auto itTo = m_map_requestsTo.find(dw); if (itTo != m_map_requestsTo.end()) { for (DWORD c : itTo->second) toRemove.push_back(c); } if (toRemove.empty()) return; // uniqueify to avoid double removals std::sort(toRemove.begin(), toRemove.end()); toRemove.erase(std::unique(toRemove.begin(), toRemove.end()), toRemove.end()); for (DWORD c : toRemove) RemoveComplex(c); } void MessengerManager::EraseIncomingRequestsForTarget(const char* targetName) { DWORD dwTarget = GetCRC32(targetName, strlen(targetName)); std::vector toRemove; // Find all requests where this person is the target (incoming requests) auto itTo = m_map_requestsTo.find(dwTarget); if (itTo != m_map_requestsTo.end()) { for (DWORD c : itTo->second) toRemove.push_back(c); } if (toRemove.empty()) return; // Remove all found requests for (DWORD c : toRemove) RemoveComplex(c); } void MessengerManager::__AddToList(MessengerManager::keyA account, MessengerManager::keyA companion, bool isRequester) { m_Relation[account].insert(companion); m_InverseRelation[companion].insert(account); LPCHARACTER ch = CHARACTER_MANAGER::instance().FindPC(account.c_str()); LPDESC d = ch ? ch->GetDesc() : NULL; if (d && isRequester) { ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<메신져> %s 님을 친구로 추가하였습니다."), companion.c_str()); } LPCHARACTER tch = CHARACTER_MANAGER::instance().FindPC(companion.c_str()); if (tch || P2P_MANAGER::Instance().Find(companion.c_str())) SendLogin(account, companion); else SendLogout(account, companion); } void MessengerManager::AddToList(MessengerManager::keyA account, MessengerManager::keyA companion) { if (companion.size() == 0) return; if (m_Relation[account].find(companion) != m_Relation[account].end()) return; DBManager::instance().EscapeString(__account, sizeof(__account), account.c_str(), account.size()); DBManager::instance().EscapeString(__companion, sizeof(__companion), companion.c_str(), companion.size()); if (account.compare(__account) || companion.compare(__companion)) return; sys_log(0, "Messenger Add %s %s", account.c_str(), companion.c_str()); DBManager::instance().Query("INSERT INTO messenger_list%s VALUES ('%s', '%s')", get_table_postfix(), account.c_str(), companion.c_str()); __AddToList(account, companion); TPacketGGMessenger p2ppck; p2ppck.bHeader = HEADER_GG_MESSENGER_ADD; strlcpy(p2ppck.szAccount, account.c_str(), sizeof(p2ppck.szAccount)); strlcpy(p2ppck.szCompanion, companion.c_str(), sizeof(p2ppck.szCompanion)); P2P_MANAGER::instance().Send(&p2ppck, sizeof(TPacketGGMessenger)); } // MR-3: Remove from messenger Fix void MessengerManager::__RemoveFromList(MessengerManager::keyA account, MessengerManager::keyA companion) { m_Relation[account].erase(companion); m_InverseRelation[companion].erase(account); m_Relation[companion].erase(account); m_InverseRelation[account].erase(companion); LPCHARACTER tch = CHARACTER_MANAGER::Instance().FindPC(companion.c_str()); if (tch && tch->GetDesc()) { TPacketGCMessenger p; p.header = HEADER_GC_MESSENGER; p.subheader = MESSENGER_SUBHEADER_GC_REMOVE_FRIEND; p.size = sizeof(TPacketGCMessenger) + sizeof(BYTE) + account.size(); BYTE bLen = account.size(); tch->GetDesc()->BufferedPacket(&p, sizeof(p)); tch->GetDesc()->BufferedPacket(&bLen, sizeof(BYTE)); tch->GetDesc()->Packet(account.c_str(), account.size()); } } // MR-3: -- END OF -- Remove from messenger Fix bool MessengerManager::IsInList(MessengerManager::keyA account, MessengerManager::keyA companion) // Fix { if (m_Relation.find(account) == m_Relation.end()) return false; if (m_Relation[account].empty()) return false; return m_Relation[account].find(companion) != m_Relation[account].end(); } void MessengerManager::RemoveFromList(MessengerManager::keyA account, MessengerManager::keyA companion, bool isRequester) { if (companion.empty()) return; if (companion.size() == 0) return; DBManager::instance().EscapeString(__account, sizeof(__account), account.c_str(), account.size()); DBManager::instance().EscapeString(__companion, sizeof(__companion), companion.c_str(), companion.size()); if (account.compare(__account) || companion.compare(__companion)) return; sys_log(1, "Messenger Remove %s %s", account.c_str(), companion.c_str()); // Fix DBManager::instance().Query("DELETE FROM messenger_list%s WHERE (account='%s' AND companion = '%s') OR (account = '%s' AND companion = '%s')", get_table_postfix(), account.c_str(), companion.c_str(), companion.c_str(), account.c_str()); // MR-3: Remove from messenger Fix LPCHARACTER ch = CHARACTER_MANAGER::instance().FindPC(account.c_str()); LPDESC d = ch ? ch->GetDesc() : NULL; if (d && isRequester) ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<메신져> %s 님을 메신저에서 삭제하였습니다."), companion.c_str()); // MR-3: -- END OF -- Remove from messenger Fix __RemoveFromList(account, companion); TPacketGGMessenger p2ppck; p2ppck.bHeader = HEADER_GG_MESSENGER_REMOVE; strlcpy(p2ppck.szAccount, account.c_str(), sizeof(p2ppck.szAccount)); strlcpy(p2ppck.szCompanion, companion.c_str(), sizeof(p2ppck.szCompanion)); P2P_MANAGER::instance().Send(&p2ppck, sizeof(TPacketGGMessenger)); } void MessengerManager::RemoveAllList(keyA account) { std::set company(m_Relation[account]); DBManager::instance().EscapeString(__account, sizeof(__account), account.c_str(), account.size()); if (account.compare(__account)) return; /* SQL Data 삭제 */ DBManager::instance().Query("DELETE FROM messenger_list%s WHERE account='%s' OR companion='%s'", get_table_postfix(), account.c_str(), account.c_str()); /* 내가 가지고있는 리스트 삭제 */ for (std::set::iterator iter = company.begin(); iter != company.end(); iter++ ) { // MR-3: Remove from messenger Fix this->RemoveFromList(account, *iter, false); this->RemoveFromList(*iter, account, false); // MR-3: -- END OF -- Remove from messenger Fix } /* 복사한 데이타 삭제 */ for (std::set::iterator iter = company.begin(); iter != company.end(); ) { company.erase(iter++); } company.clear(); } void MessengerManager::SendList(MessengerManager::keyA account) { LPCHARACTER ch = CHARACTER_MANAGER::instance().FindPC(account.c_str()); if (!ch) return; LPDESC d = ch->GetDesc(); if (!d) return; if (m_Relation.find(account) == m_Relation.end()) return; if (m_Relation[account].empty()) return; TPacketGCMessenger pack; pack.header = HEADER_GC_MESSENGER; pack.subheader = MESSENGER_SUBHEADER_GC_LIST; pack.size = sizeof(TPacketGCMessenger); TPacketGCMessengerListOffline pack_offline; TPacketGCMessengerListOnline pack_online; TEMP_BUFFER buf(128 * 1024); // 128k itertype(m_Relation[account]) it = m_Relation[account].begin(), eit = m_Relation[account].end(); while (it != eit) { if (m_set_loginAccount.find(*it) != m_set_loginAccount.end()) { pack_online.connected = 1; // Online pack_online.length = it->size(); buf.write(&pack_online, sizeof(TPacketGCMessengerListOnline)); buf.write(it->c_str(), it->size()); } else { pack_offline.connected = 0; // Offline pack_offline.length = it->size(); buf.write(&pack_offline, sizeof(TPacketGCMessengerListOffline)); buf.write(it->c_str(), it->size()); } ++it; } pack.size += buf.size(); d->BufferedPacket(&pack, sizeof(TPacketGCMessenger)); d->Packet(buf.read_peek(), buf.size()); } void MessengerManager::SendLogin(MessengerManager::keyA account, MessengerManager::keyA companion) { LPCHARACTER ch = CHARACTER_MANAGER::instance().FindPC(account.c_str()); LPDESC d = ch ? ch->GetDesc() : NULL; if (!d) return; if (!d->GetCharacter()) return; if (ch->GetGMLevel() == GM_PLAYER && gm_get_level(companion.c_str()) != GM_PLAYER) return; BYTE bLen = companion.size(); TPacketGCMessenger pack; pack.header = HEADER_GC_MESSENGER; pack.subheader = MESSENGER_SUBHEADER_GC_LOGIN; pack.size = sizeof(TPacketGCMessenger) + sizeof(BYTE) + bLen; d->BufferedPacket(&pack, sizeof(TPacketGCMessenger)); d->BufferedPacket(&bLen, sizeof(BYTE)); d->Packet(companion.c_str(), companion.size()); } void MessengerManager::SendLogout(MessengerManager::keyA account, MessengerManager::keyA companion) { if (!companion.size()) return; LPCHARACTER ch = CHARACTER_MANAGER::instance().FindPC(account.c_str()); LPDESC d = ch ? ch->GetDesc() : NULL; if (!d) return; BYTE bLen = companion.size(); TPacketGCMessenger pack; pack.header = HEADER_GC_MESSENGER; pack.subheader = MESSENGER_SUBHEADER_GC_LOGOUT; pack.size = sizeof(TPacketGCMessenger) + sizeof(BYTE) + bLen; d->BufferedPacket(&pack, sizeof(TPacketGCMessenger)); d->BufferedPacket(&bLen, sizeof(BYTE)); d->Packet(companion.c_str(), companion.size()); }