<commit_before>/*
 * ArcEmu MMORPG Server
 * Copyright (C) 2005-2007 Ascent Team <http://www.ascentemu.com/>
 * Copyright (C) 2008 <http://www.ArcEmu.org/>
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 */

#include "StdAfx.h"

void WorldSession::HandleSetVisibleRankOpcode( WorldPacket& recv_data )
{
	CHECK_PACKET_SIZE( recv_data, 4 );
	uint32 ChosenRank;
	recv_data >> ChosenRank;
	if( ChosenRank == 0xFFFFFFFF )
		_player->SetUInt32Value( PLAYER_CHOSEN_TITLE, 0 );
	else if( _player->HasKnownTitle( static_cast< RankTitles >( ChosenRank ) ) )
		_player->SetUInt32Value( PLAYER_CHOSEN_TITLE, ChosenRank );
}

void HonorHandler::AddHonorPointsToPlayer(Player *pPlayer, uint32 uAmount)
{
	pPlayer->HandleProc(PROC_ON_GAIN_EXPIERIENCE, pPlayer, NULL);
	pPlayer->m_procCounter = 0;

	if( pPlayer->GetMapId() == 559 || pPlayer->GetMapId() == 562 || pPlayer->GetMapId() == 572)
		return;
	pPlayer->m_honorPoints += uAmount;
	pPlayer->m_honorToday += uAmount;
	if (pPlayer->m_honorPoints > 75000) pPlayer->m_honorPoints = 75000;

	RecalculateHonorFields(pPlayer);
}

int32 HonorHandler::CalculateHonorPointsForKill( Player *pPlayer, Unit* pVictim )
{
	// this sucks.. ;p
	if( pVictim == NULL )
	{
		int32 pts = rand() % 100 + 100;
		return pts;
	}

	// Suicide lol
	if( pVictim == pPlayer )
		return 0;

	if( pVictim->GetTypeId() != TYPEID_PLAYER )
		return 0;

	//use Player::m_honorless, applied with Aura::SpellAuraNoPVPCredit
	// How dishonorable, you fiend!
	//if( pVictim->HasActiveAura( PLAYER_HONORLESS_TARGET_SPELL ) )
	//	return 0;

	if ( pVictim->GetTypeId() == TYPEID_PLAYER && (static_cast< Player* >(pVictim)->m_honorless || static_cast< Player* >(pVictim)->HasAura(15007)) )
		return 0;

	uint32 k_level = pPlayer->GetUInt32Value( UNIT_FIELD_LEVEL );
	uint32 v_level = pVictim->GetUInt32Value( UNIT_FIELD_LEVEL );

	int k_honor = pPlayer->m_honorPoints;
	int v_honor = static_cast< Player* >( pVictim )->m_honorPoints;

	uint32 k_grey = 0;

	if( k_level > 5 && k_level < 40 )
	{
		k_grey = k_level - 5 - float2int32( floor( ((float)k_level) / 10.0f ) );
	}
	else
	{
		k_grey = k_level - 1 - float2int32( floor( ((float)k_level) / 5.0f ) );
	}

	if( k_honor == 0 )
		k_honor = 1;

	float diff_level = ((float)v_level - k_grey) / ((float)k_level - k_grey);
	if( diff_level > 2 ) diff_level = 2.0f;
	if( diff_level < 0 ) diff_level = 0.0f;

	float diff_honor = ((float)v_honor) / ((float)k_honor);
	if( diff_honor > 3 ) diff_honor = 3.0f;
	if( diff_honor < 0 ) diff_honor = 0.0f;

	float honor_points = diff_level * ( 150.0f + diff_honor * 60 );
	honor_points *= ((float)k_level) / PLAYER_LEVEL_CAP;
	honor_points *= World::getSingleton().getRate( RATE_HONOR );

	return float2int32( honor_points );
}

void HonorHandler::OnPlayerKilledUnit( Player *pPlayer, Unit* pVictim )
{
	if( pVictim == NULL || pPlayer == NULL )
		return;

	if( pPlayer->GetTypeId() != TYPEID_PLAYER || !pVictim->IsUnit() )
		return;

	if( !pVictim->IsPlayer() || static_cast< Player* >( pVictim )->m_honorless )
		return;

    if( pVictim->IsPlayer() )
	{
		if( pPlayer->m_bg )
		{
			if( static_cast< Player* >( pVictim )->m_bgTeam == pPlayer->m_bgTeam )
				return;

			// patch 2.4, players killed >50 times in battlegrounds won't be worth honor for the rest of that bg
			if( static_cast<Player*>(pVictim)->m_bgScore.Deaths >= 50 )
				return;
		}
		else
		{
			if( pPlayer->GetTeam() == static_cast< Player* >( pVictim )->GetTeam() )
				return;
		}
	}

	// Calculate points
	int32 Points = CalculateHonorPointsForKill(pPlayer, pVictim);

	if( Points > 0 )
	{
		if( pPlayer->m_bg )
		{
			// hackfix for battlegrounds (since the gorups there are disabled, we need to do this manually)
			vector<Player*> toadd;
			uint32 t = pPlayer->m_bgTeam;
			toadd.reserve(15);		// shouldnt have more than this
			pPlayer->m_bg->Lock();
			set<Player*> * s = &pPlayer->m_bg->m_players[t];

			for(set<Player*>::iterator itr = s->begin(); itr != s->end(); ++itr)
			{
				if((*itr) == pPlayer || (*itr)->isInRange(pPlayer,100.0f))
					toadd.push_back(*itr);
			}

			if( toadd.size() > 0 )
			{
				uint32 pts = Points / (uint32)toadd.size();
				for(vector<Player*>::iterator vtr = toadd.begin(); vtr != toadd.end(); ++vtr)
				{
					AddHonorPointsToPlayer(*vtr, pts);

					(*vtr)->m_killsToday++;
					(*vtr)->m_killsLifetime++;
					pPlayer->m_bg->HookOnHK(*vtr);
					if(pVictim)
					{
						// Send PVP credit
						WorldPacket data(SMSG_PVP_CREDIT, 12);
						uint32 pvppoints = pts;
						data << pvppoints << pVictim->GetGUID() << uint32(static_cast< Player* >(pVictim)->GetPVPRank());
						(*vtr)->GetSession()->SendPacket(&data);
					}
				}
			}

			pPlayer->m_bg->Unlock();
		}
		else
		{
			set<Player*> contributors;
			// First loop: Get all the people in the attackermap.
			pVictim->UpdateOppFactionSet();
			for(std::set<Object*>::iterator itr = pVictim->GetInRangeOppFactsSetBegin(); itr != pVictim->GetInRangeOppFactsSetEnd(); itr++)
			{
				if(!(*itr)->IsPlayer())
					continue;

				bool added = false;
				Player * plr = (Player*)(*itr);
				if(pVictim->CombatStatus.m_attackers.find(plr->GetGUID()) != pVictim->CombatStatus.m_attackers.end())
				{
					added = true;
					contributors.insert(plr);
				}

				if(added && plr->GetGroup())
				{
					Group * pGroup = plr->GetGroup();
					uint32 groups = pGroup->GetSubGroupCount();
					for(uint32 i = 0; i < groups; i++)
					{
						SubGroup * sg = pGroup->GetSubGroup(i);
						if(!sg) continue;

						for(GroupMembersSet::iterator itr2 = sg->GetGroupMembersBegin(); itr2 != sg->GetGroupMembersEnd(); itr2++)
						{
							PlayerInfo * pi = (*itr2);
							Player * gm = objmgr.GetPlayer(pi->guid);
							if(!gm) continue;

							if(gm->isInRange(pVictim, 100.0f))
								contributors.insert(gm);
						}
					}
				}
			}

			for(set<Player*>::iterator itr = contributors.begin(); itr != contributors.end(); itr++)
			{
				Player * pAffectedPlayer = (*itr);
				if(!pAffectedPlayer) continue;

				pAffectedPlayer->m_killsToday++;
				pAffectedPlayer->m_killsLifetime++;
				if(pAffectedPlayer->m_bg)
					pAffectedPlayer->m_bg->HookOnHK(pAffectedPlayer);

				int32 contributorpts = Points / (int32)contributors.size();
				AddHonorPointsToPlayer(pAffectedPlayer, contributorpts);
				if(pVictim->IsPlayer())
				{
					sHookInterface.OnHonorableKill(pAffectedPlayer, (Player*)pVictim);

					WorldPacket data(SMSG_PVP_CREDIT, 12);
					uint32 pvppoints = contributorpts; // Why *10?
					data << pvppoints << pVictim->GetGUID() << uint32(static_cast< Player* >(pVictim)->GetPVPRank());
					pAffectedPlayer->GetSession()->SendPacket(&data);
				}

				if(pAffectedPlayer->GetZoneId() == 3518)
				{
					// Add Halaa Battle Token
					SpellEntry * pvp_token_spell = dbcSpell.LookupEntry(pAffectedPlayer->GetTeam()? 33004 : 33005);
					pAffectedPlayer->CastSpell(pAffectedPlayer, pvp_token_spell, true);
				}
				// If we are in Hellfire Peninsula <http://www.wowwiki.com/Hellfire_Peninsula#World_PvP_-_Hellfire_Fortifications>
				if(pAffectedPlayer->GetZoneId() == 3483)
				{
					// Hellfire Horde Controlled Towers
					if(pAffectedPlayer->GetMapMgr()->GetWorldState(2478) != 3 && pAffectedPlayer->GetTeam() == 1)
						return;

					// Hellfire Alliance Controlled Towers
					if(pAffectedPlayer->GetMapMgr()->GetWorldState(2476) != 3 && pAffectedPlayer->GetTeam() == 0)
						return;

					// Add Mark of Thrallmar/Honor Hold
					SpellEntry * pvp_token_spell = dbcSpell.LookupEntry(pAffectedPlayer->GetTeam()? 32158 : 32155);
					pAffectedPlayer->CastSpell(pAffectedPlayer, pvp_token_spell, true);
				}
			}
		}
	}
}

void HonorHandler::RecalculateHonorFields(Player *pPlayer)
{
	pPlayer->SetUInt32Value(PLAYER_FIELD_KILLS, uint16(pPlayer->m_killsToday) | ( pPlayer->m_killsYesterday << 16 ) );
	pPlayer->SetUInt32Value(PLAYER_FIELD_TODAY_CONTRIBUTION, pPlayer->m_honorToday);
	pPlayer->SetUInt32Value(PLAYER_FIELD_YESTERDAY_CONTRIBUTION, pPlayer->m_honorYesterday);
	pPlayer->SetUInt32Value(PLAYER_FIELD_LIFETIME_HONORBALE_KILLS, pPlayer->m_killsLifetime);
	pPlayer->SetUInt32Value(PLAYER_FIELD_HONOR_CURRENCY, pPlayer->m_honorPoints);
	pPlayer->SetUInt32Value(PLAYER_FIELD_ARENA_CURRENCY, pPlayer->m_arenaPoints);
}

bool ChatHandler::HandleAddKillCommand(const char* args, WorldSession* m_session)
{
	uint32 KillAmount = args ? atol(args) : 1;
	Player *plr = getSelectedChar(m_session, true);
	if(plr == 0)
		return true;

	BlueSystemMessage(m_session, "Adding %u kills to player %s.", KillAmount, plr->GetName());
	GreenSystemMessage(plr->GetSession(), "You have had %u honor kills added to your character.", KillAmount);

	for(uint32 i = 0; i < KillAmount; ++i)
		HonorHandler::OnPlayerKilledUnit(plr, 0);

	return true;
}

bool ChatHandler::HandleAddHonorCommand(const char* args, WorldSession* m_session)
{
	uint32 HonorAmount = args ? atol(args) : 1;
	Player *plr = getSelectedChar(m_session, true);
	if(plr == 0)
		return true;

	BlueSystemMessage(m_session, "Adding %u honor to player %s.", HonorAmount, plr->GetName());
	GreenSystemMessage(plr->GetSession(), "You have had %u honor points added to your character.", HonorAmount);

	HonorHandler::AddHonorPointsToPlayer(plr, HonorAmount);
	return true;
}

bool ChatHandler::HandlePVPCreditCommand(const char* args, WorldSession* m_session)
{
	uint32 Rank, Points;
	if(sscanf(args, "%u %u", (unsigned int*)&Rank, (unsigned int*)&Points) != 2)
	{
		RedSystemMessage(m_session, "Command must be in format <rank> <points>.");
		return true;
	}
	Points *= 10;
	uint64 Guid = m_session->GetPlayer()->GetSelection();
	if(Guid == 0)
	{
		RedSystemMessage(m_session, "A selection of a unit or player is required.");
		return true;
	}

	BlueSystemMessage(m_session, "Building packet with Rank %u, Points %u, GUID "I64FMT".",
		Rank, Points, Guid);

	WorldPacket data(SMSG_PVP_CREDIT, 12);
	data << Points << Guid << Rank;
	m_session->SendPacket(&data);
	return true;
}

