PacketData CGameServerPlayer::GetCharacterInfo()
{
	PacketData outgoingPacket(std::begin(g_client0_login8), std::end(g_client0_login8));

	{
		const uint32 setInitialPositionBase = 0x320;
		CSetInitialPositionPacket setInitialPosition;
		setInitialPosition.SetSourceId(PLAYER_ID);
		setInitialPosition.SetTargetId(PLAYER_ID);
		setInitialPosition.SetX(157.55f);
		setInitialPosition.SetY(0);
		setInitialPosition.SetZ(165.05f);
		setInitialPosition.SetAngle(-1.53f);
		auto setInitialPositionPacket = setInitialPosition.ToPacketData();

		memcpy(outgoingPacket.data() + setInitialPositionBase, setInitialPositionPacket.data(), setInitialPositionPacket.size());
	}

	auto playerActor = m_instance.GetActor<CPlayerActor>(PLAYER_ID);
	const auto& character = playerActor->GetCharacter();

	const uint32 characterInfoBase = 0x368;

	*reinterpret_cast<uint32*>(&outgoingPacket[characterInfoBase + 0x20]) = CCharacter::GetModelFromTribe(character.tribe);
	*reinterpret_cast<uint32*>(&outgoingPacket[characterInfoBase + 0x28]) = character.size;
	*reinterpret_cast<uint32*>(&outgoingPacket[characterInfoBase + 0x30]) = character.GetColorInfo();
	*reinterpret_cast<uint32*>(&outgoingPacket[characterInfoBase + 0x38]) = character.GetFaceInfo();
	*reinterpret_cast<uint32*>(&outgoingPacket[characterInfoBase + 0x40]) = character.hairStyle << 10;
	*reinterpret_cast<uint32*>(&outgoingPacket[characterInfoBase + 0x48]) = character.voice;
	*reinterpret_cast<uint32*>(&outgoingPacket[characterInfoBase + 0x50]) = character.weapon1;			//weapon 1
	*reinterpret_cast<uint32*>(&outgoingPacket[characterInfoBase + 0x58]) = character.weapon2;			//weapon 2?
//	*reinterpret_cast<uint32*>(&outgoingPacket[characterInfoBase + 0x68]) = 0;							//weapon 3?
	*reinterpret_cast<uint32*>(&outgoingPacket[characterInfoBase + 0x88]) = character.headGear;			//headGear
	*reinterpret_cast<uint32*>(&outgoingPacket[characterInfoBase + 0x90]) = character.bodyGear;			//bodyGear
	*reinterpret_cast<uint32*>(&outgoingPacket[characterInfoBase + 0x98]) = character.legsGear;			//legsGear
	*reinterpret_cast<uint32*>(&outgoingPacket[characterInfoBase + 0xA0]) = character.handsGear;		//handsGear
	*reinterpret_cast<uint32*>(&outgoingPacket[characterInfoBase + 0xA8]) = character.feetGear;			//feetGear
	*reinterpret_cast<uint32*>(&outgoingPacket[characterInfoBase + 0xB0]) = character.waistGear;		//waistGear
//	*reinterpret_cast<uint32*>(&outgoingPacket[characterInfoBase + 0xB8]) = 0;							//???
	*reinterpret_cast<uint32*>(&outgoingPacket[characterInfoBase + 0xC0]) = character.rightEarGear;		//rightEarGear
	*reinterpret_cast<uint32*>(&outgoingPacket[characterInfoBase + 0xC8]) = character.leftEarGear;		//leftEarGear
//	*reinterpret_cast<uint32*>(&outgoingPacket[characterInfoBase + 0xD0]) = 0;							//???
//	*reinterpret_cast<uint32*>(&outgoingPacket[characterInfoBase + 0xD8]) = 0;							//???
	*reinterpret_cast<uint32*>(&outgoingPacket[characterInfoBase + 0xE0]) = character.rightFingerGear;	//rightFingerGear
	*reinterpret_cast<uint32*>(&outgoingPacket[characterInfoBase + 0xE8]) = character.leftFingerGear;	//leftFingerGear

	//Insert character name
	for(unsigned int i = 0; i < character.name.size(); i++)
	{
		outgoingPacket[characterInfoBase + 0x14C + i] = character.name[i];
	}
	outgoingPacket[characterInfoBase + 0x14C + character.name.size()] = 0;

	return outgoingPacket;
}
void CLobbyServerPlayer::ProcessStartSession(const PacketData& packetData)
{
	uint32 clientTime = *reinterpret_cast<const uint32*>(&packetData[0x84]);

	//We assume clientTime is 0x50E0E812, but we need to generate a proper key later
	uint8 blowfishKey[0x10] = { 0xB4, 0xEE, 0x3F, 0x6C, 0x01, 0x6F, 0x5B, 0xD9, 0x71, 0x50, 0x0D, 0xB1, 0x85, 0xA2, 0xAB, 0x43 };
	InitializeBlowfish(reinterpret_cast<char*>(blowfishKey), 0x10);

	CLog::GetInstance().LogMessage(LOG_NAME, "Received encryption key: 0x%0.8X.\r\n", clientTime);

	//Respond with acknowledgment
	std::vector<uint8> outgoingPacket(std::begin(g_secureConnectionAcknowledgment), std::end(g_secureConnectionAcknowledgment));
	CPacketUtils::EncryptPacket(outgoingPacket);
	QueuePacket(outgoingPacket);
}
void CLobbyServerPlayer::ProcessSelectCharacter(const PacketData& packetData)
{
	uint32 characterId = *reinterpret_cast<const uint32*>(&packetData[0x28]);

	CLog::GetInstance().LogMessage(LOG_NAME, "SelectCharacter(id = %d).", characterId);

	const char* gameServerAddress = CAppConfig::GetInstance().GetPreferenceString(PREF_FFXIVD_GAMESERVER_ADDRESS);

	std::vector<uint8> outgoingPacket(std::begin(g_selectCharacterPacket), std::end(g_selectCharacterPacket));
	*reinterpret_cast<uint32*>(outgoingPacket.data() + 0x38) = characterId;			//Character Id (normally the actor id which is different from the id passed in that packet)
	*reinterpret_cast<uint16*>(outgoingPacket.data() + 0x86) = CGameServer::GAME_SERVER_PORT;
	strcpy(reinterpret_cast<char*>(outgoingPacket.data() + 0x88), gameServerAddress);

	CPacketUtils::EncryptPacket(outgoingPacket);
	QueuePacket(outgoingPacket);
}
void CGameServerPlayer::SpawnNpc(uint32 id, uint32 appearanceId, uint32 stringId, float x, float y, float z, float angle)
{
	PacketData outgoingPacket(std::begin(g_spawnNpc), std::end(g_spawnNpc));

	uint32 packetIndex = 0x10;
	while(packetIndex < outgoingPacket.size())
	{
		auto packetPtr = outgoingPacket.data() + packetIndex;
		uint16 subPacketSize = *reinterpret_cast<uint16*>(packetPtr + 0x00);
		uint16 subPacketCmd = *reinterpret_cast<uint16*>(packetPtr + 0x12);
		*reinterpret_cast<uint32*>(packetPtr + 0x4) = id;
		if(subPacketCmd == 0xCE || subPacketCmd == 0xCF)
		{
			*reinterpret_cast<float*>(packetPtr + 0x28) = x;
			*reinterpret_cast<float*>(packetPtr + 0x2C) = y;
			*reinterpret_cast<float*>(packetPtr + 0x30) = z;
			*reinterpret_cast<float*>(packetPtr + 0x34) = angle;
		}
		else if(subPacketCmd == 0xCC)
		{
			static int magicNumber = 0;
			*reinterpret_cast<uint8*>(packetPtr + 0x40) = 0x30 + magicNumber;
			magicNumber++;
		}
		else if(subPacketCmd == 0xD6)
		{
			*reinterpret_cast<uint32*>(packetPtr + 0x20) = appearanceId;
		}
		else if(subPacketCmd == 0x13D)
		{
			*reinterpret_cast<uint32*>(packetPtr + 0x20) = stringId;
		}
		packetIndex += subPacketSize;
	}
	assert(packetIndex == outgoingPacket.size());

	{
		auto enemyActor = std::make_unique<CEnemyActor>();
		enemyActor->GlobalPacketReady.connect([&] (CActor* actor, const PacketPtr& packet) { QueueToCurrentComposite(actor, packet); });
		enemyActor->SetId(id);
		enemyActor->SetHp(ENEMY_INITIAL_HP);
		m_instance.AddActor(std::move(enemyActor));
	}

	QueuePacket(outgoingPacket);
}
void CLobbyServerPlayer::ProcessSessionAcknowledgement(const PacketData& packetData)
{
	std::string sessionId = reinterpret_cast<const char*>(&packetData[0x30]);
	std::string clientVersion = reinterpret_cast<const char*>(&packetData[0x70]);

	CLog::GetInstance().LogMessage(LOG_NAME, "Got acknowledgment for secure session.");
	CLog::GetInstance().LogMessage(LOG_NAME, "SESSION_ID: %s", sessionId.c_str());
	CLog::GetInstance().LogMessage(LOG_NAME, "CLIENT_VERSION: %s", clientVersion.c_str());

	if(m_dbConnection.IsEmpty())
	{
		CLog::GetInstance().LogMessage(LOG_NAME, "No database connection available. Bailing.");
		m_disconnect = true;
		return;
	}

	try
	{
		auto query = string_format("SELECT userId FROM ffxiv_sessions WHERE id = '%s' AND expiration > NOW()", sessionId.c_str());
		auto result = m_dbConnection.Query(query.c_str());
		if(result.GetRowCount() == 0)
		{
			throw std::runtime_error("Session expired or doesn't exist.");
		}
		auto row = result.FetchRow();
		assert(row != nullptr);
		assert(result.GetFieldCount() == 1);
		m_userId = boost::lexical_cast<uint32>(row[0]);
		CLog::GetInstance().LogMessage(LOG_NAME, "User (id: %u) logged in.", m_userId);
	}
	catch(const std::exception& exception)
	{
		CLog::GetInstance().LogError(LOG_NAME, "Failed to validate user session (id: %s): %s.", sessionId.c_str(), exception.what());
		m_disconnect = true;
		return;
	}

	std::vector<uint8> outgoingPacket(std::begin(g_loginAcknowledgment), std::end(g_loginAcknowledgment));
	CPacketUtils::EncryptPacket(outgoingPacket);
	QueuePacket(outgoingPacket);
}
void CGameServerPlayer::SendTeleportSequence(uint32 levelId, uint32 musicId, float x, float y, float z, float angle)
{
	QueuePacket(PacketData(std::begin(g_client0_moor1), std::end(g_client0_moor1)));
	QueuePacket(PacketData(std::begin(g_client0_moor2), std::end(g_client0_moor2)));
	QueuePacket(PacketData(std::begin(g_client0_moor3), std::end(g_client0_moor3)));
	QueuePacket(PacketData(std::begin(g_client0_moor4), std::end(g_client0_moor4)));
	QueuePacket(PacketData(std::begin(g_client0_moor5), std::end(g_client0_moor5)));
	QueuePacket(PacketData(std::begin(g_client0_moor6), std::end(g_client0_moor6)));
	QueuePacket(PacketData(std::begin(g_client0_moor7), std::end(g_client0_moor7)));
	QueuePacket(PacketData(std::begin(g_client0_moor8), std::end(g_client0_moor8)));

	QueuePacket(PacketData(std::begin(g_client0_moor9), std::end(g_client0_moor9)));

	{
		CCompositePacket result;

		{
			CSetMusicPacket packet;
			packet.SetSourceId(PLAYER_ID);
			packet.SetTargetId(PLAYER_ID);
			packet.SetMusicId(musicId);
			result.AddPacket(packet.ToPacketData());
		}

		{
			CSetWeatherPacket packet;
			packet.SetSourceId(PLAYER_ID);
			packet.SetTargetId(PLAYER_ID);
			packet.SetWeatherId(CSetWeatherPacket::WEATHER_CLEAR);
			result.AddPacket(packet.ToPacketData());
		}

		{
			CSetMapPacket packet;
			packet.SetSourceId(PLAYER_ID);
			packet.SetTargetId(PLAYER_ID);
			packet.SetMapId(levelId);
			result.AddPacket(packet.ToPacketData());
		}

		QueuePacket(result.ToPacketData());
	}

	QueuePacket(PacketData(std::begin(g_client0_moor11), std::end(g_client0_moor11)));
	QueuePacket(PacketData(std::begin(g_client0_moor12), std::end(g_client0_moor12)));

	{
		PacketData outgoingPacket(std::begin(g_client0_moor13), std::end(g_client0_moor13));

		{
			const uint32 setInitialPositionBase = 0x360;

			CSetInitialPositionPacket setInitialPosition;
			setInitialPosition.SetSourceId(PLAYER_ID);
			setInitialPosition.SetTargetId(PLAYER_ID);
			setInitialPosition.SetX(x);
			setInitialPosition.SetY(y);
			setInitialPosition.SetZ(z);
			setInitialPosition.SetAngle(angle);
			auto setInitialPositionPacket = setInitialPosition.ToPacketData();

			memcpy(outgoingPacket.data() + setInitialPositionBase, setInitialPositionPacket.data(), setInitialPositionPacket.size());
		}

		QueuePacket(outgoingPacket);
	}

	QueuePacket(GetInventoryInfo());
	QueuePacket(PacketData(std::begin(g_client0_moor21), std::end(g_client0_moor21)));
	//QueuePacket(PacketData(std::begin(g_client0_moor22), std::end(g_client0_moor22)));
	
	if(!m_zoneMasterCreated)
	{
		//Zone Master
		QueuePacket(PacketData(std::begin(g_client0_moor23), std::end(g_client0_moor23)));

	/*
		QueuePacket(PacketData(std::begin(g_client0_moor24), std::end(g_client0_moor24)));
		QueuePacket(PacketData(std::begin(g_client0_moor25), std::end(g_client0_moor25)));

		QueuePacket(PacketData(std::begin(g_client0_moor26), std::end(g_client0_moor26)));
		QueuePacket(PacketData(std::begin(g_client0_moor27), std::end(g_client0_moor27)));
		QueuePacket(PacketData(std::begin(g_client0_moor28), std::end(g_client0_moor28)));
		QueuePacket(PacketData(std::begin(g_client0_moor29), std::end(g_client0_moor29)));

		QueuePacket(PacketData(std::begin(g_client0_moor30), std::end(g_client0_moor30)));
		QueuePacket(PacketData(std::begin(g_client0_moor31), std::end(g_client0_moor31)));

		QueuePacket(PacketData(std::begin(g_client0_moor32), std::end(g_client0_moor32)));
		QueuePacket(PacketData(std::begin(g_client0_moor33), std::end(g_client0_moor33)));
		QueuePacket(PacketData(std::begin(g_client0_moor34), std::end(g_client0_moor34)));
		QueuePacket(PacketData(std::begin(g_client0_moor35), std::end(g_client0_moor35)));
		QueuePacket(PacketData(std::begin(g_client0_moor36), std::end(g_client0_moor36)));
		QueuePacket(PacketData(std::begin(g_client0_moor37), std::end(g_client0_moor37)));
	*/
		//Enables chat?
	//	QueuePacket(PacketData(std::begin(g_client0_moor38), std::end(g_client0_moor38)));

		{
			CCompositePacket packet;
			packet.AddPacket(PacketData(std::begin(g_client0_moor38), std::end(g_client0_moor38)));
			QueuePacket(packet.ToPacketData());
		}

	//	QueuePacket(PacketData(std::begin(g_client0_moor39), std::end(g_client0_moor39)));

	//	QueuePacket(PacketData(std::begin(g_client0_moor40), std::end(g_client0_moor40)));

		ResetInstance();
		//Only makes sense in Black Shroud for now
		if(levelId == CSetMapPacket::MAP_BLACKSHROUD)
		{
			const auto& actorDatabase = CGlobalData::GetInstance().GetActorDatabase();
			for(const auto& actorInfo : actorDatabase.GetActors())
			{
				SpawnNpc(actorInfo.id, actorInfo.baseModelId, actorInfo.nameStringId, 
					std::get<0>(actorInfo.pos), std::get<1>(actorInfo.pos), std::get<2>(actorInfo.pos), 0);
			}
		}

		m_zoneMasterCreated = true;
	}

}
void CLobbyServerPlayer::ProcessGetCharacters(const PacketData& packetData)
{
	CLog::GetInstance().LogMessage(LOG_NAME, "GetCharacters");

	if(m_dbConnection.IsEmpty())
	{
		CLog::GetInstance().LogMessage(LOG_NAME, "No database connection available. Bailing.");
		m_disconnect = true;
		return;
	}

	PacketData outgoingPacket(std::begin(g_characterListPacket), std::end(g_characterListPacket));

	CCharacter character;

	try
	{
		auto query = string_format("SELECT * FROM ffxiv_characters WHERE userId = %d", m_userId);
		auto result = m_dbConnection.Query(query.c_str());
		if(result.GetRowCount() != 0)
		{
			character = CCharacter(result);
		}
	}
	catch(const std::exception& exception)
	{
		CLog::GetInstance().LogError(LOG_NAME, "Failed to fetch characters for user (id = %d): %s", m_userId, exception.what());
		m_disconnect = true;
		return;
	}

	PacketData characterData(std::begin(g_characterData), std::end(g_characterData));
	
	characterData[0x21] = CCharacter::GetModelFromTribe(character.tribe);
	characterData[0x9F] = character.tribe;
	characterData[0xC7] = character.guardian;
	characterData[0xC8] = character.birthMonth;
	characterData[0xC9] = character.birthDay;
	characterData[0xE8] = character.allegiance;

	*reinterpret_cast<uint32*>(&characterData[0x25]) = character.size;				//size

	*reinterpret_cast<uint32*>(&characterData[0x29]) = character.GetColorInfo();	//hairColor + skinColor
	*reinterpret_cast<uint32*>(&characterData[0x2D]) = character.GetFaceInfo();		//face Stuff?
	*reinterpret_cast<uint32*>(&characterData[0x31]) = character.hairStyle << 10;	//hair model
	*reinterpret_cast<uint32*>(&characterData[0x35]) = character.voice;				//voice
	*reinterpret_cast<uint32*>(&characterData[0x39]) = character.weapon1;			//weapon1
	*reinterpret_cast<uint32*>(&characterData[0x3D]) = character.weapon2;			//weapon2
	*reinterpret_cast<uint32*>(&characterData[0x55]) = character.headGear;			//headGear
	*reinterpret_cast<uint32*>(&characterData[0x59]) = character.bodyGear;			//bodyGear
	*reinterpret_cast<uint32*>(&characterData[0x5D]) = character.legsGear;			//legsGear
	*reinterpret_cast<uint32*>(&characterData[0x61]) = character.handsGear;			//handsGear
	*reinterpret_cast<uint32*>(&characterData[0x65]) = character.feetGear;			//feetGear
	*reinterpret_cast<uint32*>(&characterData[0x69]) = character.waistGear;			//waistGear
//	*reinterpret_cast<uint32*>(&characterData[0x6D]) = 0;							//???
	*reinterpret_cast<uint32*>(&characterData[0x71]) = character.rightEarGear;		//rightEarGear
	*reinterpret_cast<uint32*>(&characterData[0x75]) = character.leftEarGear;		//leftEarGear
//	*reinterpret_cast<uint32*>(&characterData[0x79]) = 0;							//???
//	*reinterpret_cast<uint32*>(&characterData[0x7D]) = 0;							//???
	*reinterpret_cast<uint32*>(&characterData[0x81]) = character.rightFingerGear;	//rightFingerGear
	*reinterpret_cast<uint32*>(&characterData[0x85]) = character.leftFingerGear;	//leftFingerGear

	auto encodedCharacterData = Framework::ToBase64(characterData.data(), characterData.size());
	std::replace(std::begin(encodedCharacterData), std::end(encodedCharacterData), '+', '-');
	std::replace(std::begin(encodedCharacterData), std::end(encodedCharacterData), '/', '_');

	static const uint32 characterInfoBase = 0x860;

	if(character.active)
	{
		for(unsigned int i = 0; i < encodedCharacterData.size(); i++)
		{
			outgoingPacket[characterInfoBase + 0x40 + i] = encodedCharacterData[i];
		}

		*reinterpret_cast<uint32*>(&outgoingPacket[characterInfoBase + 0x00]) = 0x0158E7FC;
		*reinterpret_cast<uint32*>(&outgoingPacket[characterInfoBase + 0x04]) = character.id;
		*reinterpret_cast<uint32*>(&outgoingPacket[characterInfoBase + 0x0C]) = 0x000000F4;

		//Insert character name
		for(unsigned int i = 0; i < character.name.size(); i++)
		{
			outgoingPacket[characterInfoBase + 0x10 + i] = character.name[i];
		}
		outgoingPacket[characterInfoBase + 0x10 + character.name.size()] = 0;
	}

	CPacketUtils::EncryptPacket(outgoingPacket);
	QueuePacket(outgoingPacket);
}