コード例 #1
0
ファイル: Restore.cpp プロジェクト: jun199004/server
//////////////////////////////////////////////////////////////////////////////
// 슬레이어 오브젝트 핸들러
//////////////////////////////////////////////////////////////////////////////
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());
			}
コード例 #2
0
ファイル: CGConnectHandler.cpp プロジェクト: mrktj/darkeden
//////////////////////////////////////////////////////////////////////////////
// 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());
				}
コード例 #3
0
ファイル: Restore2.cpp プロジェクト: jun199004/server
//////////////////////////////////////////////////////////////////////////////
// 슬레이어 오브젝트 핸들러
//////////////////////////////////////////////////////////////////////////////
void Restore::execute(Slayer* pSlayer, ObjectID_t TargetObjectID, SkillSlot* pSkillSlot, CEffectID_t CEffectID)
	throw(Error)
{
	__BEGIN_TRY

	//cout << "TID[" << Thread::self() << "]" << getSkillHandlerName() << " Begin" << endl;
	//cout << "Restore2 Start" << 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)
		{
			//////////////////////////////////////////////////////////////////////
			// 각종 존 레벨 정보를 삭제해야 한다.
			//////////////////////////////////////////////////////////////////////

			// 파티 초대 중이라면 정보를 삭제해 준다.
			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);

			pNewSlayer->setZone(pZone);
			pNewSlayer->load();
			pNewSlayer->setObjectID(pFromCreature->getObjectID());
			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 )
				g_pGuildManager->getGuild(pVampire->getGuildID() )->deleteCurrentMember(pVampire->getName());

			// 인벤토리 교체.
			Inventory* pInventory = pVampire->getInventory();
			pNewSlayer->setInventory(pInventory);
			pVampire->setInventory(NULL);

			// 보관함 교체
			pNewSlayer->deleteStash();
			pNewSlayer->setStash(pVampire->getStash());
			pNewSlayer->setStashNum(pVampire->getStashNum());
			pNewSlayer->setStashStatus(false);
			pVampire->setStash(NULL);

			/*
			// 가비지 교체
			while (true)
			{
				Item* pGarbage = pVampire->popItemFromGarbage();

				// 더 이상 없다면 브레이크...
				if (pGarbage == NULL) break;

				pNewSlayer->addItemToGarbage(pGarbage);
			}
			*/

			// 플래그 셋 교체
			pNewSlayer->deleteFlagSet();
			pNewSlayer->setFlagSet(pVampire->getFlagSet());
			pVampire->setFlagSet(NULL);

			Item* pItem = NULL;
			_TPOINT point;

			// 입고 있는 아이템들을 인벤토리 또는 바닥으로 옮긴다.
			for(int part = 0; part < (int)Vampire::VAMPIRE_WEAR_MAX; part++)
			{
				pItem = pVampire->getWearItem((Vampire::WearPart)part);
				if (pItem != NULL)
				{
					// 먼저 기어에서 삭제하고...
					pVampire->deleteWearItem((Vampire::WearPart)part);
			
					// 인벤토리에 자리가 있으면 인벤토리에 더하고...
					if (pInventory->getEmptySlot(pItem, point))
					{
						pInventory->addItem(point.x, point.y, pItem);
						pItem->save(pNewSlayer->getName(), STORAGE_INVENTORY, 0, point.x, point.y);
					}
					// 자리가 없으면 바닥에 떨어뜨린다.
					else
					{
						ZoneCoord_t ZoneX = pVampire->getX();
						ZoneCoord_t ZoneY = pVampire->getY();

						TPOINT pt;

						pt = pZone->addItem(pItem, ZoneX , ZoneY);

						if (pt.x != -1) 
						{
							pItem->save("", STORAGE_ZONE, pZone->getZoneID(), pt.x, pt.y);
						} 
						else 
						{
							pItem->destroy();
							SAFE_DELETE(pItem);
						}
					}
				}
			}

			pItem = pVampire->getExtraInventorySlotItem();
			if (pItem != NULL)
			{
				pVampire->deleteItemFromExtraInventorySlot();

				// 인벤토리에 자리가 있으면 인벤토리에 더하고...
				if (pInventory->getEmptySlot(pItem, point))
				{
					pInventory->addItem(point.x, point.y, pItem);
					pItem->save(pNewSlayer->getName(), STORAGE_INVENTORY, 0, point.x, point.y);
				}
				// 자리가 없으면 바닥에 떨어뜨린다.
				else
				{
					TPOINT pt;
					ZoneCoord_t ZoneX = pVampire->getX();
					ZoneCoord_t ZoneY = pVampire->getY();

					pt = pZone->addItem(pItem, ZoneX , ZoneY);

					if (pt.x != -1) 
					{
						pItem->save("", STORAGE_ZONE, pZone->getZoneID(), pt.x, pt.y);
					} 
					else 
					{
						pItem->destroy();
						SAFE_DELETE(pItem);
					}
				}
			}

			// 뱀파이어 가지고 있던 돈을 슬레이어로 옮겨준다.
			pNewSlayer->setGoldEx(pVampire->getGold());

			// 스킬 정보를 전송한다.
			pNewSlayer->sendSlayerSkillInfo();

			_GCMorph1.setPCInfo2(pNewSlayer->getSlayerInfo2());
			_GCMorph1.setInventoryInfo(pNewSlayer->getInventoryInfo());
			_GCMorph1.setGearInfo(pNewSlayer->getGearInfo());
			_GCMorph1.setExtraInfo(pNewSlayer->getExtraInfo());

			_GCMorphSlayer2.setSlayerInfo(pNewSlayer->getSlayerInfo3());

			pFromPlayer->sendPacket(&_GCMorph1);
			//pFromGamePlayer->deleteEvent(Event::EVENT_CLASS_REGENERATION);

			pZone->broadcastPacket(x, y, &_GCMorphSlayer2, pNewSlayer);

			// 타일 및 존에서 기존 뱀파이어를 삭제하고, 새로운 슬레이어를 더한다.
			tile.deleteCreature(pFromCreature->getObjectID());
			pZone->deletePC(pFromCreature);

			TPOINT pt = findSuitablePosition(pZone, x, y, Creature::MOVE_MODE_WALKING);
			Tile& newtile = pZone->getTile(pt.x, pt.y);

			newtile.addCreature(pNewSlayer);
			pNewSlayer->setXYDir(pt.x, pt.y, dir);

			pZone->addPC(pNewSlayer);

			pNewSlayer->tinysave("Race='SLAYER'");
			SAFE_DELETE(pFromCreature);

			// 시야 update..
			pZone->updateHiddenScan(pNewSlayer);
		
			_GCSkillToObjectOK1.setSkillType(SkillType);
			_GCSkillToObjectOK1.setCEffectID(CEffectID);
			_GCSkillToObjectOK1.setTargetObjectID(TargetObjectID);
			_GCSkillToObjectOK1.setDuration(0);

			pPlayer->sendPacket(&_GCSkillToObjectOK1);

			pSkillSlot->setRunTime(0);

			EffectRestore* pEffectRestore = new EffectRestore(pNewSlayer);
			pEffectRestore->setDeadline(60*60*24*7*10); // 7일 
			pNewSlayer->addEffect(pEffectRestore);
			pNewSlayer->setFlag(Effect::EFFECT_CLASS_RESTORE);
			pEffectRestore->create(pNewSlayer->getName());
		}
		else 
		{
			executeSkillFailNormal(pSlayer, getSkillType(), pFromCreature);
		}
	}