bool ChatHandler::HandleGlobalHonorDailyMaintenanceCommand(const char* args, WorldSession* m_session)
{
	return false;
}

bool ChatHandler::HandleNextDayCommand(const char* args, WorldSession* m_session)
{
	return false;
}
<commit_msg> - Ignore me with this *facepalm*<commit_after>/*
 * ArcEmu MMORPG Server
 * Copyright (C) 2005-2007 Ascent Team <http://www.ascentemu.com/>
 * Copyright (C) 2008 <http://www.ArcEmu.org/>
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 */

#include "StdAfx.h"

void WorldSession::HandleSetVisibleRankOpcode( WorldPacket& recv_data )
{
	CHECK_PACKET_SIZE( recv_data, 4 );
	uint32 ChosenRank;
	recv_data >> ChosenRank;
	if( ChosenRank == 0xFFFFFFFF )
		_player->SetUInt32Value( PLAYER_CHOSEN_TITLE, 0 );
	else if( _player->HasKnownTitle( static_cast< RankTitles >( ChosenRank ) ) )
		_player->SetUInt32Value( PLAYER_CHOSEN_TITLE, ChosenRank );
}

void HonorHandler::AddHonorPointsToPlayer(Player *pPlayer, uint32 uAmount)
{
	pPlayer->HandleProc(PROC_ON_GAIN_EXPIERIENCE, pPlayer, NULL);
	pPlayer->m_procCounter = 0;

	if( pPlayer->GetMapId() == 559 || pPlayer->GetMapId() == 562 || pPlayer->GetMapId() == 572)
		return;
	pPlayer->m_honorPoints += uAmount;
	pPlayer->m_honorToday += uAmount;
	if (pPlayer->m_honorPoints > 75000) pPlayer->m_honorPoints = 75000;

	RecalculateHonorFields(pPlayer);
}

int32 HonorHandler::CalculateHonorPointsForKill( Player *pPlayer, Unit* pVictim )
{
	// this sucks.. ;p
	if( pVictim == NULL )
	{
		int32 pts = rand() % 100 + 100;
		return pts;
	}

	// Suicide lol
	if( pVictim == pPlayer )
		return 0;

	if( pVictim->GetTypeId() != TYPEID_PLAYER )
		return 0;

	//use Player::m_honorless, applied with Aura::SpellAuraNoPVPCredit
	// How dishonorable, you fiend!
	//if( pVictim->HasActiveAura( PLAYER_HONORLESS_TARGET_SPELL ) )
	//	return 0;

	if ( pVictim->GetTypeId() == TYPEID_PLAYER && (static_cast< Player* >(pVictim)->m_honorless || static_cast< Player* >(pVictim)->HasAura(15007)) )
		return 0;

	uint32 k_level = pPlayer->GetUInt32Value( UNIT_FIELD_LEVEL );
	uint32 v_level = pVictim->GetUInt32Value( UNIT_FIELD_LEVEL );

	int k_honor = pPlayer->m_honorPoints;
	int v_honor = static_cast< Player* >( pVictim )->m_honorPoints;

	uint32 k_grey = 0;

	if( k_level > 5 && k_level < 40 )
	{
		k_grey = k_level - 5 - float2int32( floor( ((float)k_level) / 10.0f ) );
	}
	else
	{
		k_grey = k_level - 1 - float2int32( floor( ((float)k_level) / 5.0f ) );
	}

	if( k_honor == 0 )
		k_honor = 1;

	float diff_level = ((float)v_level - k_grey) / ((float)k_level - k_grey);
	if( diff_level > 2 ) diff_level = 2.0f;
	if( diff_level < 0 ) diff_level = 0.0f;

	float diff_honor = ((float)v_honor) / ((float)k_honor);
	if( diff_honor > 3 ) diff_honor = 3.0f;
	if( diff_honor < 0 ) diff_honor = 0.0f;

	float honor_points = diff_level * ( 150.0f + diff_honor * 60 );
	honor_points *= ((float)k_level) / PLAYER_LEVEL_CAP;
	honor_points *= World::getSingleton().getRate( RATE_HONOR );

	return float2int32( honor_points );
}

void HonorHandler::OnPlayerKilledUnit( Player *pPlayer, Unit* pVictim )
{
	if( pVictim == NULL || pPlayer == NULL )
		return;

	if( pPlayer->GetTypeId() != TYPEID_PLAYER || !pVictim->IsUnit() )
		return;

	if( !pVictim->IsPlayer() || static_cast< Player* >( pVictim )->m_honorless )
		return;

    if( pVictim->IsPlayer() )
	{
		if( pPlayer->m_bg )
		{
			if( static_cast< Player* >( pVictim )->m_bgTeam == pPlayer->m_bgTeam )
				return;

			// patch 2.4, players killed >50 times in battlegrounds won't be worth honor for the rest of that bg
			if( static_cast<Player*>(pVictim)->m_bgScore.Deaths >= 50 )
				return;
		}
		else
		{
			if( pPlayer->GetTeam() == static_cast< Player* >( pVictim )->GetTeam() )
				return;
		}
	}

	// Calculate points
	int32 Points = CalculateHonorPointsForKill(pPlayer, pVictim);

	if( Points > 0 )
	{
		if( pPlayer->m_bg )
		{
			// hackfix for battlegrounds (since the gorups there are disabled, we need to do this manually)
			vector<Player*> toadd;
			uint32 t = pPlayer->m_bgTeam;
			toadd.reserve(15);		// shouldnt have more than this
			pPlayer->m_bg->Lock();
			set<Player*> * s = &pPlayer->m_bg->m_players[t];

			for(set<Player*>::iterator itr = s->begin(); itr != s->end(); ++itr)
			{
				if((*itr) == pPlayer || (*itr)->isInRange(pPlayer,100.0f))
					toadd.push_back(*itr);
			}

			if( toadd.size() > 0 )
			{
				uint32 pts = Points / (uint32)toadd.size();
				for(vector<Player*>::iterator vtr = toadd.begin(); vtr != toadd.end(); ++vtr)
				{
					AddHonorPointsToPlayer(*vtr, pts);

					(*vtr)->m_killsToday++;
					(*vtr)->m_killsLifetime++;
					pPlayer->m_bg->HookOnHK(*vtr);
					if(pVictim)
					{
						// Send PVP credit
						WorldPacket data(SMSG_PVP_CREDIT, 12);
						uint32 pvppoints = pts * 10;
						data << pvppoints << pVictim->GetGUID() << uint32(static_cast< Player* >(pVictim)->GetPVPRank());
						(*vtr)->GetSession()->SendPacket(&data);
					}
				}
			}

			pPlayer->m_bg->Unlock();
		}
		else
		{
			set<Player*> contributors;
			// First loop: Get all the people in the attackermap.
			pVictim->UpdateOppFactionSet();
			for(std::set<Object*>::iterator itr = pVictim->GetInRangeOppFactsSetBegin(); itr != pVictim->GetInRangeOppFactsSetEnd(); itr++)
			{
				if(!(*itr)->IsPlayer())
					continue;

				bool added = false;
				Player * plr = (Player*)(*itr);
				if(pVictim->CombatStatus.m_attackers.find(plr->GetGUID()) != pVictim->CombatStatus.m_attackers.end())
				{
					added = true;
					contributors.insert(plr);
				}

				if(added && plr->GetGroup())
				{
					Group * pGroup = plr->GetGroup();
					uint32 groups = pGroup->GetSubGroupCount();
					for(uint32 i = 0; i < groups; i++)
					{
						SubGroup * sg = pGroup->GetSubGroup(i);
						if(!sg) continue;

						for(GroupMembersSet::iterator itr2 = sg->GetGroupMembersBegin(); itr2 != sg->GetGroupMembersEnd(); itr2++)
						{
							PlayerInfo * pi = (*itr2);
							Player * gm = objmgr.GetPlayer(pi->guid);
							if(!gm) continue;

							if(gm->isInRange(pVictim, 100.0f))
								contributors.insert(gm);
						}
					}
				}
			}

			for(set<Player*>::iterator itr = contributors.begin(); itr != contributors.end(); itr++)
			{
				Player * pAffectedPlayer = (*itr);
				if(!pAffectedPlayer) continue;

				pAffectedPlayer->m_killsToday++;
				pAffectedPlayer->m_killsLifetime++;
				if(pAffectedPlayer->m_bg)
					pAffectedPlayer->m_bg->HookOnHK(pAffectedPlayer);

				int32 contributorpts = Points / (int32)contributors.size();
				AddHonorPointsToPlayer(pAffectedPlayer, contributorpts);
				if(pVictim->IsPlayer())
				{
					sHookInterface.OnHonorableKill(pAffectedPlayer, (Player*)pVictim);

					WorldPacket data(SMSG_PVP_CREDIT, 12);
					uint32 pvppoints = contributorpts * 10; // Why *10?
					data << pvppoints << pVictim->GetGUID() << uint32(static_cast< Player* >(pVictim)->GetPVPRank());
					pAffectedPlayer->GetSession()->SendPacket(&data);
				}

				if(pAffectedPlayer->GetZoneId() == 3518)
				{
					// Add Halaa Battle Token
					SpellEntry * pvp_token_spell = dbcSpell.LookupEntry(pAffectedPlayer->GetTeam()? 33004 : 33005);
					pAffectedPlayer->CastSpell(pAffectedPlayer, pvp_token_spell, true);
				}
				// If we are in Hellfire Peninsula <http://www.wowwiki.com/Hellfire_Peninsula#World_PvP_-_Hellfire_Fortifications>
				if(pAffectedPlayer->GetZoneId() == 3483)
				{
					// Hellfire Horde Controlled Towers
					if(pAffectedPlayer->GetMapMgr()->GetWorldState(2478) != 3 && pAffectedPlayer->GetTeam() == 1)
						return;

					// Hellfire Alliance Controlled Towers
					if(pAffectedPlayer->GetMapMgr()->GetWorldState(2476) != 3 && pAffectedPlayer->GetTeam() == 0)
						return;

					// Add Mark of Thrallmar/Honor Hold
					SpellEntry * pvp_token_spell = dbcSpell.LookupEntry(pAffectedPlayer->GetTeam()? 32158 : 32155);
					pAffectedPlayer->CastSpell(pAffectedPlayer, pvp_token_spell, true);
				}
			}
		}
	}
}

void HonorHandler::RecalculateHonorFields(Player *pPlayer)
{
	pPlayer->SetUInt32Value(PLAYER_FIELD_KILLS, uint16(pPlayer->m_killsToday) | ( pPlayer->m_killsYesterday << 16 ) );
	pPlayer->SetUInt32Value(PLAYER_FIELD_TODAY_CONTRIBUTION, pPlayer->m_honorToday);
	pPlayer->SetUInt32Value(PLAYER_FIELD_YESTERDAY_CONTRIBUTION, pPlayer->m_honorYesterday);
	pPlayer->SetUInt32Value(PLAYER_FIELD_LIFETIME_HONORBALE_KILLS, pPlayer->m_killsLifetime);
	pPlayer->SetUInt32Value(PLAYER_FIELD_HONOR_CURRENCY, pPlayer->m_honorPoints);
	pPlayer->SetUInt32Value(PLAYER_FIELD_ARENA_CURRENCY, pPlayer->m_arenaPoints);
}

bool ChatHandler::HandleAddKillCommand(const char* args, WorldSession* m_session)
{
	uint32 KillAmount = args ? atol(args) : 1;
	Player *plr = getSelectedChar(m_session, true);
	if(plr == 0)
		return true;

	BlueSystemMessage(m_session, "Adding %u kills to player %s.", KillAmount, plr->GetName());
	GreenSystemMessage(plr->GetSession(), "You have had %u honor kills added to your character.", KillAmount);

	for(uint32 i = 0; i < KillAmount; ++i)
		HonorHandler::OnPlayerKilledUnit(plr, 0);

	return true;
}

bool ChatHandler::HandleAddHonorCommand(const char* args, WorldSession* m_session)
{
	uint32 HonorAmount = args ? atol(args) : 1;
	Player *plr = getSelectedChar(m_session, true);
	if(plr == 0)
		return true;

	BlueSystemMessage(m_session, "Adding %u honor to player %s.", HonorAmount, plr->GetName());
	GreenSystemMessage(plr->GetSession(), "You have had %u honor points added to your character.", HonorAmount);

	HonorHandler::AddHonorPointsToPlayer(plr, HonorAmount);
	return true;
}

