Ejemplo n.º 1
0
//////////////////////////////////////////////////////////////////////////////
// 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());
				}
Ejemplo n.º 2
0
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)
		}