////////////////////////////////////////////////////////////////////////////// // 슬레이어 오브젝트 핸들러 ////////////////////////////////////////////////////////////////////////////// void Restore::execute(Slayer* pSlayer, ObjectID_t TargetObjectID, SkillSlot* pSkillSlot, CEffectID_t CEffectID) throw(Error) { __BEGIN_TRY //cout << "TID[" << Thread::self() << "]" << getSkillHandlerName() << " Begin" << endl; Assert(pSlayer != NULL); Assert(pSkillSlot != NULL); try { Player* pPlayer = pSlayer->getPlayer(); Zone* pZone = pSlayer->getZone(); Assert(pPlayer != NULL); Assert(pZone != NULL); Creature* pFromCreature = pZone->getCreature(TargetObjectID); // 뱀파이어만 건드릴 수가 있다. // NoSuch제거. by sigi. 2002.5.2 if (pFromCreature==NULL || !pFromCreature->isVampire()) { executeSkillFailException(pSlayer, getSkillType()); //cout << "TID[" << Thread::self() << "]" << getSkillHandlerName() << " End" << endl; return; } GCSkillToObjectOK1 _GCSkillToObjectOK1; // 스킬 쓴 넘에게... GCMorph1 _GCMorph1; // 변신 당사자에게.. GCMorphSlayer2 _GCMorphSlayer2; // 변신 구경꾼들에게.. SkillType_t SkillType = pSkillSlot->getSkillType(); SkillInfo* pSkillInfo = g_pSkillInfoManager->getSkillInfo(SkillType); bool bRangeCheck = verifyDistance(pSlayer, pFromCreature, pSkillInfo->getRange()); bool bHitRoll = true; if (bRangeCheck && bHitRoll) { dropRelicToZone(pFromCreature); dropFlagToZone(pFromCreature); ////////////////////////////////////////////////////////////////////// // 각종 존 레벨 정보를 삭제해야 한다. ////////////////////////////////////////////////////////////////////// // 파티 초대 중이라면 정보를 삭제해 준다. PartyInviteInfoManager* pPIIM = pZone->getPartyInviteInfoManager(); Assert(pPIIM != NULL); pPIIM->cancelInvite(pFromCreature); // 파티 관련 정보를 삭제해 준다. int PartyID = pFromCreature->getPartyID(); if (PartyID != 0) { // 먼저 로컬에서 삭제하고... LocalPartyManager* pLPM = pZone->getLocalPartyManager(); Assert(pLPM != NULL); pLPM->deletePartyMember(PartyID, pFromCreature); // 글로벌에서도 삭제해 준다. deleteAllPartyInfo(pFromCreature); } // 트레이드 중이었다면 트레이드 관련 정보를 삭제해준다. TradeManager* pTM = pZone->getTradeManager(); Assert(pTM != NULL); pTM->cancelTrade(pFromCreature); ////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////// Slayer* pNewSlayer = new Slayer; Vampire* pVampire = dynamic_cast<Vampire*>(pFromCreature); // DB에서 혹시 남아있을 지 모르는 흡혈 정보를 삭제해준다. Statement* pStmt = NULL; BEGIN_DB { pStmt = g_pDatabaseManager->getConnection("DARKEDEN")->createStatement(); StringStream sql; sql << "DELETE FROM EffectBloodDrain WHERE OwnerID = '" + pFromCreature->getName() + "'"; pStmt->executeQuery(sql.toString()); SAFE_DELETE(pStmt); } END_DB(pStmt) pNewSlayer->setName(pFromCreature->getName()); // 크리쳐 안의 플레이어 포인터와 플레이어 안의 크리쳐 포인터를 갱신한다. Player* pFromPlayer = pFromCreature->getPlayer(); pNewSlayer->setPlayer(pFromPlayer); GamePlayer* pFromGamePlayer = dynamic_cast<GamePlayer*>(pFromPlayer); pFromGamePlayer->setCreature(pNewSlayer); // load하면 load한 zone에서 objectID를 받으므로 다시 설정한다. by sigi. 2002.6.4 pNewSlayer->load(); pNewSlayer->setZone(pZone); pNewSlayer->setObjectID(pFromCreature->getObjectID()); //pZone->getObjectRegistry().registerObject(pNewSlayer); pNewSlayer->setMoveMode(Creature::MOVE_MODE_WALKING); ZoneCoord_t x = pFromCreature->getX(); ZoneCoord_t y = pFromCreature->getY(); Dir_t dir = pFromCreature->getDir(); Tile& tile = pZone->getTile(x, y); // 곧 pFromCreature 즉, 원래의 뱀파이어 객체는 지워질 것이므로, // PCFinder에 들어가 있는 값은 쓰레기 값이 될 것이다. // 그러므로 뱀파이어 포인터를 지워주고, 새로운 슬레이어 포인터를 더한다. g_pPCFinder->deleteCreature(pFromCreature->getName()); g_pPCFinder->addCreature(pNewSlayer); // 길드 현재 접속 멤버 리스트에서 삭제한다. if (pVampire->getGuildID() != 0 ) { Guild* pGuild = g_pGuildManager->getGuild(pVampire->getGuildID()); if (pGuild != NULL ) { pGuild->deleteCurrentMember(pVampire->getName()); GSGuildMemberLogOn gsGuildMemberLogOn; gsGuildMemberLogOn.setGuildID(pGuild->getID()); gsGuildMemberLogOn.setName(pVampire->getName()); gsGuildMemberLogOn.setLogOn(false); g_pSharedServerManager->sendPacket(&gsGuildMemberLogOn); Statement* pStmt = NULL; // 디비에 업데이트 한다. BEGIN_DB { pStmt = g_pDatabaseManager->getConnection("DARKEDEN")->createStatement(); pStmt->executeQuery("UPDATE GuildMember SET LogOn = 0 WHERE Name = '%s'", pVampire->getName().c_str()); } END_DB(pStmt) } else filelog("GuildMissing.log", "[NoSuchGuild] GuildID : %d, Name : %s\n", (int)pVampire->getGuildID(), pVampire->getName().c_str()); }
////////////////////////////////////////////////////////////////////////////// // CGConnectHandler::execute() // // 이 패킷은 로그인서버에서 최초로 게임 서버로 연결할 때, 또는 게임 서버에서 // 다른 게임 서버로 이동할 때, 클라이언트가 서버로 전송하는 최초의 패킷이다. // 이때 플레이어 객체는 새로 생겨난 상태이고, 이 플레이어 객체는 IPM 에서 // 관리되는 상태이다. // // 당연히 최초의 패킷으로 다른 패킷이 넘어오는 경우라면, 이것은 해킹 시도라고 // 봐도 무방하므로 이 패킷이 최초인지 검사해야 한다. 이를 위해서 플레이어 객체에 // 이전 패킷을 저장하고 있으므로, 이 값이 NULL 인지만 보면 되겠다. // // 잘못된 패킷이라면, ban 에 등록하고 접속을 종료한다. ////////////////////////////////////////////////////////////////////////////// void CGConnectHandler::execute (CGConnect* pPacket , Player* pPlayer) throw(ProtocolException , Error) { __BEGIN_TRY __BEGIN_DEBUG_EX __BEGIN_DEBUG #ifdef __GAME_SERVER__ Assert(pPacket != NULL); Assert(pPlayer != NULL); GamePlayer* pGamePlayer = dynamic_cast<GamePlayer*>(pPlayer); // set MAC Address pGamePlayer->setMacAddress(pPacket->getMacAddress()); // 이 패킷을 ConnectionInfo 객체를 갖고 온다. // 크래커는 키값과 캐릭터 이름을 일정 시간안에 맞춰야만 접속이 가능하다. try { ConnectionInfo* pConnectionInfo = g_pConnectionInfoManager->getConnectionInfo(pGamePlayer->getSocket()->getHost()); // 키값을 인증한다. if (pPacket->getKey() != pConnectionInfo->getKey()) { FILELOG_INCOMING_CONNECTION("connectionError.log", "Wrong Key: [%s] %s", pConnectionInfo->getPCName().c_str(), pGamePlayer->getSocket()->getHost().c_str()); throw InvalidProtocolException("invalid key"); } // 이름을 인증한다. if (pPacket->getPCName() != pConnectionInfo->getPCName()) { FILELOG_INCOMING_CONNECTION("connectionError.log", "Wrong PCName: [%s] %s", pConnectionInfo->getPCName().c_str(), pGamePlayer->getSocket()->getHost().c_str()); throw InvalidProtocolException("invalid pc name"); } // 일단 이름을 저장한다. 어차피 다음에 실패하면 객체를 삭제하니까 무방하다. pGamePlayer->setID(pConnectionInfo->getPlayerID()); // CIM의 heartbeat가 실행되기 전에 재수좋게 접속할 가능성이 있다. // (타이밍이 좋으면 heartbeat 실행주기*2 안에만 접속하면 된다.) // 따라서, 현재 시간과 expire time 을 비교한다. Timeval currentTime; getCurrentTime(currentTime); if (pConnectionInfo->getExpireTime() < currentTime) { FILELOG_INCOMING_CONNECTION("connectionError.log", "Expired: [%s] %s", pConnectionInfo->getPCName().c_str(), pGamePlayer->getSocket()->getHost().c_str()); // 일단 삭제한다. g_pConnectionInfoManager->deleteConnectionInfo(pConnectionInfo->getClientIP()); throw InvalidProtocolException("session already expired"); } // by sigi. 2002.12.7 FILELOG_INCOMING_CONNECTION("connectionInfo.log", "Login [%s:%s] %s (%u)", pConnectionInfo->getPlayerID().c_str(), pConnectionInfo->getPCName().c_str(), pConnectionInfo->getClientIP().c_str(), pConnectionInfo->getKey()); // 인증되었으니, ConnectionInfo 를 삭제한다. try { g_pConnectionInfoManager->deleteConnectionInfo(pConnectionInfo->getClientIP()); } catch (NoSuchElementException& nsee) { FILELOG_INCOMING_CONNECTION("connectionInfoDelete.log", "DeleteNoSuch [%s:%s] %s (%u)", pConnectionInfo->getPlayerID().c_str(), pConnectionInfo->getPCName().c_str(), pConnectionInfo->getClientIP().c_str(), pConnectionInfo->getKey()); } } catch (NoSuchElementException & nsee) // 그런 IP를 가진 CI 가 없을 경우 { FILELOG_INCOMING_CONNECTION("connectionError.log", "NoSuchConnectionInfo: %s", pGamePlayer->getSocket()->getHost().c_str()); // 흠.. 연결 이후 CGConnect 패킷을 보내는 딜레이가 너무 길 경우 // session 이 expire 된다. 이럴 경우에도 짜르자! // (예를 들어서, 최초 연결에서는 성공했으나 그다음에 디버깅 상태로 // 들어갈 경우, CGConnect 패킷을 보낼 때쯤에는 expire 된다.) GCDisconnect gcDisconnect; gcDisconnect.setMessage(nsee.toString()); pGamePlayer->sendPacket(&gcDisconnect); // 이렇게 던지면 상위 IPM::processCommands()에서 disconnect 처리한다. throw InvalidProtocolException(nsee.toString().c_str()); } catch (InvalidProtocolException & ipe) { FILELOG_INCOMING_CONNECTION("connectionError.log", "%s: %s", ipe.toString().c_str(), pGamePlayer->getSocket()->getHost().c_str()); //cout << endl << "+-----------------------+" << endl << "| Level 2 Access Denied |" << endl << "+-----------------------+" << endl << endl; GCDisconnect gcDisconnect; gcDisconnect.setMessage(ipe.toString()); pGamePlayer->sendPacket(&gcDisconnect); // 이렇게 던지면 상위 IPM::processCommands()에서 disconnect 처리한다. throw; } //---------------------------------------------------------------------- // 로그인 체크 //---------------------------------------------------------------------- Statement* pStmt = NULL; Result* pResult = NULL; // 빌링~ PayType payType; string payPlayDate; uint payPlayHours; uint payPlayFlag; int billingUserKey = 0; // by sigi. 2002.11.18 string familyPayPlayDate; try { pStmt = g_pDatabaseManager->getConnection("DARKEDEN")->createStatement(); pResult = pStmt->executeQuery("SELECT `PlayerID` FROM `Slayer` WHERE `Name` = '%s'",pPacket->getPCName().c_str()); if (pResult->getRowCount() != 1) { StringStream msg; msg << "Failed to load PlayerCreature data from DB. Not 1 PlayerID (" << pPacket->getPCName().c_str() << ")"; filelog("connectDB_BUG.txt", "%s", msg.toString().c_str()); SAFE_DELETE(pStmt); throw ProtocolException(msg.toString().c_str()); } if (pResult->next()) { string spID = pResult->getString(1); if (strcasecmp(spID.c_str(), pGamePlayer->getID().c_str()) != 0) { StringStream msg; msg << "Failed to load PlayerCreature data from DB. No Character(" << spID.c_str() << "!=" << pGamePlayer->getID().c_str() << ")"; filelog("connectDB_BUG.txt", "%s", msg.toString().c_str()); SAFE_DELETE(pStmt); throw ProtocolException(msg.toString().c_str()); } } SAFE_DELETE(pStmt); //pStmt = g_pDatabaseManager->getConnection("DARKEDEN")->createStatement(); pStmt = g_pDatabaseManager->getDistConnection("PLAYER_DB")->createStatement(); #ifdef __THAILAND_SERVER__ pResult = pStmt->executeQuery("SELECT `PlayerID`, `CurrentServerGroupID`, `LogOn`, `SpecialEventCount`, `PayType`, `PayPlayDate`, `PayPlayHours`, `PayPlayFlag`, `BillingUserKey`, `FamilyPayPlayDate`, `Birthday` FROM `Player` WHERE `PlayerID` = '%s'", pGamePlayer->getID().c_str()); #else pResult = pStmt->executeQuery("SELECT `PlayerID`, `CurrentServerGroupID`, `LogOn`, `SpecialEventCount`, `PayType`, `PayPlayDate`, `PayPlayHours`, `PayPlayFlag`, `BillingUserKey`, `FamilyPayPlayDate` FROM `Player` WHERE `PlayerID` = '%s'", pGamePlayer->getID().c_str()); #endif if (pResult->getRowCount() != 1) { StringStream msg; msg << "Failed to load PlayerCreature data from DB. No Player(" << pPacket->getPCName().c_str() << ")"; filelog("connectDB_BUG.txt", "%s", msg.toString().c_str()); SAFE_DELETE(pStmt); throw ProtocolException(msg.toString().c_str()); } pResult->next(); int i = 0; string playerID = pResult->getString(++i); ServerGroupID_t GID = pResult->getInt(++i); string logon = pResult->getString(++i); uint scount = pResult->getDWORD(++i); payType = (PayType)pResult->getInt(++i); payPlayDate = pResult->getString(++i); payPlayHours = pResult->getInt(++i); payPlayFlag = pResult->getInt(++i); billingUserKey = pResult->getInt(++i); familyPayPlayDate = pResult->getString(++i); #ifdef __THAILAND_SERVER__ string Birthday = pResult->getString(++i); pGamePlayer->setPermission(isAdultByBirthdayDate(Birthday )); #endif pGamePlayer->setServerGroupID(GID); pGamePlayer->setSpecialEventCount(scount); pGamePlayer->setBillingUserKey(billingUserKey); if (logon != "LOGOFF") { SAFE_DELETE(pStmt); char str[80]; sprintf(str, "Already connected player ID: %s, %s", playerID.c_str(), logon.c_str()); throw ProtocolException(str); } pStmt->executeQuery("UPDATE `Player` SET `LogOn`='GAME' WHERE `PlayerID` = '%s' AND `LogOn`='LOGOFF'", playerID.c_str()); // LogOn이 LOGOFF가 아니거나.. 등등.. by sigi. 2002.5.15 if (pStmt->getAffectedRowCount()==0) { SAFE_DELETE(pStmt); char str[80]; sprintf(str, "Already connected player ID2: %s, %s", playerID.c_str(), logon.c_str()); throw ProtocolException(str); } string connectIP = pGamePlayer->getSocket()->getHost(); // 빌링 by sigi. 2002.5.31 #ifdef __CONNECT_BILLING_SYSTEM__ if (payType == PAY_TYPE_FREE ) { pGamePlayer->setMetroFreePlayer(); } #elif defined(__PAY_SYSTEM_LOGIN__) if (pGamePlayer->loginPayPlay(payType, payPlayDate, payPlayHours, payPlayFlag, connectIP, playerID)) { sendPayInfo(pGamePlayer); } else { SAFE_DELETE(pStmt); throw ProtocolException("no pay account"); } // by sigi. 2002.11.18. 제한적 무료 사용자. - -; 일단 login #elif defined(__PAY_SYSTEM_FREE_LIMIT__) if (pGamePlayer->loginPayPlay(payType, payPlayDate, payPlayHours, payPlayFlag, connectIP, playerID)) { sendPayInfo(pGamePlayer); } #else //defined(__PAY_SYSTEM_ZONE__) pGamePlayer->setPayPlayValue(payType, payPlayDate, payPlayHours, payPlayFlag, familyPayPlayDate); #endif SAFE_DELETE(pStmt); } catch (SQLQueryException & sqe) { SAFE_DELETE(pStmt); throw Error(sqe.toString()); } //---------------------------------------------------------------------- // 슬레이어 또는 뱀파이어 캐릭터를 로딩한다. //---------------------------------------------------------------------- Slayer* pSlayer = NULL; Vampire* pVampire = NULL; Ousters* pOusters = NULL; bool bAlreadyConnected = false; //try //{ if (pPacket->getPCType() == PC_SLAYER) { pSlayer = new Slayer(); pSlayer->setName(pPacket->getPCName()); pSlayer->setPlayer(pGamePlayer); if (!pSlayer->load()) { filelog("connectDB_BUG.txt", "Failed to load SLAYER(%s) data from DB", pPacket->getPCName().c_str()); throw ProtocolException("Failed to load SLAYER data from DB"); } // 유료존에서만 적용되는 아이템 때문에 밑에서 체크 //pSlayer->loadItem(); //Assert(pSlayer->getName() == pPacket->getPCName()); if (pSlayer->getName() != pPacket->getPCName()) { //cout << "Different Name : " << pSlayer->getName().c_str() << ", " << pPacket->getPCName().c_str() << endl; Assert(pSlayer->getName() == pPacket->getPCName()); } pGamePlayer->setCreature(pSlayer); // Slayer를 TelephoneCenter에 등록한다. //g_pTelephoneCenter->addSlayer(pSlayer); // 주기 회복 이벤트를 플레이어 객체에 추가한다. // 이때 기본적으로 10초 회복을 원칙으로 한다. // (setDeadline의 파라미터는 0.1 초라는 데 유의할 것) EventRegeneration* pEventRegeneration = new EventRegeneration(pGamePlayer); pEventRegeneration->setDeadline(10* 10); pGamePlayer->addEvent(pEventRegeneration); // PCFinder에 추가한다. // PCFinder의 삭제는 ~GamePlayer()에서만 한다. try { g_pPCFinder->addCreature(pSlayer); } catch (DuplicatedException& de) { bAlreadyConnected = true; } // 이미 접속중인 경우가 아니라면.. by sigi. 2002.8.29 if (!bAlreadyConnected) { // 길드 현재 접속 멤버 리스트에 추가한다. if (pSlayer->getGuildID() != 99 ) { Guild* pGuild = g_pGuildManager->getGuild(pSlayer->getGuildID()); if (pGuild != NULL ) { // sharedserver로 접속을 알리고 DB 도 update 한다. try { pGuild->addCurrentMember(pSlayer->getName()); GSGuildMemberLogOn gsGuildMemberLogOn; gsGuildMemberLogOn.setGuildID(pGuild->getID()); gsGuildMemberLogOn.setName(pSlayer->getName()); gsGuildMemberLogOn.setLogOn(true); gsGuildMemberLogOn.setServerID( g_pConfig->getPropertyInt("ServerID")); g_pSharedServerManager->sendPacket(&gsGuildMemberLogOn); // DB 업데이트 BEGIN_DB { pStmt = g_pDatabaseManager->getConnection("DARKEDEN")->createStatement(); pStmt->executeQuery("UPDATE `GuildMember` SET `LogOn` = 1 WHERE `Name` = '%s'", pSlayer->getName().c_str()); } END_DB(pStmt ) } catch (DuplicatedException& t) { // 일단 무시한다. by sigi. 2002.8.29 filelog("guildBug.log", "%s", t.toString().c_str()); } } else filelog("GuildMissing.log", "[NoSuchGuild] GuildID : %d, Name : %s\n", (int)pSlayer->getGuildID(), pSlayer->getName().c_str()); }
void EventMorph::activate () throw(Error) { __BEGIN_TRY __BEGIN_DEBUG Assert(m_pGamePlayer != NULL); Creature* pFromCreature = m_pGamePlayer->getCreature(); Assert(pFromCreature->isSlayer()); if (m_pGamePlayer->getPlayerStatus() != GPS_NORMAL) { // 플레이어의 상태가 WAITING_FOR_CG_READY인데, morph가 // activate되어 밑의 존에서 크리쳐를 지우는 부분에서 에러가 throw되어 // 서버가 죽는 버그가 있었다. 정확히 어떻게 해서 CG_READY상태에서 // 이벤트가 activate되는지는 모르겠으나, GamePlayer의 // EventManager 자체를 GPS_NORMAL일 때만 돌아가게 하면, // Resurrect가 되지 않으니 주의하길 바란다. 결국 GamePlayer 내부에서 // 체크를 하기가 곤란하기 때문에 이 부분에서, 처리한다. StringStream msg; msg << "EventMorph::activate() : GamePlayer의 상태가 GPS_NORMAL이 아닙니다." << "PlayerID[" << m_pGamePlayer->getID() << "]" << "CreatureName[" << pFromCreature->getName() << "]"; filelog("EventMorphError.log", "%s", msg.toString().c_str()); return; } pFromCreature->removeFlag(Effect::EFFECT_CLASS_BLOOD_DRAIN); Zone* pZone = pFromCreature->getZone(); // 만일 Restore 이펙트가 걸려있다면 변신이 되지 않는다. if (pFromCreature->isFlag(Effect::EFFECT_CLASS_RESTORE)) { return; } dropRelicToZone(pFromCreature); dropFlagToZone(pFromCreature); dropSweeperToZone(pFromCreature); ////////////////////////////////////////////////////////////////////// // 각종 존 레벨 정보를 삭제해야 한다. ////////////////////////////////////////////////////////////////////// // 파티 초대 중이라면 정보를 삭제해 준다. PartyInviteInfoManager* pPIIM = pZone->getPartyInviteInfoManager(); Assert(pPIIM != NULL); pPIIM->cancelInvite(pFromCreature); // 파티 관련 정보를 삭제해 준다. uint PartyID = pFromCreature->getPartyID(); if (PartyID != 0) { // 먼저 로컬에서 삭제하고... LocalPartyManager* pLPM = pZone->getLocalPartyManager(); Assert(pLPM != NULL); pLPM->deletePartyMember(PartyID, pFromCreature); // 글로벌에서도 삭제해 준다. deleteAllPartyInfo(pFromCreature); } // 트레이드 중이었다면 트레이드 관련 정보를 삭제해준다. TradeManager* pTM = pZone->getTradeManager(); Assert(pTM != NULL); pTM->cancelTrade(pFromCreature); ////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////// Vampire* pVampire = new Vampire(); GCMorph1 gcEventMorph1; // 변신 당사자에게.. GCMorphVampire2 gcEventMorphVampire2; // 변신 구경꾼들에게.. pVampire->setName(pFromCreature->getName()); ObjectID_t fromObjectID = pFromCreature->getObjectID(); pVampire->setObjectID(fromObjectID); Player* pPlayer = pFromCreature->getPlayer(); dynamic_cast<GamePlayer*>(pPlayer)->setCreature(pVampire); pVampire->setPlayer(pPlayer); pVampire->setZone(pZone); pVampire->load(); Coord_t x = pFromCreature->getX(), y = pFromCreature->getY(); Dir_t dir = pFromCreature->getDir(); pVampire->setXYDir(x, y, dir); pVampire->setMoveMode(pFromCreature->getMoveMode()); // slayer to vampire Slayer* pSlayer = dynamic_cast<Slayer*>(pFromCreature); // 뱀파이어로 변신할때 Creature Pointer가 달라지므로... // 원래 등록 되어있던 포인터는 개가 된다... // 따라서 새로운 Creature Pointer를 등록해줘야 한다. g_pPCFinder->deleteCreature(pFromCreature->getName()); g_pPCFinder->addCreature(pVampire); // 길드 현재 접속 리스트에서 삭제한다. if (pSlayer->getGuildID() != 99 ) { Guild* pGuild = g_pGuildManager->getGuild(pSlayer->getGuildID()); if (pGuild != NULL ) { pGuild->deleteCurrentMember(pSlayer->getName()); GSGuildMemberLogOn gsGuildMemberLogOn; gsGuildMemberLogOn.setGuildID(pGuild->getID()); gsGuildMemberLogOn.setName(pSlayer->getName()); gsGuildMemberLogOn.setLogOn(false); g_pSharedServerManager->sendPacket(&gsGuildMemberLogOn); Statement* pStmt = NULL; // 디비에 업데이트 한다. BEGIN_DB { pStmt = g_pDatabaseManager->getConnection("DARKEDEN")->createStatement(); pStmt->executeQuery("UPDATE GuildMember SET LogOn = 0 WHERE Name = '%s'", pSlayer->getName().c_str()); } END_DB(pStmt) }