bool ChatHandler::HandlePVPCreditCommand(const char* args, WorldSession* m_session)
{
	uint32 Rank, Points;
	if(sscanf(args, "%u %u", (unsigned int*)&Rank, (unsigned int*)&Points) != 2)
	{
		RedSystemMessage(m_session, "Command must be in format <rank> <points>.");
		return true;
	}
	Points *= 10;
	uint64 Guid = m_session->GetPlayer()->GetSelection();
	if(Guid == 0)
	{
		RedSystemMessage(m_session, "A selection of a unit or player is required.");
		return true;
	}

	BlueSystemMessage(m_session, "Building packet with Rank %u, Points %u, GUID "I64FMT".",
		Rank, Points, Guid);

	WorldPacket data(SMSG_PVP_CREDIT, 12);
	data << Points << Guid << Rank;
	m_session->SendPacket(&data);
	return true;
}

bool ChatHandler::HandleGlobalHonorDailyMaintenanceCommand(const char* args, WorldSession* m_session)
{
	return false;
}

bool ChatHandler::HandleNextDayCommand(const char* args, WorldSession* m_session)
{
	return false;
}
<|endoftext|>


<commit_before>#include <iostream>
#include <vector>
#include <string>
#include <map>
#include <fstream>

using namespace std;
typedef char Symbol;
typedef string Symbols;
typedef vector<string> Patterns;

class Trie {
    private:
    struct Node {
        Node() : value(numberNodes++) {
        }
        map<Symbol, Node*> childrens;

        bool has_edge_with_symbol(Symbol s) {
            return childrens[s] != nullptr;
        }

        Node* get_node_with_symbol(Symbol s) {
            return childrens[s];
        }

        Node* add_node(Symbol s) {
            childrens[s] = new Node();
            return childrens[s];
        }

        bool is_leaf() {
            return childrens.size() == 0;
        }

        ~Node() {
            for (auto pair : childrens)
                delete pair.second;
        }

        int value;
    };

    public:
    Trie(Patterns patterns) {
        root = new Node();
        for (auto str : patterns) {
            Node* currentNode = root;
            for (auto sym : str)
                if (currentNode->has_edge_with_symbol(sym))
                    currentNode = currentNode->get_node_with_symbol(sym);
                else
                    currentNode = currentNode->add_node(sym);
        }
    }

    void print_trie(ostream& os) {
        rec_print(root, os);
    }

    vector<int> matching(const Symbols& text) {
        size_t pos = 0;
        vector<int> positions;
        while (pos < text.size()) {
            if (prefix_matching(text.substr(pos)))
                positions.push_back(pos);
            ++pos;
        }
        return positions;
    }

    ~Trie() {
        delete root;
    }

    private:
    Node* root;
    static int numberNodes;

    void rec_print(Node* start, ostream& os) {
        Node* node = start;
        for (auto child : node->childrens) {
            os << node->value << "->" << child.second->value << ":" << child.first << endl;
            rec_print(child.second, os);
        }
    }

    bool prefix_matching(const Symbols& text) {
        int count = 0;
        Symbol sym = text[count];
        Node* v = root;
        while (true) {
            if (v->is_leaf()) {
                return true;
            } else if (v->has_edge_with_symbol(sym)) {
                ++count;
                v = v->get_node_with_symbol(sym);
                sym = text[count];
            } else
                return false;
        }
    }
};
int Trie::numberNodes = 0;

void read_file(ifstream& file, Symbols& s, Patterns& p) {
    string str;
    file >> s;
    while (file >> str)
        p.push_back(str);
}
void print_vector(ofstream& os, vector<int>& vec) {
    for (auto i : vec)
        os << i << " ";
    os << endl;
}

int main() {
    ofstream answer("answer.txt");
    ifstream file("data.txt");
    Symbols syms;
    Patterns p;
    read_file(file, syms, p);
    Trie trie(p);
    auto vec = trie.matching(syms);
    print_vector(answer, vec);
    return 0;
}
<commit_msg>Add Ukkonen algorithm for building SuffixTree<commit_after>#include <iostream>
#include <vector>
#include <string>
#include <map>
#include <fstream>

using namespace std;
typedef char Symbol;
typedef string Symbols;
typedef vector<string> Patterns;

class Trie {
    private:
    struct Node {
        Node() : value(numberNodes++) {
        }
        map<Symbol, Node*> children;

        bool has_edge_with_symbol(Symbol s) {
            return children[s] != nullptr;
        }

        Node* get_node_with_symbol(Symbol s) {
            return children[s];
        }

        Node* add_node(Symbol s) {
            children[s] = new Node();
            return children[s];
        }

        bool is_leaf() {
            return children.size() == 0;
        }

        ~Node() {
            for (auto pair : children)
                delete pair.second;
        }

        int value;
    };

    public:
    Trie(Patterns patterns) {
        root = new Node();
        for (auto str : patterns) {
            Node* currentNode = root;
            for (auto sym : str)
                if (currentNode->has_edge_with_symbol(sym))
                    currentNode = currentNode->get_node_with_symbol(sym);
                else
                    currentNode = currentNode->add_node(sym);
        }
    }

    void print_trie(ostream& os) {
        rec_print(root, os);
    }

    vector<int> matching(const Symbols& text) {
        size_t pos = 0;
        vector<int> positions;
        while (pos < text.size()) {
            if (prefix_matching(text.substr(pos)))
                positions.push_back(pos);
            ++pos;
        }
        return positions;
    }

    ~Trie() {
        delete root;
    }

    private:
    Node* root;
    static int numberNodes;

    void rec_print(Node* start, ostream& os) {
        Node* node = start;
        for (auto child : node->children) {
            os << node->value << "->" << child.second->value << ":" << child.first << endl;
            rec_print(child.second, os);
        }
    }

    bool prefix_matching(const Symbols& text) {
        int count = 0;
        Symbol sym = text[count];
        Node* v = root;
        while (true) {
            if (v->is_leaf()) {
                return true;
            } else if (v->has_edge_with_symbol(sym)) {
                ++count;
                v = v->get_node_with_symbol(sym);
                sym = text[count];
            } else
                return false;
        }
    }
};
int Trie::numberNodes = 0;

class SuffixTree {
    struct Node; // Forward declaration

    private:
    string text;
    size_t size;
    static Node* root;
    Node* lastNewNode;

    Node* activeNode;
    int activeEdge;
    int activeLength;

    int leafEnd;
    int* rootEnd;
    int* splitEnd;
    int remainingSuffixCount;

    struct Node {
        map<char, Node*> children;
        Node* suffixLink;

        int start;
        int* end;
        bool ownEnd;
        int suffixIndex;

        Node(int s, int* e, bool o) : children({}), suffixLink(root), start(s), end(e), ownEnd(o), suffixIndex(-1) {
        }
        ~Node() {
            for (auto it : children)
                delete it.second;

            if (ownEnd)
                delete end;
        }
        int get_edge_length() {
            return *end - start + 1;
        }
    };
    bool try_walk_down(Node& currNode) {
        int length = currNode.get_edge_length();
        if (activeLength >= length) {
            activeEdge += length;
            activeLength -= length;
            activeNode = &currNode;
            return true;
        }
        return false;
    }

    void extend_suffix_tree(int pos) {
        leafEnd = pos;
        remainingSuffixCount++;
        lastNewNode = nullptr;

        while (remainingSuffixCount > 0) {
            if (activeLength == 0)
                activeEdge = pos;

            if (activeNode->children[text[activeEdge]] == nullptr) {
                activeNode->children[text[activeEdge]] = new Node(pos, &leafEnd, false);

                if (lastNewNode != nullptr) {
                    lastNewNode->suffixLink = activeNode;
                    lastNewNode = nullptr;
                }
            } else {
                Node& next = *activeNode->children[text[activeEdge]];
                if (try_walk_down(next))
                    continue;

                if (text[next.start + activeLength] == text[pos]) {
                    if (lastNewNode != nullptr && activeNode != root) {
                        lastNewNode->suffixLink = activeNode;
                        lastNewNode = nullptr;
                    }

                    activeLength++;
                    break;
                }

                // New internal node
                splitEnd = new int(next.start + activeLength - 1);
                auto split = new Node(next.start, splitEnd, true);
                activeNode->children[text[activeEdge]] = split;

                // New leaf coming out of new internal node
                split->children[text[pos]] = new Node(pos, &leafEnd, false);
                next.start += activeLength;
                split->children[text[next.start]] = &next;

                if (lastNewNode != nullptr)
                    lastNewNode->suffixLink = split;

                lastNewNode = split;
            }

            remainingSuffixCount--;
            if (activeNode == root && activeLength > 0) {
                activeLength--;
                activeEdge = pos - remainingSuffixCount + 1;
            } else if (activeNode != root)
                activeNode = activeNode->suffixLink;
        }
    }

    void set_suffix_index_by_DFS(Node* n, int labelHeight) {
        if (n == nullptr)
            return;

        bool leaf = true;

        for (auto child : n->children) {
            leaf = false;
            set_suffix_index_by_DFS(child.second, labelHeight + child.second->get_edge_length());
        }
        if (leaf == true)
            n->suffixIndex = size - labelHeight;
    }

    void build_suffix_tree() {
        size = text.size();

        rootEnd = new int(-1);

        root = new Node(-1, rootEnd, true);
        root->suffixLink = nullptr;
        activeNode = root;

        for (size_t i = 0; i < size; i++)
            extend_suffix_tree(i);
    }

    void print(int i, int j, ofstream& os) {
        for (int k = i; k <= j; k++)
            os << text[k];
        os << endl;
    }
    void print_tree_by_DFS(Node* n, ofstream& os) {
        if (n == nullptr)
            return;

        if (n->start != -1)
            print(n->start, *(n->end), os);

        for (auto child : n->children)
            print_tree_by_DFS(child.second, os);
    }

    public:
    SuffixTree(string str)
    : text(std::move(str)), activeNode(nullptr), activeEdge(-1), activeLength(0), leafEnd(-1), rootEnd(nullptr),
      splitEnd(nullptr), remainingSuffixCount(0) {
        build_suffix_tree();
        int labelHeight = 0;
        set_suffix_index_by_DFS(root, labelHeight);
    }

    void print_tree(ofstream& os) {
        print_tree_by_DFS(root, os);
    }

    ~SuffixTree() {
        delete root;
    }
};
SuffixTree::Node* SuffixTree::root = nullptr;

void read_file(ifstream& file, Symbols& s, Patterns& p) {
    string str;
    file >> s;
    //    while (file >> str)
    //       p.push_back(str);
}
void print_vector(ofstream& os, vector<int>& vec) {
    for (auto i : vec)
        os << i << " ";
    os << endl;
}

int main() {
    ofstream answer("answer.txt");
    ifstream file("data.txt");
    Symbols syms;
    Patterns p;
    read_file(file, syms, p);
    SuffixTree tree(syms);
    tree.print_tree(answer);
    return 0;
}
<|endoftext|>


<commit_before>////////////////////////////////////////////////////////////////////////////
//
// Copyright 2020 Realm Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
////////////////////////////////////////////////////////////////////////////

#include <realm/util/to_string.hpp>

#include <atomic>
#include <dispatch/dispatch.h>
#include <objc/runtime.h>
#include <pthread.h>

namespace {
using namespace realm;

class RunLoopScheduler : public util::Scheduler {
public:
    RunLoopScheduler(CFRunLoopRef run_loop = nullptr);
    ~RunLoopScheduler();

    void notify() override;
    void set_notify_callback(std::function<void()>) override;

    bool is_on_thread() const noexcept override;
    bool can_deliver_notifications() const noexcept override;

private:
    CFRunLoopRef m_runloop;
    CFRunLoopSourceRef m_signal = nullptr;
};

RunLoopScheduler::RunLoopScheduler(CFRunLoopRef run_loop)
: m_runloop(run_loop ?: CFRunLoopGetCurrent())
{
    CFRetain(m_runloop);
}

RunLoopScheduler::~RunLoopScheduler()
{
    if (m_signal) {
        CFRunLoopSourceInvalidate(m_signal);
        CFRelease(m_signal);
    }
    CFRelease(m_runloop);
}

void RunLoopScheduler::set_notify_callback(std::function<void ()> callback)
{
    if (m_signal) {
        CFRunLoopSourceInvalidate(m_signal);
        CFRelease(m_signal);
        m_signal = nullptr;
    }

    struct RefCountedRunloopCallback {
        std::function<void()> callback;
        std::atomic<size_t> ref_count;
    };

    CFRunLoopSourceContext ctx{};
    ctx.info = new RefCountedRunloopCallback{std::move(callback), {0}};
    ctx.perform = [](void* info) {
        static_cast<RefCountedRunloopCallback*>(info)->callback();
    };
    ctx.retain = [](const void* info) {
        static_cast<RefCountedRunloopCallback*>(const_cast<void*>(info))->ref_count.fetch_add(1, std::memory_order_relaxed);
        return info;
    };
    ctx.release = [](const void* info) {
        auto ptr = static_cast<RefCountedRunloopCallback*>(const_cast<void*>(info));
        if (ptr->ref_count.fetch_add(-1, std::memory_order_acq_rel) == 1) {
            delete ptr;
        }
    };

    m_signal = CFRunLoopSourceCreate(kCFAllocatorDefault, 0, &ctx);
    CFRunLoopAddSource(m_runloop, m_signal, kCFRunLoopDefaultMode);
}

void RunLoopScheduler::notify()
{
    if (!m_signal)
        return;

    CFRunLoopSourceSignal(m_signal);
    // Signalling the source makes it run the next time the runloop gets
    // to it, but doesn't make the runloop start if it's currently idle
    // waiting for events
    CFRunLoopWakeUp(m_runloop);
}

bool RunLoopScheduler::is_on_thread() const noexcept
{
    return CFRunLoopGetCurrent() == m_runloop;
}

bool RunLoopScheduler::can_deliver_notifications() const noexcept
{
    // The main thread may not be in a run loop yet if we're called from
    // something like `applicationDidFinishLaunching:`, but it presumably will
    // be in the future
    if (pthread_main_np())
        return true;

    // Current mode indicates why the current callout from the runloop was made,
    // and is null if a runloop callout isn't currently being processed
    if (auto mode = CFRunLoopCopyCurrentMode(CFRunLoopGetCurrent())) {
        CFRelease(mode);
        return true;
    }
    return false;
}

class DispatchQueueScheduler : public util::Scheduler {
public:
    DispatchQueueScheduler(dispatch_queue_t queue);
    ~DispatchQueueScheduler();

    void notify() override;
    void set_notify_callback(std::function<void()>) override;

    bool is_on_thread() const noexcept override;
    bool can_deliver_notifications() const noexcept override { return true; }

private:
    dispatch_queue_t m_queue;
    void (^m_callback)();
};

static const void* c_queue_key = &c_queue_key;

DispatchQueueScheduler::DispatchQueueScheduler(dispatch_queue_t queue)
: m_queue(queue)
{
    static auto class_dispatch_queue_serial = objc_getClass("OS_dispatch_queue_serial");
    static auto class_dispatch_queue_main = objc_getClass("OS_dispatch_queue_main");
    auto cls = object_getClass(reinterpret_cast<id>(queue));
    if (cls != class_dispatch_queue_serial && cls != class_dispatch_queue_main) {
        auto msg = util::format("Invalid queue '%1' (%2): Realms can only be confined to serial queues or the main queue.",
                                dispatch_queue_get_label(queue) ?: "<nil>", class_getName(cls));
        throw std::logic_error(msg);
    }
    dispatch_retain(m_queue);
    if (dispatch_queue_get_specific(m_queue, c_queue_key) == nullptr) {
        dispatch_queue_set_specific(m_queue, c_queue_key, queue, nullptr);
    }
}

DispatchQueueScheduler::~DispatchQueueScheduler()
{
    dispatch_release(m_queue);
    if (m_callback)
        Block_release(m_callback);
}

void DispatchQueueScheduler::notify()
{
    dispatch_async(m_queue, m_callback);
}

void DispatchQueueScheduler::set_notify_callback(std::function<void ()> callback)
{
    m_callback = Block_copy(^{
        callback();
    });
}

bool DispatchQueueScheduler::is_on_thread() const noexcept
{
    return dispatch_get_specific(c_queue_key) == m_queue;
}
} // anonymous namespace

namespace realm {
namespace util {
std::shared_ptr<Scheduler> Scheduler::make_default()
{
    return std::make_shared<RunLoopScheduler>();
}

std::shared_ptr<Scheduler> Scheduler::make_runloop(CFRunLoopRef run_loop)
{
    return std::make_shared<RunLoopScheduler>(run_loop ?: CFRunLoopGetCurrent());
}

std::shared_ptr<Scheduler> Scheduler::make_dispatch(void* queue)
{
    return std::make_shared<DispatchQueueScheduler>(static_cast<dispatch_queue_t>(queue));
}
} // namespace util
} // namespace realm
<commit_msg>Fix a crash when a DispatchQueueScheduler is destroyed without ever setting the callback<commit_after>////////////////////////////////////////////////////////////////////////////
//
// Copyright 2020 Realm Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
////////////////////////////////////////////////////////////////////////////

#include <realm/util/to_string.hpp>

#include <atomic>
#include <dispatch/dispatch.h>
#include <objc/runtime.h>
#include <pthread.h>

namespace {
using namespace realm;

class RunLoopScheduler : public util::Scheduler {
public:
    RunLoopScheduler(CFRunLoopRef run_loop = nullptr);
    ~RunLoopScheduler();

    void notify() override;
    void set_notify_callback(std::function<void()>) override;

    bool is_on_thread() const noexcept override;
    bool can_deliver_notifications() const noexcept override;

private:
    CFRunLoopRef m_runloop;
    CFRunLoopSourceRef m_signal = nullptr;
};

RunLoopScheduler::RunLoopScheduler(CFRunLoopRef run_loop)
: m_runloop(run_loop ?: CFRunLoopGetCurrent())
{
    CFRetain(m_runloop);
}

RunLoopScheduler::~RunLoopScheduler()
{
    if (m_signal) {
        CFRunLoopSourceInvalidate(m_signal);
        CFRelease(m_signal);
    }
    CFRelease(m_runloop);
}

void RunLoopScheduler::set_notify_callback(std::function<void ()> callback)
{
    if (m_signal) {
        CFRunLoopSourceInvalidate(m_signal);
        CFRelease(m_signal);
        m_signal = nullptr;
    }

    struct RefCountedRunloopCallback {
        std::function<void()> callback;
        std::atomic<size_t> ref_count;
    };

    CFRunLoopSourceContext ctx{};
    ctx.info = new RefCountedRunloopCallback{std::move(callback), {0}};
    ctx.perform = [](void* info) {
        static_cast<RefCountedRunloopCallback*>(info)->callback();
    };
    ctx.retain = [](const void* info) {
        static_cast<RefCountedRunloopCallback*>(const_cast<void*>(info))->ref_count.fetch_add(1, std::memory_order_relaxed);
        return info;
    };
    ctx.release = [](const void* info) {
        auto ptr = static_cast<RefCountedRunloopCallback*>(const_cast<void*>(info));
        if (ptr->ref_count.fetch_add(-1, std::memory_order_acq_rel) == 1) {
            delete ptr;
        }
    };

    m_signal = CFRunLoopSourceCreate(kCFAllocatorDefault, 0, &ctx);
    CFRunLoopAddSource(m_runloop, m_signal, kCFRunLoopDefaultMode);
}

void RunLoopScheduler::notify()
{
    if (!m_signal)
        return;

    CFRunLoopSourceSignal(m_signal);
    // Signalling the source makes it run the next time the runloop gets
    // to it, but doesn't make the runloop start if it's currently idle
    // waiting for events
    CFRunLoopWakeUp(m_runloop);
}

bool RunLoopScheduler::is_on_thread() const noexcept
{
    return CFRunLoopGetCurrent() == m_runloop;
}

bool RunLoopScheduler::can_deliver_notifications() const noexcept
{
    // The main thread may not be in a run loop yet if we're called from
    // something like `applicationDidFinishLaunching:`, but it presumably will
    // be in the future
    if (pthread_main_np())
        return true;

    // Current mode indicates why the current callout from the runloop was made,
    // and is null if a runloop callout isn't currently being processed
    if (auto mode = CFRunLoopCopyCurrentMode(CFRunLoopGetCurrent())) {
        CFRelease(mode);
        return true;
    }
    return false;
}

class DispatchQueueScheduler : public util::Scheduler {
public:
    DispatchQueueScheduler(dispatch_queue_t queue);
    ~DispatchQueueScheduler();

    void notify() override;
    void set_notify_callback(std::function<void()>) override;

    bool is_on_thread() const noexcept override;
    bool can_deliver_notifications() const noexcept override { return true; }

private:
    dispatch_queue_t m_queue = nullptr;
    void (^m_callback)() = nullptr;
};

static const void* c_queue_key = &c_queue_key;

DispatchQueueScheduler::DispatchQueueScheduler(dispatch_queue_t queue)
: m_queue(queue)
{
    static auto class_dispatch_queue_serial = objc_getClass("OS_dispatch_queue_serial");
    static auto class_dispatch_queue_main = objc_getClass("OS_dispatch_queue_main");
    auto cls = object_getClass(reinterpret_cast<id>(queue));
    if (cls != class_dispatch_queue_serial && cls != class_dispatch_queue_main) {
        auto msg = util::format("Invalid queue '%1' (%2): Realms can only be confined to serial queues or the main queue.",
                                dispatch_queue_get_label(queue) ?: "<nil>", class_getName(cls));
        throw std::logic_error(msg);
    }
    dispatch_retain(m_queue);
    if (dispatch_queue_get_specific(m_queue, c_queue_key) == nullptr) {
        dispatch_queue_set_specific(m_queue, c_queue_key, queue, nullptr);
    }
}

DispatchQueueScheduler::~DispatchQueueScheduler()
{
    dispatch_release(m_queue);
    if (m_callback)
        Block_release(m_callback);
}

void DispatchQueueScheduler::notify()
{
    dispatch_async(m_queue, m_callback);
}

void DispatchQueueScheduler::set_notify_callback(std::function<void ()> callback)
{
    m_callback = Block_copy(^{
        callback();
    });
}

bool DispatchQueueScheduler::is_on_thread() const noexcept
{
    return dispatch_get_specific(c_queue_key) == m_queue;
}
} // anonymous namespace

namespace realm {
namespace util {
std::shared_ptr<Scheduler> Scheduler::make_default()
{
    return std::make_shared<RunLoopScheduler>();
}

std::shared_ptr<Scheduler> Scheduler::make_runloop(CFRunLoopRef run_loop)
{
    return std::make_shared<RunLoopScheduler>(run_loop ?: CFRunLoopGetCurrent());
}

std::shared_ptr<Scheduler> Scheduler::make_dispatch(void* queue)
{
    return std::make_shared<DispatchQueueScheduler>(static_cast<dispatch_queue_t>(queue));
}
} // namespace util
} // namespace realm
<|endoftext|>


<commit_before><commit_msg>Prevent AI Engineers from being created if there is a Strike event on the planet, or if the planet is blockaded.<commit_after><|endoftext|>


<commit_before>// Copyright (c) 2010 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chrome/browser/sync/notifier/cache_invalidation_packet_handler.h"

#include <string>

#include "base/base64.h"
#include "base/callback.h"
#include "base/logging.h"
#include "base/rand_util.h"
#include "base/string_util.h"
#include "chrome/browser/sync/sync_constants.h"
#include "google/cacheinvalidation/invalidation-client.h"
#include "jingle/notifier/listener/xml_element_util.h"
#include "talk/xmpp/constants.h"
#include "talk/xmpp/jid.h"
#include "talk/xmpp/xmppclient.h"
#include "talk/xmpp/xmpptask.h"

namespace sync_notifier {

namespace {

const char kBotJid[] = "tango@bot.talk.google.com";

const buzz::QName kQnData("google:notifier", "data");
const buzz::QName kQnSeq("", "seq");
const buzz::QName kQnSid("", "sid");
const buzz::QName kQnServiceUrl("", "serviceUrl");

// TODO(akalin): Move these task classes out so that they can be
// unit-tested.  This'll probably be done easier once we consolidate
// all the packet sending/receiving classes.

// A task that listens for ClientInvalidation messages and calls the
// given callback on them.
class CacheInvalidationListenTask : public buzz::XmppTask {
 public:
  // Takes ownership of callback.
  CacheInvalidationListenTask(Task* parent,
                              Callback1<const std::string&>::Type* callback)
      : XmppTask(parent, buzz::XmppEngine::HL_TYPE), callback_(callback) {}
  virtual ~CacheInvalidationListenTask() {}

  virtual int ProcessStart() {
    LOG(INFO) << "CacheInvalidationListenTask started";
    return STATE_RESPONSE;
  }

  virtual int ProcessResponse() {
    const buzz::XmlElement* stanza = NextStanza();
    if (stanza == NULL) {
      LOG(INFO) << "CacheInvalidationListenTask blocked";
      return STATE_BLOCKED;
    }
    LOG(INFO) << "CacheInvalidationListenTask response received";
    std::string data;
    if (GetCacheInvalidationIqPacketData(stanza, &data)) {
      callback_->Run(data);
    } else {
      LOG(ERROR) << "Could not get packet data";
    }
    // Acknowledge receipt of the iq to the buzz server.
    // TODO(akalin): Send an error response for malformed packets.
    scoped_ptr<buzz::XmlElement> response_stanza(MakeIqResult(stanza));
    SendStanza(response_stanza.get());
    return STATE_RESPONSE;
  }

  virtual bool HandleStanza(const buzz::XmlElement* stanza) {
    LOG(INFO) << "Stanza received: "
              << notifier::XmlElementToString(*stanza);
    if (IsValidCacheInvalidationIqPacket(stanza)) {
      LOG(INFO) << "Queueing stanza";
      QueueStanza(stanza);
      return true;
    }
    LOG(INFO) << "Stanza skipped";
    return false;
  }

 private:
  bool IsValidCacheInvalidationIqPacket(const buzz::XmlElement* stanza) {
    return
        (MatchRequestIq(stanza, buzz::STR_SET, kQnData) &&
         (stanza->Attr(buzz::QN_TO) == GetClient()->jid().Str()));
  }

  bool GetCacheInvalidationIqPacketData(const buzz::XmlElement* stanza,
                            std::string* data) {
    DCHECK(IsValidCacheInvalidationIqPacket(stanza));
    const buzz::XmlElement* cache_invalidation_iq_packet =
        stanza->FirstNamed(kQnData);
    if (!cache_invalidation_iq_packet) {
      LOG(ERROR) << "Could not find cache invalidation IQ packet element";
      return false;
    }
    *data = cache_invalidation_iq_packet->BodyText();
    return true;
  }

  scoped_ptr<Callback1<const std::string&>::Type> callback_;
  DISALLOW_COPY_AND_ASSIGN(CacheInvalidationListenTask);
};

// A task that sends a single outbound ClientInvalidation message.
class CacheInvalidationSendMessageTask : public buzz::XmppTask {
 public:
  CacheInvalidationSendMessageTask(Task* parent,
                                   const buzz::Jid& to_jid,
                                   const std::string& msg,
                                   int seq,
                                   const std::string& sid)
      : XmppTask(parent, buzz::XmppEngine::HL_SINGLE),
        to_jid_(to_jid), msg_(msg), seq_(seq), sid_(sid) {}
  virtual ~CacheInvalidationSendMessageTask() {}

  virtual int ProcessStart() {
    scoped_ptr<buzz::XmlElement> stanza(
        MakeCacheInvalidationIqPacket(to_jid_, task_id(), msg_,
                                      seq_, sid_));
    LOG(INFO) << "Sending message: "
              << notifier::XmlElementToString(*stanza.get());
    if (SendStanza(stanza.get()) != buzz::XMPP_RETURN_OK) {
      LOG(INFO) << "Error when sending message";
      return STATE_ERROR;
    }
    return STATE_RESPONSE;
  }

  virtual int ProcessResponse() {
    const buzz::XmlElement* stanza = NextStanza();
    if (stanza == NULL) {
      LOG(INFO) << "CacheInvalidationSendMessageTask blocked...";
      return STATE_BLOCKED;
    }
    LOG(INFO) << "CacheInvalidationSendMessageTask response received: "
              << notifier::XmlElementToString(*stanza);
    // TODO(akalin): Handle errors here.
    return STATE_DONE;
  }

  virtual bool HandleStanza(const buzz::XmlElement* stanza) {
    LOG(INFO) << "Stanza received: "
              << notifier::XmlElementToString(*stanza);
    if (!MatchResponseIq(stanza, to_jid_, task_id())) {
      LOG(INFO) << "Stanza skipped";
      return false;
    }
    LOG(INFO) << "Queueing stanza";
    QueueStanza(stanza);
    return true;
  }

 private:
  static buzz::XmlElement* MakeCacheInvalidationIqPacket(
      const buzz::Jid& to_jid,
      const std::string& task_id,
      const std::string& msg,
      int seq, const std::string& sid) {
    buzz::XmlElement* iq = MakeIq(buzz::STR_SET, to_jid, task_id);
    buzz::XmlElement* cache_invalidation_iq_packet =
        new buzz::XmlElement(kQnData, true);
    iq->AddElement(cache_invalidation_iq_packet);
    // TODO(akalin): Remove use of seq and sid.
    cache_invalidation_iq_packet->SetAttr(kQnSeq, IntToString(seq));
    cache_invalidation_iq_packet->SetAttr(kQnSid, sid);
    cache_invalidation_iq_packet->SetAttr(kQnServiceUrl,
                                          browser_sync::kSyncServiceUrl);
    cache_invalidation_iq_packet->SetBodyText(msg);
    return iq;
  }

  const buzz::Jid to_jid_;
  std::string msg_;
  int seq_;
  std::string sid_;

  DISALLOW_COPY_AND_ASSIGN(CacheInvalidationSendMessageTask);
};

std::string MakeSid() {
  uint64 sid = base::RandUint64();
  return std::string("chrome-sync-") + Uint64ToString(sid);
}

}  // namespace

CacheInvalidationPacketHandler::CacheInvalidationPacketHandler(
    buzz::XmppClient* xmpp_client,
    invalidation::InvalidationClient* invalidation_client)
    : xmpp_client_(xmpp_client),
      invalidation_client_(invalidation_client),
      seq_(0),
      sid_(MakeSid()) {
  CHECK(xmpp_client_);
  CHECK(invalidation_client_);
  invalidation::NetworkEndpoint* network_endpoint =
      invalidation_client_->network_endpoint();
  CHECK(network_endpoint);
  network_endpoint->RegisterOutboundListener(
      invalidation::NewPermanentCallback(
          this,
          &CacheInvalidationPacketHandler::HandleOutboundPacket));
  // Owned by xmpp_client.
  CacheInvalidationListenTask* listen_task =
      new CacheInvalidationListenTask(
          xmpp_client, NewCallback(
              this, &CacheInvalidationPacketHandler::HandleInboundPacket));
  listen_task->Start();
}

CacheInvalidationPacketHandler::~CacheInvalidationPacketHandler() {
  invalidation::NetworkEndpoint* network_endpoint =
      invalidation_client_->network_endpoint();
  CHECK(network_endpoint);
  network_endpoint->RegisterOutboundListener(NULL);
}

void CacheInvalidationPacketHandler::HandleOutboundPacket(
    invalidation::NetworkEndpoint* const& network_endpoint) {
  CHECK_EQ(network_endpoint, invalidation_client_->network_endpoint());
  invalidation::string message;
  network_endpoint->TakeOutboundMessage(&message);
  std::string encoded_message;
  if (!base::Base64Encode(message, &encoded_message)) {
    LOG(ERROR) << "Could not base64-encode message to send: "
               << message;
    return;
  }
  // Owned by xmpp_client.
  CacheInvalidationSendMessageTask* send_message_task =
      new CacheInvalidationSendMessageTask(xmpp_client_,
                                           buzz::Jid(kBotJid),
                                           encoded_message,
                                           seq_, sid_);
  send_message_task->Start();
  ++seq_;
}

void CacheInvalidationPacketHandler::HandleInboundPacket(
    const std::string& packet) {
  invalidation::NetworkEndpoint* network_endpoint =
      invalidation_client_->network_endpoint();
  std::string decoded_message;
  if (!base::Base64Decode(packet, &decoded_message)) {
    LOG(ERROR) << "Could not base64-decode received message: "
               << packet;
    return;
  }
  network_endpoint->HandleInboundMessage(decoded_message);
}

}  // namespace sync_notifier
<commit_msg>Relaxed check in IsValidCacheInvalidationIqPacket().<commit_after>// Copyright (c) 2010 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chrome/browser/sync/notifier/cache_invalidation_packet_handler.h"

#include <string>

#include "base/base64.h"
#include "base/callback.h"
#include "base/logging.h"
#include "base/rand_util.h"
#include "base/string_util.h"
#include "chrome/browser/sync/sync_constants.h"
#include "google/cacheinvalidation/invalidation-client.h"
#include "jingle/notifier/listener/xml_element_util.h"
#include "talk/xmpp/constants.h"
#include "talk/xmpp/jid.h"
#include "talk/xmpp/xmppclient.h"
#include "talk/xmpp/xmpptask.h"

namespace sync_notifier {

namespace {

const char kBotJid[] = "tango@bot.talk.google.com";

const buzz::QName kQnData("google:notifier", "data");
const buzz::QName kQnSeq("", "seq");
const buzz::QName kQnSid("", "sid");
const buzz::QName kQnServiceUrl("", "serviceUrl");

// TODO(akalin): Move these task classes out so that they can be
// unit-tested.  This'll probably be done easier once we consolidate
// all the packet sending/receiving classes.

// A task that listens for ClientInvalidation messages and calls the
// given callback on them.
class CacheInvalidationListenTask : public buzz::XmppTask {
 public:
  // Takes ownership of callback.
  CacheInvalidationListenTask(Task* parent,
                              Callback1<const std::string&>::Type* callback)
      : XmppTask(parent, buzz::XmppEngine::HL_TYPE), callback_(callback) {}
  virtual ~CacheInvalidationListenTask() {}

  virtual int ProcessStart() {
    LOG(INFO) << "CacheInvalidationListenTask started";
    return STATE_RESPONSE;
  }

  virtual int ProcessResponse() {
    const buzz::XmlElement* stanza = NextStanza();
    if (stanza == NULL) {
      LOG(INFO) << "CacheInvalidationListenTask blocked";
      return STATE_BLOCKED;
    }
    LOG(INFO) << "CacheInvalidationListenTask response received";
    std::string data;
    if (GetCacheInvalidationIqPacketData(stanza, &data)) {
      callback_->Run(data);
    } else {
      LOG(ERROR) << "Could not get packet data";
    }
    // Acknowledge receipt of the iq to the buzz server.
    // TODO(akalin): Send an error response for malformed packets.
    scoped_ptr<buzz::XmlElement> response_stanza(MakeIqResult(stanza));
    SendStanza(response_stanza.get());
    return STATE_RESPONSE;
  }

  virtual bool HandleStanza(const buzz::XmlElement* stanza) {
    LOG(INFO) << "Stanza received: "
              << notifier::XmlElementToString(*stanza);
    if (IsValidCacheInvalidationIqPacket(stanza)) {
      LOG(INFO) << "Queueing stanza";
      QueueStanza(stanza);
      return true;
    }
    LOG(INFO) << "Stanza skipped";
    return false;
  }

 private:
  bool IsValidCacheInvalidationIqPacket(const buzz::XmlElement* stanza) {
    // We make sure to compare jids (which are normalized) instead of
    // just strings -- server may use non-normalized jids in
    // attributes.
    //
    // TODO(akalin): Add unit tests for this.
    buzz::Jid to(stanza->Attr(buzz::QN_TO));
    return
        (MatchRequestIq(stanza, buzz::STR_SET, kQnData) &&
         (to == GetClient()->jid()));
  }

  bool GetCacheInvalidationIqPacketData(const buzz::XmlElement* stanza,
                            std::string* data) {
    DCHECK(IsValidCacheInvalidationIqPacket(stanza));
    const buzz::XmlElement* cache_invalidation_iq_packet =
        stanza->FirstNamed(kQnData);
    if (!cache_invalidation_iq_packet) {
      LOG(ERROR) << "Could not find cache invalidation IQ packet element";
      return false;
    }
    *data = cache_invalidation_iq_packet->BodyText();
    return true;
  }

  scoped_ptr<Callback1<const std::string&>::Type> callback_;
  DISALLOW_COPY_AND_ASSIGN(CacheInvalidationListenTask);
};

// A task that sends a single outbound ClientInvalidation message.
class CacheInvalidationSendMessageTask : public buzz::XmppTask {
 public:
  CacheInvalidationSendMessageTask(Task* parent,
                                   const buzz::Jid& to_jid,
                                   const std::string& msg,
                                   int seq,
                                   const std::string& sid)
      : XmppTask(parent, buzz::XmppEngine::HL_SINGLE),
        to_jid_(to_jid), msg_(msg), seq_(seq), sid_(sid) {}
  virtual ~CacheInvalidationSendMessageTask() {}

  virtual int ProcessStart() {
    scoped_ptr<buzz::XmlElement> stanza(
        MakeCacheInvalidationIqPacket(to_jid_, task_id(), msg_,
                                      seq_, sid_));
    LOG(INFO) << "Sending message: "
              << notifier::XmlElementToString(*stanza.get());
    if (SendStanza(stanza.get()) != buzz::XMPP_RETURN_OK) {
      LOG(INFO) << "Error when sending message";
      return STATE_ERROR;
    }
    return STATE_RESPONSE;
  }

  virtual int ProcessResponse() {
    const buzz::XmlElement* stanza = NextStanza();
    if (stanza == NULL) {
      LOG(INFO) << "CacheInvalidationSendMessageTask blocked...";
      return STATE_BLOCKED;
    }
    LOG(INFO) << "CacheInvalidationSendMessageTask response received: "
              << notifier::XmlElementToString(*stanza);
    // TODO(akalin): Handle errors here.
    return STATE_DONE;
  }

  virtual bool HandleStanza(const buzz::XmlElement* stanza) {
    LOG(INFO) << "Stanza received: "
              << notifier::XmlElementToString(*stanza);
    if (!MatchResponseIq(stanza, to_jid_, task_id())) {
      LOG(INFO) << "Stanza skipped";
      return false;
    }
    LOG(INFO) << "Queueing stanza";
    QueueStanza(stanza);
    return true;
  }

 private:
  static buzz::XmlElement* MakeCacheInvalidationIqPacket(
      const buzz::Jid& to_jid,
      const std::string& task_id,
      const std::string& msg,
      int seq, const std::string& sid) {
    buzz::XmlElement* iq = MakeIq(buzz::STR_SET, to_jid, task_id);
    buzz::XmlElement* cache_invalidation_iq_packet =
        new buzz::XmlElement(kQnData, true);
    iq->AddElement(cache_invalidation_iq_packet);
    cache_invalidation_iq_packet->SetAttr(kQnSeq, IntToString(seq));
    cache_invalidation_iq_packet->SetAttr(kQnSid, sid);
    cache_invalidation_iq_packet->SetAttr(kQnServiceUrl,
                                          browser_sync::kSyncServiceUrl);
    cache_invalidation_iq_packet->SetBodyText(msg);
    return iq;
  }

  const buzz::Jid to_jid_;
  std::string msg_;
  int seq_;
  std::string sid_;

  DISALLOW_COPY_AND_ASSIGN(CacheInvalidationSendMessageTask);
};

std::string MakeSid() {
  uint64 sid = base::RandUint64();
  return std::string("chrome-sync-") + Uint64ToString(sid);
}

}  // namespace

CacheInvalidationPacketHandler::CacheInvalidationPacketHandler(
    buzz::XmppClient* xmpp_client,
    invalidation::InvalidationClient* invalidation_client)
    : xmpp_client_(xmpp_client),
      invalidation_client_(invalidation_client),
      seq_(0),
      sid_(MakeSid()) {
  CHECK(xmpp_client_);
  CHECK(invalidation_client_);
  invalidation::NetworkEndpoint* network_endpoint =
      invalidation_client_->network_endpoint();
  CHECK(network_endpoint);
  network_endpoint->RegisterOutboundListener(
      invalidation::NewPermanentCallback(
          this,
          &CacheInvalidationPacketHandler::HandleOutboundPacket));
  // Owned by xmpp_client.
  CacheInvalidationListenTask* listen_task =
      new CacheInvalidationListenTask(
          xmpp_client, NewCallback(
              this, &CacheInvalidationPacketHandler::HandleInboundPacket));
  listen_task->Start();
}

CacheInvalidationPacketHandler::~CacheInvalidationPacketHandler() {
  invalidation::NetworkEndpoint* network_endpoint =
      invalidation_client_->network_endpoint();
  CHECK(network_endpoint);
  network_endpoint->RegisterOutboundListener(NULL);
}

void CacheInvalidationPacketHandler::HandleOutboundPacket(
    invalidation::NetworkEndpoint* const& network_endpoint) {
  CHECK_EQ(network_endpoint, invalidation_client_->network_endpoint());
  invalidation::string message;
  network_endpoint->TakeOutboundMessage(&message);
  std::string encoded_message;
  if (!base::Base64Encode(message, &encoded_message)) {
    LOG(ERROR) << "Could not base64-encode message to send: "
               << message;
    return;
  }
  // Owned by xmpp_client.
  CacheInvalidationSendMessageTask* send_message_task =
      new CacheInvalidationSendMessageTask(xmpp_client_,
                                           buzz::Jid(kBotJid),
                                           encoded_message,
                                           seq_, sid_);
  send_message_task->Start();
  ++seq_;
}

void CacheInvalidationPacketHandler::HandleInboundPacket(
    const std::string& packet) {
  invalidation::NetworkEndpoint* network_endpoint =
      invalidation_client_->network_endpoint();
  std::string decoded_message;
  if (!base::Base64Decode(packet, &decoded_message)) {
    LOG(ERROR) << "Could not base64-decode received message: "
               << packet;
    return;
  }
  network_endpoint->HandleInboundMessage(decoded_message);
}

}  // namespace sync_notifier
<|endoftext|>


<commit_before>#ifdef PLATFORM_ANDROID

#include "platform_android.h"
#include "tangram.h"

#include <GLES2/gl2platform.h>

#ifndef GL_GLEXT_PROTOTYPES
#define GL_GLEXT_PROTOTYPES 1
#endif

#include <GLES2/gl2.h>
#include <GLES2/gl2ext.h>
#include <dlfcn.h> // dlopen, dlsym

#include <android/log.h>
#include <android/asset_manager.h>
#include <android/asset_manager_jni.h>
#include <cstdarg>

#include <libgen.h>
#include <unistd.h>
#include <sys/resource.h>
#include <fstream>

/* Followed the following document for JavaVM tips when used with native threads
 * http://android.wooyd.org/JNIExample/#NWD1sCYeT-I
 * http://developer.android.com/training/articles/perf-jni.html and
 * http://www.ibm.com/developerworks/library/j-jni/
 * http://docs.oracle.com/javase/7/docs/technotes/guides/jni/spec/invocation.html
 */

static JavaVM* jvm;
// JNI Env bound on androids render thread (our native main thread)
static JNIEnv* jniRenderThreadEnv;
static jobject tangramInstance;
static jmethodID requestRenderMethodID;
static jmethodID setRenderModeMethodID;
static jmethodID startUrlRequestMID;
static jmethodID cancelUrlRequestMID;
static jmethodID getFontFilePath;
static jmethodID featureSelectionCbMID;

static jmethodID propertiesConstructorMID;

static AAssetManager* assetManager;

static bool s_isContinuousRendering = false;
static bool s_useInternalResources = true;
static std::string s_resourceRoot;

PFNGLBINDVERTEXARRAYOESPROC glBindVertexArrayOESEXT = 0;
PFNGLDELETEVERTEXARRAYSOESPROC glDeleteVertexArraysOESEXT = 0;
PFNGLGENVERTEXARRAYSOESPROC glGenVertexArraysOESEXT = 0;

void setupJniEnv(JNIEnv* _jniEnv, jobject _tangramInstance, jobject _assetManager) {
    _jniEnv->GetJavaVM(&jvm);
    JNIEnv* jniEnv = _jniEnv;
    jniRenderThreadEnv = _jniEnv;

    tangramInstance = jniEnv->NewGlobalRef(_tangramInstance);
    jclass tangramClass = jniEnv->FindClass("com/mapzen/tangram/MapController");
    startUrlRequestMID = jniEnv->GetMethodID(tangramClass, "startUrlRequest", "(Ljava/lang/String;J)Z");
    cancelUrlRequestMID = jniEnv->GetMethodID(tangramClass, "cancelUrlRequest", "(Ljava/lang/String;)V");
    getFontFilePath = jniEnv->GetMethodID(tangramClass, "getFontFilePath", "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;");
        requestRenderMethodID = _jniEnv->GetMethodID(tangramClass, "requestRender", "()V");
    setRenderModeMethodID = _jniEnv->GetMethodID(tangramClass, "setRenderMode", "(I)V");
    featureSelectionCbMID = _jniEnv->GetMethodID(tangramClass, "featureSelectionCb", "(Lcom/mapzen/tangram/Properties;)V");

    jclass propertiesClass = jniEnv->FindClass("com/mapzen/tangram/Properties");

    propertiesConstructorMID = jniEnv->GetMethodID(propertiesClass, "<init>", "(JZ)V");

    assetManager = AAssetManager_fromJava(jniEnv, _assetManager);

    if (assetManager == nullptr) {
        logMsg("ERROR: Could not obtain Asset Manager reference\n");
    }

}

void logMsg(const char* fmt, ...) {

    va_list args;
    va_start(args, fmt);
    __android_log_vprint(ANDROID_LOG_DEBUG, "Tangram", fmt, args);
    va_end(args);

}

class JniThreadBinding {
private:
    JavaVM* jvm;
    JNIEnv *jniEnv;
    int status;
public:
    JniThreadBinding(JavaVM* _jvm) : jvm(_jvm) {
        status = jvm->GetEnv((void**)&jniEnv, JNI_VERSION_1_6);
        if (status == JNI_EDETACHED) { jvm->AttachCurrentThread(&jniEnv, NULL);}
    }
    ~JniThreadBinding() {
        if (status == JNI_EDETACHED) { jvm->DetachCurrentThread(); }
    }

    JNIEnv* operator->() const {
        return jniEnv;
    }
};

void requestRender() {

    JniThreadBinding jniEnv(jvm);

    jniEnv->CallVoidMethod(tangramInstance, requestRenderMethodID);
}

std::string systemFontPath(const std::string& _family, const std::string& _weight, const std::string& _style) {

    JniThreadBinding jniEnv(jvm);

    jstring jfamily = jniEnv->NewStringUTF(_family.c_str());
    jstring jweight = jniEnv->NewStringUTF(_weight.c_str());
    jstring jstyle = jniEnv->NewStringUTF(_style.c_str());
    jstring returnStr = (jstring) jniEnv->CallObjectMethod(tangramInstance, getFontFilePath, jfamily, jweight, jstyle);

    size_t length = jniEnv->GetStringUTFLength(returnStr);
    std::string fontPath = std::string(length, 0);
    jniEnv->GetStringUTFRegion(returnStr, 0, length, &fontPath[0]);
    return fontPath;
}

void setContinuousRendering(bool _isContinuous) {

    s_isContinuousRendering = _isContinuous;

    JniThreadBinding jniEnv(jvm);

    jniEnv->CallVoidMethod(tangramInstance, requestRenderMethodID, _isContinuous ? 1 : 0);
}

bool isContinuousRendering() {

    return s_isContinuousRendering;

}

std::string setResourceRoot(const char* _path) {

    s_resourceRoot = std::string(dirname(_path));

    s_useInternalResources = (*_path != '/');

    // For unclear reasons, the AAssetManager will fail to open a file at
    // path "filepath" if the path is instead given as "./filepath", so in
    // cases where dirname returns "." we simply use an empty string. For
    // all other cases, we add a "/" for appending relative paths.
    if (!s_resourceRoot.empty() && s_resourceRoot.front() == '.') {
        s_resourceRoot = "";
    } else {
        s_resourceRoot += '/';
    }

    return std::string(basename(_path));

}

unsigned char* bytesFromAssetManager(const char* _path, unsigned int* _size) {

    unsigned char* data = nullptr;

    AAsset* asset = AAssetManager_open(assetManager, _path, AASSET_MODE_UNKNOWN);

    if (asset == nullptr) {
        logMsg("Failed to open asset at path: %s\n", _path);
        *_size = 0;
        return data;
    }

    *_size = AAsset_getLength(asset);

    data = (unsigned char*) malloc(sizeof(unsigned char) * (*_size));

    int read = AAsset_read(asset, data, *_size);

    if (read <= 0) {
        logMsg("Failed to read asset at path: %s\n", _path);
    }

    AAsset_close(asset);

    return data;
}

unsigned char* bytesFromFileSystem(const char* _path, unsigned int* _size) {

    std::ifstream resource(_path, std::ifstream::ate | std::ifstream::binary);

    if(!resource.is_open()) {
        logMsg("Failed to read file at path: %s\n", _path);
        *_size = 0;
        return nullptr;
    }

    *_size = resource.tellg();

    resource.seekg(std::ifstream::beg);

    char* cdata = (char*) malloc(sizeof(char) * (*_size));

    resource.read(cdata, *_size);
    resource.close();

    return reinterpret_cast<unsigned char *>(cdata);

}

std::string stringFromFile(const char* _path, PathType _type) {

    unsigned int length = 0;
    unsigned char* bytes = bytesFromFile(_path, _type, &length);
    std::string out(reinterpret_cast<char*>(bytes), length);
    free(bytes);

    return out;

}

unsigned char* bytesFromFile(const char* _path, PathType _type, unsigned int* _size) {

    std::string resourcePath = s_resourceRoot + _path;

    switch (_type) {
    case PathType::absolute:
        return bytesFromFileSystem(_path, _size);
    case PathType::internal:
        return bytesFromAssetManager(_path, _size);
    case PathType::resource:
        if (s_useInternalResources) {
            return bytesFromAssetManager(resourcePath.c_str(), _size);
        } else {
            return bytesFromFileSystem(resourcePath.c_str(), _size);
        }
    }

}

bool startUrlRequest(const std::string& _url, UrlCallback _callback) {

    jstring jUrl = jniRenderThreadEnv->NewStringUTF(_url.c_str());

    // This is probably super dangerous. In order to pass a reference to our callback we have to convert it
    // to a Java type. We allocate a new callback object and then reinterpret the pointer to it as a Java long.
    // In Java, we associate this long with the current network request and pass it back to native code when
    // the request completes (either in onUrlSuccess or onUrlFailure), reinterpret the long back into a
    // pointer, call the callback function if the request succeeded, and delete the heap-allocated UrlCallback
    // to make sure nothing is leaked.
    jlong jCallbackPtr = reinterpret_cast<jlong>(new UrlCallback(_callback));

    jboolean methodResult = jniRenderThreadEnv->CallBooleanMethod(tangramInstance, startUrlRequestMID, jUrl, jCallbackPtr);

    return methodResult;
}

void cancelUrlRequest(const std::string& _url) {

    jstring jUrl = jniRenderThreadEnv->NewStringUTF(_url.c_str());
    jniRenderThreadEnv->CallVoidMethod(tangramInstance, cancelUrlRequestMID, jUrl);

}

void onUrlSuccess(JNIEnv* _jniEnv, jbyteArray _jBytes, jlong _jCallbackPtr) {

    size_t length = _jniEnv->GetArrayLength(_jBytes);

    std::vector<char> content;
    content.resize(length);

    _jniEnv->GetByteArrayRegion(_jBytes, 0, length, reinterpret_cast<jbyte*>(content.data()));

    UrlCallback* callback = reinterpret_cast<UrlCallback*>(_jCallbackPtr);
    (*callback)(std::move(content));
    delete callback;

}

void onUrlFailure(JNIEnv* _jniEnv, jlong _jCallbackPtr) {

    UrlCallback* callback = reinterpret_cast<UrlCallback*>(_jCallbackPtr);
    delete callback;

}

void setCurrentThreadPriority(int priority) {
    int  tid = gettid();
    setpriority(PRIO_PROCESS, tid, priority);
}

void featureSelectionCallback(JNIEnv* jniEnv, const std::vector<Tangram::TouchItem>& items) {

    jlong jresult = 0;
    jclass propertiesClass = jniEnv->FindClass("com/mapzen/tangram/Properties");

    jresult = reinterpret_cast<jlong>(new std::shared_ptr<Tangram::Properties >(items[0].properties));
    jobject object = jniEnv->NewObject(propertiesClass, propertiesConstructorMID, jresult, true);

    jniEnv->CallVoidMethod(tangramInstance, featureSelectionCbMID, object);
}

void initGLExtensions() {
    void* libhandle = dlopen("libGLESv2.so", RTLD_LAZY);

    glBindVertexArrayOESEXT = (PFNGLBINDVERTEXARRAYOESPROC) dlsym(libhandle, "glBindVertexArrayOES");
    glDeleteVertexArraysOESEXT = (PFNGLDELETEVERTEXARRAYSOESPROC) dlsym(libhandle, "glDeleteVertexArraysOES");
    glGenVertexArraysOESEXT = (PFNGLGENVERTEXARRAYSOESPROC) dlsym(libhandle, "glGenVertexArraysOES");
}

#endif
<commit_msg>android: notify TileManager when tile failed to load<commit_after>#ifdef PLATFORM_ANDROID

#include "platform_android.h"
#include "tangram.h"

#include <GLES2/gl2platform.h>

#ifndef GL_GLEXT_PROTOTYPES
#define GL_GLEXT_PROTOTYPES 1
#endif

#include <GLES2/gl2.h>
#include <GLES2/gl2ext.h>
#include <dlfcn.h> // dlopen, dlsym

#include <android/log.h>
#include <android/asset_manager.h>
#include <android/asset_manager_jni.h>
#include <cstdarg>

#include <libgen.h>
#include <unistd.h>
#include <sys/resource.h>
#include <fstream>

/* Followed the following document for JavaVM tips when used with native threads
 * http://android.wooyd.org/JNIExample/#NWD1sCYeT-I
 * http://developer.android.com/training/articles/perf-jni.html and
 * http://www.ibm.com/developerworks/library/j-jni/
 * http://docs.oracle.com/javase/7/docs/technotes/guides/jni/spec/invocation.html
 */

static JavaVM* jvm;
// JNI Env bound on androids render thread (our native main thread)
static JNIEnv* jniRenderThreadEnv;
static jobject tangramInstance;
static jmethodID requestRenderMethodID;
static jmethodID setRenderModeMethodID;
static jmethodID startUrlRequestMID;
static jmethodID cancelUrlRequestMID;
static jmethodID getFontFilePath;
static jmethodID featureSelectionCbMID;

static jmethodID propertiesConstructorMID;

static AAssetManager* assetManager;

static bool s_isContinuousRendering = false;
static bool s_useInternalResources = true;
static std::string s_resourceRoot;

PFNGLBINDVERTEXARRAYOESPROC glBindVertexArrayOESEXT = 0;
PFNGLDELETEVERTEXARRAYSOESPROC glDeleteVertexArraysOESEXT = 0;
PFNGLGENVERTEXARRAYSOESPROC glGenVertexArraysOESEXT = 0;

void setupJniEnv(JNIEnv* _jniEnv, jobject _tangramInstance, jobject _assetManager) {
    _jniEnv->GetJavaVM(&jvm);
    JNIEnv* jniEnv = _jniEnv;
    jniRenderThreadEnv = _jniEnv;

    tangramInstance = jniEnv->NewGlobalRef(_tangramInstance);
    jclass tangramClass = jniEnv->FindClass("com/mapzen/tangram/MapController");
    startUrlRequestMID = jniEnv->GetMethodID(tangramClass, "startUrlRequest", "(Ljava/lang/String;J)Z");
    cancelUrlRequestMID = jniEnv->GetMethodID(tangramClass, "cancelUrlRequest", "(Ljava/lang/String;)V");
    getFontFilePath = jniEnv->GetMethodID(tangramClass, "getFontFilePath", "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;");
        requestRenderMethodID = _jniEnv->GetMethodID(tangramClass, "requestRender", "()V");
    setRenderModeMethodID = _jniEnv->GetMethodID(tangramClass, "setRenderMode", "(I)V");
    featureSelectionCbMID = _jniEnv->GetMethodID(tangramClass, "featureSelectionCb", "(Lcom/mapzen/tangram/Properties;)V");

    jclass propertiesClass = jniEnv->FindClass("com/mapzen/tangram/Properties");

    propertiesConstructorMID = jniEnv->GetMethodID(propertiesClass, "<init>", "(JZ)V");

    assetManager = AAssetManager_fromJava(jniEnv, _assetManager);

    if (assetManager == nullptr) {
        logMsg("ERROR: Could not obtain Asset Manager reference\n");
    }

}

void logMsg(const char* fmt, ...) {

    va_list args;
    va_start(args, fmt);
    __android_log_vprint(ANDROID_LOG_DEBUG, "Tangram", fmt, args);
    va_end(args);

}

class JniThreadBinding {
private:
    JavaVM* jvm;
    JNIEnv *jniEnv;
    int status;
public:
    JniThreadBinding(JavaVM* _jvm) : jvm(_jvm) {
        status = jvm->GetEnv((void**)&jniEnv, JNI_VERSION_1_6);
        if (status == JNI_EDETACHED) { jvm->AttachCurrentThread(&jniEnv, NULL);}
    }
    ~JniThreadBinding() {
        if (status == JNI_EDETACHED) { jvm->DetachCurrentThread(); }
    }

    JNIEnv* operator->() const {
        return jniEnv;
    }
};

void requestRender() {

    JniThreadBinding jniEnv(jvm);

    jniEnv->CallVoidMethod(tangramInstance, requestRenderMethodID);
}

std::string systemFontPath(const std::string& _family, const std::string& _weight, const std::string& _style) {

    JniThreadBinding jniEnv(jvm);

    jstring jfamily = jniEnv->NewStringUTF(_family.c_str());
    jstring jweight = jniEnv->NewStringUTF(_weight.c_str());
    jstring jstyle = jniEnv->NewStringUTF(_style.c_str());
    jstring returnStr = (jstring) jniEnv->CallObjectMethod(tangramInstance, getFontFilePath, jfamily, jweight, jstyle);

    size_t length = jniEnv->GetStringUTFLength(returnStr);
    std::string fontPath = std::string(length, 0);
    jniEnv->GetStringUTFRegion(returnStr, 0, length, &fontPath[0]);
    return fontPath;
}

void setContinuousRendering(bool _isContinuous) {

    s_isContinuousRendering = _isContinuous;

    JniThreadBinding jniEnv(jvm);

    jniEnv->CallVoidMethod(tangramInstance, requestRenderMethodID, _isContinuous ? 1 : 0);
}

bool isContinuousRendering() {

    return s_isContinuousRendering;

}

std::string setResourceRoot(const char* _path) {

    s_resourceRoot = std::string(dirname(_path));

    s_useInternalResources = (*_path != '/');

    // For unclear reasons, the AAssetManager will fail to open a file at
    // path "filepath" if the path is instead given as "./filepath", so in
    // cases where dirname returns "." we simply use an empty string. For
    // all other cases, we add a "/" for appending relative paths.
    if (!s_resourceRoot.empty() && s_resourceRoot.front() == '.') {
        s_resourceRoot = "";
    } else {
        s_resourceRoot += '/';
    }

    return std::string(basename(_path));

}

unsigned char* bytesFromAssetManager(const char* _path, unsigned int* _size) {

    unsigned char* data = nullptr;

    AAsset* asset = AAssetManager_open(assetManager, _path, AASSET_MODE_UNKNOWN);

    if (asset == nullptr) {
        logMsg("Failed to open asset at path: %s\n", _path);
        *_size = 0;
        return data;
    }

    *_size = AAsset_getLength(asset);

    data = (unsigned char*) malloc(sizeof(unsigned char) * (*_size));

    int read = AAsset_read(asset, data, *_size);

    if (read <= 0) {
        logMsg("Failed to read asset at path: %s\n", _path);
    }

    AAsset_close(asset);

    return data;
}

unsigned char* bytesFromFileSystem(const char* _path, unsigned int* _size) {

    std::ifstream resource(_path, std::ifstream::ate | std::ifstream::binary);

    if(!resource.is_open()) {
        logMsg("Failed to read file at path: %s\n", _path);
        *_size = 0;
        return nullptr;
    }

    *_size = resource.tellg();

    resource.seekg(std::ifstream::beg);

    char* cdata = (char*) malloc(sizeof(char) * (*_size));

    resource.read(cdata, *_size);
    resource.close();

    return reinterpret_cast<unsigned char *>(cdata);

}

std::string stringFromFile(const char* _path, PathType _type) {

    unsigned int length = 0;
    unsigned char* bytes = bytesFromFile(_path, _type, &length);
    std::string out(reinterpret_cast<char*>(bytes), length);
    free(bytes);

    return out;

}

unsigned char* bytesFromFile(const char* _path, PathType _type, unsigned int* _size) {

    std::string resourcePath = s_resourceRoot + _path;

    switch (_type) {
    case PathType::absolute:
        return bytesFromFileSystem(_path, _size);
    case PathType::internal:
        return bytesFromAssetManager(_path, _size);
    case PathType::resource:
        if (s_useInternalResources) {
            return bytesFromAssetManager(resourcePath.c_str(), _size);
        } else {
            return bytesFromFileSystem(resourcePath.c_str(), _size);
        }
    }

}

bool startUrlRequest(const std::string& _url, UrlCallback _callback) {

    jstring jUrl = jniRenderThreadEnv->NewStringUTF(_url.c_str());

    // This is probably super dangerous. In order to pass a reference to our callback we have to convert it
    // to a Java type. We allocate a new callback object and then reinterpret the pointer to it as a Java long.
    // In Java, we associate this long with the current network request and pass it back to native code when
    // the request completes (either in onUrlSuccess or onUrlFailure), reinterpret the long back into a
    // pointer, call the callback function if the request succeeded, and delete the heap-allocated UrlCallback
    // to make sure nothing is leaked.
    jlong jCallbackPtr = reinterpret_cast<jlong>(new UrlCallback(_callback));

    jboolean methodResult = jniRenderThreadEnv->CallBooleanMethod(tangramInstance, startUrlRequestMID, jUrl, jCallbackPtr);

    return methodResult;
}

void cancelUrlRequest(const std::string& _url) {
    jstring jUrl = jniRenderThreadEnv->NewStringUTF(_url.c_str());
    jniRenderThreadEnv->CallVoidMethod(tangramInstance, cancelUrlRequestMID, jUrl);
}

void onUrlSuccess(JNIEnv* _jniEnv, jbyteArray _jBytes, jlong _jCallbackPtr) {

    size_t length = _jniEnv->GetArrayLength(_jBytes);
    std::vector<char> content;
    content.resize(length);

    _jniEnv->GetByteArrayRegion(_jBytes, 0, length, reinterpret_cast<jbyte*>(content.data()));

    UrlCallback* callback = reinterpret_cast<UrlCallback*>(_jCallbackPtr);
    (*callback)(std::move(content));
    delete callback;
}

void onUrlFailure(JNIEnv* _jniEnv, jlong _jCallbackPtr) {
    std::vector<char> empty;

    UrlCallback* callback = reinterpret_cast<UrlCallback*>(_jCallbackPtr);
    (*callback)(std::move(empty));
    delete callback;
}

void setCurrentThreadPriority(int priority) {
    int  tid = gettid();
    setpriority(PRIO_PROCESS, tid, priority);
}

void featureSelectionCallback(JNIEnv* jniEnv, const std::vector<Tangram::TouchItem>& items) {

    jlong jresult = 0;
    jclass propertiesClass = jniEnv->FindClass("com/mapzen/tangram/Properties");

    jresult = reinterpret_cast<jlong>(new std::shared_ptr<Tangram::Properties >(items[0].properties));
    jobject object = jniEnv->NewObject(propertiesClass, propertiesConstructorMID, jresult, true);

    jniEnv->CallVoidMethod(tangramInstance, featureSelectionCbMID, object);
}

void initGLExtensions() {
    void* libhandle = dlopen("libGLESv2.so", RTLD_LAZY);

    glBindVertexArrayOESEXT = (PFNGLBINDVERTEXARRAYOESPROC) dlsym(libhandle, "glBindVertexArrayOES");
    glDeleteVertexArraysOESEXT = (PFNGLDELETEVERTEXARRAYSOESPROC) dlsym(libhandle, "glDeleteVertexArraysOES");
    glGenVertexArraysOESEXT = (PFNGLGENVERTEXARRAYSOESPROC) dlsym(libhandle, "glGenVertexArraysOES");
}

#endif
<|endoftext|>


<commit_before>/** global registry for CLI commands *****************************************
 *                                                                           *
 * Copyright (c) 2018 Florian Oetke                                          *
 *  This file is distributed under the MIT License                           *
 *  See LICENSE file for details.                                            *
\*****************************************************************************/

#pragma once

#include <mirrage/utils/func_traits.hpp>
#include <mirrage/utils/log.hpp>
#include <mirrage/utils/string_utils.hpp>

#include <functional>
#include <regex>
#include <string>
#include <string_view>
#include <unordered_map>
#include <vector>

namespace mirrage::util {

	/*
	 * auto commands = Console_command_container{};
	 * commands.add("show <window>", [&](auto& window) {
	 *	// stuff
	 * });
	 */

	class Console_command {
	  public:
		using Func = std::function<void(std::string_view)>;

		Console_command(std::string name, std::string api, Func exec)
		  : _id(gen_id()), _name(std::move(name)), _api(std::move(api)), _exec(std::move(exec))
		{
		}

		auto id() const noexcept { return _id; }
		auto name() const noexcept -> auto& { return _name; }
		auto api() const noexcept -> auto& { return _api; }
		void call(std::string_view args) const { _exec(args); }

	  private:
		static auto gen_id() -> std::int64_t
		{
			static auto next = std::int64_t(0);
			return next++;
		}

		std::int64_t _id;
		std::string  _name;
		std::string  _api;
		Func         _exec;
	};

	namespace detail {
		template <typename T>
		struct Default_var_setter {
			void operator()(T& org, T new_val) { org = new_val; }
		};
	} // namespace detail

	class Console_command_container {
	  public:
		~Console_command_container();

		template <typename F>
		auto add(std::string api, F &&) -> Console_command_container&;

		template <typename T, typename F = detail::Default_var_setter<T>>
		auto add_var(const std::string& name, T& var, F setter = {})
		{
			add_property(name,
			             [&var, setter = std::move(setter)](T new_val) mutable { setter(var, new_val); },
			             [&var, name = name]() -> decltype(auto) { return var; });
		}

		template <typename FS, typename FG>
		auto add_property(const std::string& name, FS&& setter, FG&& getter)
		{
			add("set." + name + " <value> | Sets the value of the property", std::forward<FS>(setter));
			add("get." + name + " | Gets the value of the property",
			    [name = name, getter = std::forward<FG>(getter)] {
				    LOG(plog::info) << "Value of " << name << ": " << util::to_string(getter());
			    });
		}

		/// returns false if no such command could be found
		static auto call(std::string_view cmd) -> bool;

		static auto complete(std::string_view input) -> std::vector<Console_command*>;

		static auto list_all_commands() -> const auto& { return all_commands(); }

	  private:
		static auto all_commands() -> std::unordered_multimap<std::string, Console_command>&
		{
			static std::unordered_multimap<std::string, Console_command> cmds;
			return cmds;
		}

		std::vector<std::int64_t> _command_ids;
	};

	namespace detail {
		template <typename T>
		struct Type_wrapper {
		};
	} // namespace detail

	template <typename F>
	auto Console_command_container::add(std::string api, F&& f) -> Console_command_container&
	{
		static const auto split_args_regex =
		        std::regex(R"xxx(\"((?:[^"]|\\")+(?!\\).)\"|([^ ]+))xxx", std::regex::optimize);

		auto name = api.substr(0, api.find(" "));
		auto c    = all_commands().emplace(
                std::piecewise_construct,
                std::forward_as_tuple(name),
                std::forward_as_tuple(name, api, [api, f = std::forward<F>(f)](std::string_view cmd) {
                    auto arg_iter = std::cregex_iterator(cmd.begin(), cmd.end(), split_args_regex);
                    auto arg_end  = std::cregex_iterator();

                    util::foreach_function_arg_call(
                            f, [&](auto type) -> util::maybe<typename decltype(type)::type> {
                                if(arg_iter == arg_end) {
                                    LOG(plog::error) << "Not enough arguments.";
                                    return util::nothing;

                                } else if((*arg_iter)[1].length() == 0) {
                                    return util::from_string<typename decltype(type)::type>(
                                            cmd.substr(std::size_t(arg_iter->position()),
                                                       std::size_t(arg_iter->length())));

                                } else { // quoted
                                    auto arg = std::string_view((*arg_iter)[1].first,
                                                                std::size_t((*arg_iter)[1].length()));
                                    if(!arg.find("\\"))
                                        return util::from_string<typename decltype(type)::type>(arg);
                                    else {
                                        // contains escape sequences we have to replace
                                        auto arg_str = std::string(arg);
                                        util::replace_inplace(arg_str, "\\\"", "\"");
                                        util::replace_inplace(arg_str, "\\\\", "\\");
                                        return util::from_string<typename decltype(type)::type>(arg_str);
                                    }
                                }
                            });
                }));

		_command_ids.emplace_back(c->second.id());
		return *this;
	}

} // namespace mirrage::util
<commit_msg>MSVC compatibility WIP.<commit_after>/** global registry for CLI commands *****************************************
 *                                                                           *
 * Copyright (c) 2018 Florian Oetke                                          *
 *  This file is distributed under the MIT License                           *
 *  See LICENSE file for details.                                            *
\*****************************************************************************/

#pragma once

#include <mirrage/utils/func_traits.hpp>
#include <mirrage/utils/log.hpp>
#include <mirrage/utils/string_utils.hpp>

#include <functional>
#include <regex>
#include <string>
#include <string_view>
#include <unordered_map>
#include <vector>

namespace mirrage::util {

	/*
	 * auto commands = Console_command_container{};
	 * commands.add("show <window>", [&](auto& window) {
	 *	// stuff
	 * });
	 */

	class Console_command {
	  public:
		using Func = std::function<void(std::string_view)>;

		Console_command(std::string name, std::string api, Func exec)
		  : _id(gen_id()), _name(std::move(name)), _api(std::move(api)), _exec(std::move(exec))
		{
		}

		auto id() const noexcept { return _id; }
		auto name() const noexcept -> auto& { return _name; }
		auto api() const noexcept -> auto& { return _api; }
		void call(std::string_view args) const { _exec(args); }

	  private:
		static auto gen_id() -> std::int64_t
		{
			static auto next = std::int64_t(0);
			return next++;
		}

		std::int64_t _id;
		std::string  _name;
		std::string  _api;
		Func         _exec;
	};

	namespace detail {
		template <typename T>
		struct Default_var_setter {
			void operator()(T& org, T new_val) { org = new_val; }
		};
	} // namespace detail

	class Console_command_container {
	  public:
		~Console_command_container();

		template <typename F>
		auto add(std::string api, F &&) -> Console_command_container&;

		template <typename T, typename F = detail::Default_var_setter<T>>
		auto add_var(const std::string& name, T& var, F setter = {})
		{
			add_property(name,
			             [&var, setter = std::move(setter)](T new_val) mutable { setter(var, new_val); },
			             [&var, name = name]() -> decltype(auto) { return var; });
		}

		template <typename FS, typename FG>
		auto add_property(const std::string& name, FS&& setter, FG&& getter)
		{
			add("set." + name + " <value> | Sets the value of the property", std::forward<FS>(setter));
			add("get." + name + " | Gets the value of the property",
			    [name = name, getter = std::forward<FG>(getter)] {
				    LOG(plog::info) << "Value of " << name << ": " << util::to_string(getter());
			    });
		}

		/// returns false if no such command could be found
		static auto call(std::string_view cmd) -> bool;

		static auto complete(std::string_view input) -> std::vector<Console_command*>;

		static auto list_all_commands() -> const auto& { return all_commands(); }

	  private:
		static auto all_commands() -> std::unordered_multimap<std::string, Console_command>&
		{
			static std::unordered_multimap<std::string, Console_command> cmds;
			return cmds;
		}

		std::vector<std::int64_t> _command_ids;
	};

	namespace detail {
		template <typename T>
		struct Type_wrapper {
		};
	} // namespace detail

	template <typename F>
	auto Console_command_container::add(std::string api, F&& f) -> Console_command_container&
	{
		static const auto split_args_regex =
		        std::regex(R"xxx(\"((?:[^"]|\\")+(?!\\).)\"|([^ ]+))xxx", std::regex::optimize);

		auto name = api.substr(0, api.find(" "));
		auto c    = all_commands().emplace(
                std::piecewise_construct,
                std::forward_as_tuple(name),
                std::forward_as_tuple(name, api, [api, f = std::forward<F>(f), this](std::string_view cmd) {
                    auto arg_iter = std::cregex_iterator(cmd.begin(), cmd.end(), split_args_regex);
                    auto arg_end  = std::cregex_iterator();

                    util::foreach_function_arg_call(
                            f, [&, this](auto type) -> util::maybe<typename decltype(type)::type> {
                                if(arg_iter == arg_end) {
                                    LOG(plog::error) << "Not enough arguments.";
                                    return util::nothing;

                                } else if((*arg_iter)[1].length() == 0) {
                                    return util::from_string<typename decltype(type)::type>(
                                            cmd.substr(std::size_t(arg_iter->position()),
                                                       std::size_t(arg_iter->length())));

                                } else { // quoted
                                    auto arg = std::string_view((*arg_iter)[1].first,
                                                                std::size_t((*arg_iter)[1].length()));
                                    if(!arg.find("\\"))
                                        return util::from_string<typename decltype(type)::type>(arg);
                                    else {
                                        // contains escape sequences we have to replace
                                        auto arg_str = std::string(arg);
                                        util::replace_inplace(arg_str, "\\\"", "\"");
                                        util::replace_inplace(arg_str, "\\\\", "\\");
                                        return util::from_string<typename decltype(type)::type>(arg_str);
                                    }
                                }
                            });
                }));

		_command_ids.emplace_back(c->second.id());
		return *this;
	}

} // namespace mirrage::util
<|endoftext|>