<commit_before>#include "gui/boardwidget.hpp"

#include <cmath>

#include <SFML/Graphics/Vertex.hpp>
#include <SFML/Graphics/VertexArray.hpp>
#include <SFML/Graphics/PrimitiveType.hpp>
#include <SFML/Graphics/RenderTarget.hpp>
#include <SFML/Graphics/Transform.hpp>

#include <SFML/Graphics/CircleShape.hpp>
#include <SFML/Graphics/Color.hpp>

#include <iostream>
#include <stdio.h>

#include "engine/engine.hpp"
#include "engine/player.hpp"
#include "engine/pathfinding/path.hpp"
#include "gui/guihandler.hpp"
#include "gui/cursor.hpp"
#include "gui/damagenumber.hpp"

namespace qrw
{
	BoardWidget::BoardWidget(GuiHandler* guihandler, Engine* engine, float width, float height)
	: Widget(guihandler->getRenderWindow(), width, height),
	  board(0),
	  engine(engine),
	  spritedimensions(0.0),
	  singlespritescale(0.0),
	  path(0),
	  deploywindow(guihandler->getDeployWindow())
	{
		printf("boardrenderer position: x=%f / y=%f\n", getGlobalBounds().left, getGlobalBounds().top);
		printf("boardrenderer size: w=%f / h=%f\n", getSize().x, getSize().y);

		// Create required sprites.
		TextureManager* texturemanager = TextureManager::getInstance();

		plainsquare = new sf::Sprite(*texturemanager->getTexture("plainsquare"));
		footstep = new sf::Sprite(*texturemanager->getTexture("footstep"));
		terrainsprites[ET_WOOD] = new sf::Sprite(*texturemanager->getTexture("wood"));
		terrainsprites[ET_HILL] = new sf::Sprite(*texturemanager->getTexture("hill"));
		terrainsprites[ET_WALL] = new sf::Sprite(*texturemanager->getTexture("wall"));
		p1unitsprites[EUT_SWORDMAN] = new sf::Sprite(*texturemanager->getTexture("p1swordman"));
		p1unitsprites[EUT_ARCHER] = new sf::Sprite(*texturemanager->getTexture("p1archer"));
		p1unitsprites[EUT_SPEARMAN] = new sf::Sprite(*texturemanager->getTexture("p1spearman"));
		p2unitsprites[EUT_SWORDMAN] = new sf::Sprite(*texturemanager->getTexture("p2swordman"));
		p2unitsprites[EUT_ARCHER] = new sf::Sprite(*texturemanager->getTexture("p2archer"));
		p2unitsprites[EUT_SPEARMAN] = new sf::Sprite(*texturemanager->getTexture("p2spearman"));

		signalmousemoved.connect(std::bind(&BoardWidget::updateCursor, this));
		signalmouseentered.connect(std::bind(&BoardWidget::updateCursor, this));
		signalclicked.connect(std::bind(&BoardWidget::leftClicked, this));
		signalrightclicked.connect(std::bind(&BoardWidget::rightClicked, this));
		signalkeypressed.connect(std::bind(&BoardWidget::keyPressed, this, std::placeholders::_1));
	}

	BoardWidget::~BoardWidget()
	{
		delete plainsquare;
		delete footstep;
	}

	void BoardWidget::setBoard(Board* board)
	{
		this->board = board;
		calcSpriteDimensions(board->getWidth(), board->getHeight());
	}

	void BoardWidget::draw(sf::RenderTarget &target, sf::RenderStates states) const
	{
		sf::Vector2f targetsize = target.getView().getSize();
		targetsize.x -= 180.0;
		int width = board->getWidth();
		int height = board->getHeight();

		sf::Vector2f spritescale(singlespritescale, singlespritescale);
		sf::VertexArray vertices(sf::Lines, 0);
		Square *square = 0;
		Unit *unit = 0;
		Terrain *terrain = 0;
		sf::Vector2f currpos;

		sf::Vector2i cursorpos = Cursor::getCursor()->getPosition();
		sf::Vector2i childcursorpos(-1, -1);

		if(Cursor::getCursor()->getChild() != 0)
		{
			childcursorpos.x = Cursor::getCursor()->getChild()->getPosition().x;
			childcursorpos.y = Cursor::getCursor()->getChild()->getPosition().y;
		}

		for(int i = 0; i < width; ++i)
		{
			for(int j = 0; j < height; ++j)
			{
				currpos.x = i * spritedimensions;
				currpos.y = j * spritedimensions;

				plainsquare->setPosition(currpos);
				plainsquare->setScale(spritescale);
				target.draw(*plainsquare);

				square = board->getSquare(i, j);
				terrain = square->getTerrain();
				unit = square->getUnit();
				// Render cursor
				if(cursorpos.x == i && cursorpos.y == j)
					Cursor::getCursor()->draw(target, currpos, spritedimensions);
				if(childcursorpos.x == i && childcursorpos.y == j)
					Cursor::getCursor()->drawChild(target, currpos, spritedimensions);

				// Render Terrain
				if(terrain != 0)
				{
					drawTerrain(target, terrain->getType(), currpos, spritescale);
				}
				// Render Units
				if(unit != 0)
				{
					drawUnit(target, unit->getPlayer()->getId(), unit->getType(),
						currpos, spritescale);
				}
			}
		}

		drawPath(target, spritescale);
	}

	void BoardWidget::calcSpriteDimensions(int boardwidth, int boardheight)
	{
		if(boardwidth > boardheight)
		{
			spritedimensions = getSize().x / boardwidth;
			singlespritescale = getSize().x / (boardwidth * 32.0);
		}
		else
		{
			spritedimensions = getSize().y / boardheight;
			singlespritescale = getSize().y / (boardheight * 32.0);
		}
	}

	void BoardWidget::drawTerrain(sf::RenderTarget& target,
		TERRAINTYPES terraintype, sf::Vector2f position, sf::Vector2f scale) const
	{
		terrainsprites[terraintype]->setPosition(position);
		terrainsprites[terraintype]->setScale(scale);
		target.draw(*terrainsprites[terraintype]);
	}

	void BoardWidget::drawUnit(sf::RenderTarget& target, int playerid,
		UNITTYPES unittype, sf::Vector2f position, sf::Vector2f scale) const
	{
		sf::Sprite* unitsprite = 0;
		if(playerid == 0)
			unitsprite = p1unitsprites[unittype];
		else
			unitsprite = p2unitsprites[unittype];
		unitsprite->setPosition(position);
		unitsprite->setScale(scale);
		target.draw(*unitsprite);
	}

	void BoardWidget::drawPath(sf::RenderTarget& target, sf::Vector2f scale) const
	{
		if(!path)
			return;

		int pathlength = path->getLength();

		Square* previous = 0;
		Square* current  = path->getStep(0);
		Square* next     = path->getStep(1);

		// Do not render first step.
		for(int i = 1; i < pathlength; ++i)
		{
			previous = current;
			current  = next;

			// Reset the previously applied transformations.
			footstep->setOrigin(16, 16);
			footstep->setScale(scale);
			footstep->setRotation(0);

			// Transformations relative to the previous step
			Coordinates prevdelta(previous->getCoordinates() - current->getCoordinates());
			if(prevdelta.getX() != 0)
				footstep->rotate(-90 * prevdelta.getX());
			if(prevdelta.getY() != 0)
				footstep->scale(1, prevdelta.getY());

			// Transformations relative to the next step (if possible)
			if(i < pathlength - 1)
			{
				next = path->getStep(i+1);
			}

			footstep->setPosition(
				spritedimensions * (0.5f + current->getXPosition()),
				spritedimensions * (0.5f + current->getYPosition())
			);

			target.draw(*footstep);
		}
	}

	void BoardWidget::moveUnitIngame()
	{
		Cursor* cursor = Cursor::getCursor();
		Cursor* childcursor = cursor->getChild();

		if(childcursor)
		{
			Unit* unit1 = board->getSquare(cursor->getPosition().x, cursor->getPosition().y)->getUnit();
			int unit1hp = unit1->getHP();
			Unit* unit2 = board->getSquare(childcursor->getPosition().x, childcursor->getPosition().y)->getUnit();
			int unit2hp;

			if(unit2)
				unit2hp = unit2->getHP();

			int moveresult = engine->moveUnitIngame(cursor->getPosition().x, cursor->getPosition().y,
				childcursor->getPosition().x, childcursor->getPosition().y);
			printf("moveresult: %i\n", moveresult);
			if(moveresult == 0 || moveresult == -9 || moveresult == -8 || moveresult == -11)
			{
				// Display damage numbers
				if(moveresult == -9 || moveresult == -8 || moveresult == -11)
				{
					DamageNumber* dm;
					sf::Vector2f pos;

					// First DMG number
					pos.x = cursor->getPosition().x * spritedimensions;
					pos.y = cursor->getPosition().y * spritedimensions + spritedimensions * 0.2;
					dm = new DamageNumber(unit1->getHP() - unit1hp, pos);
					// Second DMG number
					pos.x = childcursor->getPosition().x * spritedimensions;
					pos.y = childcursor->getPosition().y * spritedimensions + spritedimensions * 0.2;
					dm = new DamageNumber(unit2->getHP() - unit2hp, pos);
				}

				// Resetting cursors
				cursor->setPosition(childcursor->getPosition());
				cursor->despawnChild();
				// Reset path
				if(path)
				{
					delete path;
					path = 0;
				}
			}
		}
	}

	void BoardWidget::updateCursor()
	{
		// Calculate on which field the mouse cursor is placed.
		sf::Vector2i mousePosition = sf::Mouse::getPosition(*window);
		sf::Vector2i newCursorPos;

		newCursorPos.x = floor( ((float)mousePosition.x) / spritedimensions );
		newCursorPos.y = floor( ((float)mousePosition.y) / spritedimensions );

		// Boarder checks
		if(newCursorPos.x < 0)
			newCursorPos.x = 0;
		else if(newCursorPos.x > board->getWidth())
			newCursorPos.x = board->getWidth();

		if(newCursorPos.y < 0)
			newCursorPos.y = 0;
		else if(newCursorPos.y > board->getHeight())
			newCursorPos.y = board->getHeight();

		if(!Cursor::getCursor()->getChild())
			Cursor::getCursor()->setPosition(newCursorPos);
		else
		{
			Cursor::getCursor()->getChild()->setPosition(newCursorPos);

			if(engine->getStatus() == EES_RUNNING)
			{
				// Update path
				if(path)
					delete path;
				path = engine->findPath(
					Coordinates(Cursor::getCursor()->getPosition().x, Cursor::getCursor()->getPosition().y),
					Coordinates(newCursorPos.x, newCursorPos.y)
				);
			}
		}
	}

	void BoardWidget::leftClicked()
	{
		Cursor* cursor = Cursor::getCursor();
		Cursor* childcursor = cursor->getChild();
		Square* cursorsquare = board->getSquare(cursor->getPosition().x, cursor->getPosition().y);

		// Depploy unit / terrain by calling deploywindow methods.
		if(engine->getStatus() == EES_PREPARE)
		{
			if(childcursor)
			{
				deploywindow->moveUnit();
			}
			else if(cursorsquare->getUnit())
			{
				cursor->spawnChild();
			}
			else
			{
				printf("boardwidget leftclicked pre placeEntity (deploywindow=%p)\n", deploywindow);
				deploywindow->placeEntity();
			}
		}
		// Ingame actions.
		// TODO: Following code is just a copy&paste of GuiHandler::HandleEvent() code. Find a better solution
		// for this code duplication...
		else
		{
			if(cursorsquare->getUnit() && !childcursor)
			{
				cursor->spawnChild();
			}
			// Move a unit
			else if(childcursor)
			{
				this->moveUnitIngame();
			}
		}
	} // BoardWidget::leftClicked();

	void BoardWidget::rightClicked()
	{
		Cursor* cursor = Cursor::getCursor();
		Cursor* childcursor = cursor->getChild();
		if(childcursor)
		{
			cursor->setPosition(childcursor->getPosition());
			cursor->despawnChild();
		}
		if(path)
		{
			delete path;
			path = 0;
		}
	}

	void BoardWidget::keyPressed(const sf::Event& event)
	{
		qrw::Cursor* cursor = qrw::Cursor::getCursor();
		qrw::Cursor* childcursor = cursor->getChild();

		if(event.key.code == sf::Keyboard::Up)
			cursor->move(0, -1);
		else if(event.key.code == sf::Keyboard::Down)
			cursor->move(0, 1);
		else if(event.key.code == sf::Keyboard::Right)
			cursor->move(1, 0);
		else if(event.key.code == sf::Keyboard::Left)
			cursor->move(-1, 0);
		else if(event.key.code == sf::Keyboard::Escape)
			cursor->despawnChild();
		else if(event.key.code == sf::Keyboard::Return)
		{
			if(engine->getStatus() == EES_PREPARE)
			{
				// Check if a new unit is placed or a unit is moved.
				// A unit is moved if cursor has a child (to point to destination)
				// or there is a unit under cursor.
				Square* cursorsquare = engine->getBoard()->getSquare(cursor->getPosition().x,
						cursor->getPosition().y);
				if (cursor->getChild() != NULL)
				{
					deploywindow->moveUnit();
				}
				else if(cursorsquare->getUnit() != NULL)
				{
					cursor->spawnChild();
				}
				else
				{
					deploywindow->placeEntity();
				}
			}
			else if(childcursor == 0)
			{
				cursor->spawnChild();
			}
			else if(childcursor != 0)
			{
				this->moveUnitIngame();
			}
		}
	}
}
<commit_msg>Added 45° rotations of footsteps.<commit_after>#include "gui/boardwidget.hpp"

#include <cmath>

#include <SFML/Graphics/Vertex.hpp>
#include <SFML/Graphics/VertexArray.hpp>
#include <SFML/Graphics/PrimitiveType.hpp>
#include <SFML/Graphics/RenderTarget.hpp>
#include <SFML/Graphics/Transform.hpp>

#include <SFML/Graphics/CircleShape.hpp>
#include <SFML/Graphics/Color.hpp>

#include <iostream>
#include <stdio.h>

#include "engine/engine.hpp"
#include "engine/player.hpp"
#include "engine/pathfinding/path.hpp"
#include "gui/guihandler.hpp"
#include "gui/cursor.hpp"
#include "gui/damagenumber.hpp"

namespace qrw
{
	BoardWidget::BoardWidget(GuiHandler* guihandler, Engine* engine, float width, float height)
	: Widget(guihandler->getRenderWindow(), width, height),
	  board(0),
	  engine(engine),
	  spritedimensions(0.0),
	  singlespritescale(0.0),
	  path(0),
	  deploywindow(guihandler->getDeployWindow())
	{
		printf("boardrenderer position: x=%f / y=%f\n", getGlobalBounds().left, getGlobalBounds().top);
		printf("boardrenderer size: w=%f / h=%f\n", getSize().x, getSize().y);

		// Create required sprites.
		TextureManager* texturemanager = TextureManager::getInstance();

		plainsquare = new sf::Sprite(*texturemanager->getTexture("plainsquare"));
		footstep = new sf::Sprite(*texturemanager->getTexture("footstep"));
		terrainsprites[ET_WOOD] = new sf::Sprite(*texturemanager->getTexture("wood"));
		terrainsprites[ET_HILL] = new sf::Sprite(*texturemanager->getTexture("hill"));
		terrainsprites[ET_WALL] = new sf::Sprite(*texturemanager->getTexture("wall"));
		p1unitsprites[EUT_SWORDMAN] = new sf::Sprite(*texturemanager->getTexture("p1swordman"));
		p1unitsprites[EUT_ARCHER] = new sf::Sprite(*texturemanager->getTexture("p1archer"));
		p1unitsprites[EUT_SPEARMAN] = new sf::Sprite(*texturemanager->getTexture("p1spearman"));
		p2unitsprites[EUT_SWORDMAN] = new sf::Sprite(*texturemanager->getTexture("p2swordman"));
		p2unitsprites[EUT_ARCHER] = new sf::Sprite(*texturemanager->getTexture("p2archer"));
		p2unitsprites[EUT_SPEARMAN] = new sf::Sprite(*texturemanager->getTexture("p2spearman"));

		signalmousemoved.connect(std::bind(&BoardWidget::updateCursor, this));
		signalmouseentered.connect(std::bind(&BoardWidget::updateCursor, this));
		signalclicked.connect(std::bind(&BoardWidget::leftClicked, this));
		signalrightclicked.connect(std::bind(&BoardWidget::rightClicked, this));
		signalkeypressed.connect(std::bind(&BoardWidget::keyPressed, this, std::placeholders::_1));
	}

	BoardWidget::~BoardWidget()
	{
		delete plainsquare;
		delete footstep;
	}

	void BoardWidget::setBoard(Board* board)
	{
		this->board = board;
		calcSpriteDimensions(board->getWidth(), board->getHeight());
	}

	void BoardWidget::draw(sf::RenderTarget &target, sf::RenderStates states) const
	{
		sf::Vector2f targetsize = target.getView().getSize();
		targetsize.x -= 180.0;
		int width = board->getWidth();
		int height = board->getHeight();

		sf::Vector2f spritescale(singlespritescale, singlespritescale);
		sf::VertexArray vertices(sf::Lines, 0);
		Square *square = 0;
		Unit *unit = 0;
		Terrain *terrain = 0;
		sf::Vector2f currpos;

		sf::Vector2i cursorpos = Cursor::getCursor()->getPosition();
		sf::Vector2i childcursorpos(-1, -1);

		if(Cursor::getCursor()->getChild() != 0)
		{
			childcursorpos.x = Cursor::getCursor()->getChild()->getPosition().x;
			childcursorpos.y = Cursor::getCursor()->getChild()->getPosition().y;
		}

		for(int i = 0; i < width; ++i)
		{
			for(int j = 0; j < height; ++j)
			{
				currpos.x = i * spritedimensions;
				currpos.y = j * spritedimensions;

				plainsquare->setPosition(currpos);
				plainsquare->setScale(spritescale);
				target.draw(*plainsquare);

				square = board->getSquare(i, j);
				terrain = square->getTerrain();
				unit = square->getUnit();
				// Render cursor
				if(cursorpos.x == i && cursorpos.y == j)
					Cursor::getCursor()->draw(target, currpos, spritedimensions);
				if(childcursorpos.x == i && childcursorpos.y == j)
					Cursor::getCursor()->drawChild(target, currpos, spritedimensions);

				// Render Terrain
				if(terrain != 0)
				{
					drawTerrain(target, terrain->getType(), currpos, spritescale);
				}
				// Render Units
				if(unit != 0)
				{
					drawUnit(target, unit->getPlayer()->getId(), unit->getType(),
						currpos, spritescale);
				}
			}
		}

		drawPath(target, spritescale);
	}

	void BoardWidget::calcSpriteDimensions(int boardwidth, int boardheight)
	{
		if(boardwidth > boardheight)
		{
			spritedimensions = getSize().x / boardwidth;
			singlespritescale = getSize().x / (boardwidth * 32.0);
		}
		else
		{
			spritedimensions = getSize().y / boardheight;
			singlespritescale = getSize().y / (boardheight * 32.0);
		}
	}

	void BoardWidget::drawTerrain(sf::RenderTarget& target,
		TERRAINTYPES terraintype, sf::Vector2f position, sf::Vector2f scale) const
	{
		terrainsprites[terraintype]->setPosition(position);
		terrainsprites[terraintype]->setScale(scale);
		target.draw(*terrainsprites[terraintype]);
	}

	void BoardWidget::drawUnit(sf::RenderTarget& target, int playerid,
		UNITTYPES unittype, sf::Vector2f position, sf::Vector2f scale) const
	{
		sf::Sprite* unitsprite = 0;
		if(playerid == 0)
			unitsprite = p1unitsprites[unittype];
		else
			unitsprite = p2unitsprites[unittype];
		unitsprite->setPosition(position);
		unitsprite->setScale(scale);
		target.draw(*unitsprite);
	}

	void BoardWidget::drawPath(sf::RenderTarget& target, sf::Vector2f scale) const
	{
		if(!path)
			return;

		int pathlength = path->getLength();

		Square* previous = 0;
		Square* current  = path->getStep(0);
		Square* next     = path->getStep(1);

		// Do not render first step.
		for(int i = 1; i < pathlength; ++i)
		{
			previous = current;
			current  = next;

			// Reset the previously applied transformations.
			footstep->setOrigin(16, 16);
			footstep->setScale(scale);
			footstep->setRotation(0);

			// Transformations relative to the previous step
			Coordinates prevdelta(previous->getCoordinates() - current->getCoordinates());
			if(prevdelta.getX() != 0)
				footstep->rotate(-90 * prevdelta.getX());
			if(prevdelta.getY() != 0)
				footstep->scale(1, prevdelta.getY());

			// Transformations relative to the next step (if possible)
			if(i < pathlength - 1)
			{
				next = path->getStep(i+1);

				Coordinates prevnextdelta(previous->getCoordinates() - next->getCoordinates());

				// If the path has a corner at this position
				if(prevnextdelta.getX() != 0 && prevnextdelta.getY() != 0)
				{
					int rotationdirection = 0;
					// horizontal
					if(prevdelta.getX() == 0)
					{
						rotationdirection = -1;
					}
					// vertical
					else if(prevdelta.getY() == 0)
					{
						rotationdirection = +1;
					}
					footstep->rotate(rotationdirection * 45 * (prevnextdelta.getX() * prevnextdelta.getY()));
				}
			}

			footstep->setPosition(
				spritedimensions * (0.5f + current->getXPosition()),
				spritedimensions * (0.5f + current->getYPosition())
			);

			target.draw(*footstep);
		}
	}

	void BoardWidget::moveUnitIngame()
	{
		Cursor* cursor = Cursor::getCursor();
		Cursor* childcursor = cursor->getChild();

		if(childcursor)
		{
			Unit* unit1 = board->getSquare(cursor->getPosition().x, cursor->getPosition().y)->getUnit();
			int unit1hp = unit1->getHP();
			Unit* unit2 = board->getSquare(childcursor->getPosition().x, childcursor->getPosition().y)->getUnit();
			int unit2hp;

			if(unit2)
				unit2hp = unit2->getHP();

			int moveresult = engine->moveUnitIngame(cursor->getPosition().x, cursor->getPosition().y,
				childcursor->getPosition().x, childcursor->getPosition().y);
			printf("moveresult: %i\n", moveresult);
			if(moveresult == 0 || moveresult == -9 || moveresult == -8 || moveresult == -11)
			{
				// Display damage numbers
				if(moveresult == -9 || moveresult == -8 || moveresult == -11)
				{
					DamageNumber* dm;
					sf::Vector2f pos;

					// First DMG number
					pos.x = cursor->getPosition().x * spritedimensions;
					pos.y = cursor->getPosition().y * spritedimensions + spritedimensions * 0.2;
					dm = new DamageNumber(unit1->getHP() - unit1hp, pos);
					// Second DMG number
					pos.x = childcursor->getPosition().x * spritedimensions;
					pos.y = childcursor->getPosition().y * spritedimensions + spritedimensions * 0.2;
					dm = new DamageNumber(unit2->getHP() - unit2hp, pos);
				}

				// Resetting cursors
				cursor->setPosition(childcursor->getPosition());
				cursor->despawnChild();
				// Reset path
				if(path)
				{
					delete path;
					path = 0;
				}
			}
		}
	}

	void BoardWidget::updateCursor()
	{
		// Calculate on which field the mouse cursor is placed.
		sf::Vector2i mousePosition = sf::Mouse::getPosition(*window);
		sf::Vector2i newCursorPos;

		newCursorPos.x = floor( ((float)mousePosition.x) / spritedimensions );
		newCursorPos.y = floor( ((float)mousePosition.y) / spritedimensions );

		// Boarder checks
		if(newCursorPos.x < 0)
			newCursorPos.x = 0;
		else if(newCursorPos.x > board->getWidth())
			newCursorPos.x = board->getWidth();

		if(newCursorPos.y < 0)
			newCursorPos.y = 0;
		else if(newCursorPos.y > board->getHeight())
			newCursorPos.y = board->getHeight();

		if(!Cursor::getCursor()->getChild())
			Cursor::getCursor()->setPosition(newCursorPos);
		else
		{
			Cursor::getCursor()->getChild()->setPosition(newCursorPos);

			if(engine->getStatus() == EES_RUNNING)
			{
				// Update path
				if(path)
					delete path;
				path = engine->findPath(
					Coordinates(Cursor::getCursor()->getPosition().x, Cursor::getCursor()->getPosition().y),
					Coordinates(newCursorPos.x, newCursorPos.y)
				);
			}
		}
	}

	void BoardWidget::leftClicked()
	{
		Cursor* cursor = Cursor::getCursor();
		Cursor* childcursor = cursor->getChild();
		Square* cursorsquare = board->getSquare(cursor->getPosition().x, cursor->getPosition().y);

		// Depploy unit / terrain by calling deploywindow methods.
		if(engine->getStatus() == EES_PREPARE)
		{
			if(childcursor)
			{
				deploywindow->moveUnit();
			}
			else if(cursorsquare->getUnit())
			{
				cursor->spawnChild();
			}
			else
			{
				printf("boardwidget leftclicked pre placeEntity (deploywindow=%p)\n", deploywindow);
				deploywindow->placeEntity();
			}
		}
		// Ingame actions.
		// TODO: Following code is just a copy&paste of GuiHandler::HandleEvent() code. Find a better solution
		// for this code duplication...
		else
		{
			if(cursorsquare->getUnit() && !childcursor)
			{
				cursor->spawnChild();
			}
			// Move a unit
			else if(childcursor)
			{
				this->moveUnitIngame();
			}
		}
	} // BoardWidget::leftClicked();

	void BoardWidget::rightClicked()
	{
		Cursor* cursor = Cursor::getCursor();
		Cursor* childcursor = cursor->getChild();
		if(childcursor)
		{
			cursor->setPosition(childcursor->getPosition());
			cursor->despawnChild();
		}
		if(path)
		{
			delete path;
			path = 0;
		}
	}

	void BoardWidget::keyPressed(const sf::Event& event)
	{
		qrw::Cursor* cursor = qrw::Cursor::getCursor();
		qrw::Cursor* childcursor = cursor->getChild();

		if(event.key.code == sf::Keyboard::Up)
			cursor->move(0, -1);
		else if(event.key.code == sf::Keyboard::Down)
			cursor->move(0, 1);
		else if(event.key.code == sf::Keyboard::Right)
			cursor->move(1, 0);
		else if(event.key.code == sf::Keyboard::Left)
			cursor->move(-1, 0);
		else if(event.key.code == sf::Keyboard::Escape)
			cursor->despawnChild();
		else if(event.key.code == sf::Keyboard::Return)
		{
			if(engine->getStatus() == EES_PREPARE)
			{
				// Check if a new unit is placed or a unit is moved.
				// A unit is moved if cursor has a child (to point to destination)
				// or there is a unit under cursor.
				Square* cursorsquare = engine->getBoard()->getSquare(cursor->getPosition().x,
						cursor->getPosition().y);
				if (cursor->getChild() != NULL)
				{
					deploywindow->moveUnit();
				}
				else if(cursorsquare->getUnit() != NULL)
				{
					cursor->spawnChild();
				}
				else
				{
					deploywindow->placeEntity();
				}
			}
			else if(childcursor == 0)
			{
				cursor->spawnChild();
			}
			else if(childcursor != 0)
			{
				this->moveUnitIngame();
			}
		}
	}
}
<|endoftext|>


<commit_before>#include "gusanos/allegro.h"

#include "gconsole.h"
#include "resource_list.h"
#include "sprite.h"

#include "level.h"
#include "gusgame.h"
#include "part_type.h"
#include "particle.h"
#include "CWormHuman.h"
#include "util/macros.h"

#ifndef DEDICATED_ONLY
#include "CViewport.h"
#include "font.h"
#include "gfx.h"
#include "sound/sfx.h"
#include "menu.h"
#include "distortion.h"
#endif
#include "sprite_set.h"
#include "network.h"
#include "CMap.h"

#include "script.h"
#include "glua.h"
#include "luaapi/context.h"
#include "util/log.h"
#include "game/Game.h"
#include <memory>
#include <string>
#include <vector>

#ifdef POSIX
#include <unistd.h>
#endif

using namespace std;

bool quit = false;

static Uint32 logicLast = 0;

bool gusInitBase() {
	if(!gusGame.init())
		return false;

#ifndef DEDICATED_ONLY
	OmfgGUI::menu.clear();
#endif
	//gusGame.loadMod();
	gusGame.reloadModWithoutMap();
	//gusGame.runInitScripts();

	// TODO: check bDedicated instead
#ifndef DEDICATED_ONLY
	console.executeConfig("autoexec.cfg");
#else
	console.executeConfig("autoexec-ded.cfg");
#endif

	return true;
}

bool gusInit(const std::string& mod) {
	notes << "Gusanos: set mod " << mod << endl;

	gusGame.setMod(mod);

	quit = false;
	logicLast = SDL_GetTicks() / 10;

	return true;
}

bool gusCanRunFrame() {
	return !quit || !network.isDisconnected();
}

void gusLogicFrame() {
	Uint32 timer = SDL_GetTicks() / 10;

	size_t logicFrameCount = 0;
	while ( logicLast + 1 <= timer )
	{

		for ( Grid::iterator iter = game.objects.beginAll(); iter;)
		{
			if(iter->deleteMe)
				iter.erase();
			else
				++iter;
		}

		if ( game.shouldDoPhysicsFrame() && gusGame.isLoaded() && gusGame.isLevelLoaded() )
		{

			for ( Grid::iterator iter = game.objects.beginAll(); iter; ++iter)
			{
				iter->think();
				game.objects.relocateIfNecessary(iter);
			}

			game.objects.flush(); // Insert all new objects

			for ( vector<CWormInputHandler*>::iterator iter = game.players.begin(); iter != game.players.end(); iter++)
			{
				if(!(*iter)->deleteMe)
					(*iter)->think();
			}
		}

		gusGame.think();

#ifndef DEDICATED_ONLY
		sfx.think(); // WARNING: THIS MUST! BE PLACED BEFORE THE OBJECT DELETE LOOP
#endif

#ifndef DEDICATED_ONLY
		console.checkInput();
#endif
		console.think();

		spriteList.think();

		EACH_CALLBACK(i, afterUpdate)
		{
			(lua.call(*i))();
		}

		++logicLast;
		++logicFrameCount;

		if(logicFrameCount > 10) { // don't be too slow
			logicLast = timer; // skip left frames
			break;
		}
	}

	network.update();
}

void gusQuit() {
	//network.disconnect(); // If we haven't already, it's too late
	network.shutDown();
	gusGame.unload();
#ifndef DEDICATED_ONLY
	OmfgGUI::menu.destroy();
#endif
	console.shutDown();
#ifndef DEDICATED_ONLY
	sfx.shutDown();
#endif
	gfx.shutDown();
	lua.close();

	allegro_exit();
}

<commit_msg>cleanup<commit_after>#include "gusanos/allegro.h"

#include "gconsole.h"
#include "resource_list.h"
#include "sprite.h"

#include "level.h"
#include "gusgame.h"
#include "part_type.h"
#include "particle.h"
#include "CWormHuman.h"
#include "util/macros.h"

#ifndef DEDICATED_ONLY
#include "CViewport.h"
#include "font.h"
#include "gfx.h"
#include "sound/sfx.h"
#include "menu.h"
#include "distortion.h"
#endif
#include "sprite_set.h"
#include "network.h"
#include "CMap.h"

#include "script.h"
#include "glua.h"
#include "luaapi/context.h"
#include "util/log.h"
#include "game/Game.h"
#include <memory>
#include <string>
#include <vector>

#ifdef POSIX
#include <unistd.h>
#endif

using namespace std;

bool quit = false;

static Uint32 logicLast = 0;

bool gusInitBase() {
	if(!gusGame.init())
		return false;

#ifndef DEDICATED_ONLY
	OmfgGUI::menu.clear();
#endif
	gusGame.reloadModWithoutMap();

	if(!bDedicated)
		console.executeConfig("autoexec.cfg");
	else
		console.executeConfig("autoexec-ded.cfg");

	return true;
}

bool gusInit(const std::string& mod) {
	notes << "Gusanos: set mod " << mod << endl;

	gusGame.setMod(mod);

	quit = false;
	logicLast = SDL_GetTicks() / 10;

	return true;
}

bool gusCanRunFrame() {
	return !quit || !network.isDisconnected();
}

void gusLogicFrame() {
	Uint32 timer = SDL_GetTicks() / 10;

	size_t logicFrameCount = 0;
	while ( logicLast + 1 <= timer )
	{

		for ( Grid::iterator iter = game.objects.beginAll(); iter;)
		{
			if(iter->deleteMe)
				iter.erase();
			else
				++iter;
		}

		if ( game.shouldDoPhysicsFrame() && gusGame.isLoaded() && gusGame.isLevelLoaded() )
		{

			for ( Grid::iterator iter = game.objects.beginAll(); iter; ++iter)
			{
				iter->think();
				game.objects.relocateIfNecessary(iter);
			}

			game.objects.flush(); // Insert all new objects

			for ( vector<CWormInputHandler*>::iterator iter = game.players.begin(); iter != game.players.end(); iter++)
			{
				if(!(*iter)->deleteMe)
					(*iter)->think();
			}
		}

		gusGame.think();

#ifndef DEDICATED_ONLY
		sfx.think(); // WARNING: THIS MUST! BE PLACED BEFORE THE OBJECT DELETE LOOP
#endif

#ifndef DEDICATED_ONLY
		console.checkInput();
#endif
		console.think();

		spriteList.think();

		EACH_CALLBACK(i, afterUpdate)
		{
			(lua.call(*i))();
		}

		++logicLast;
		++logicFrameCount;

		if(logicFrameCount > 10) { // don't be too slow
			logicLast = timer; // skip left frames
			break;
		}
	}

	network.update();
}

void gusQuit() {
	//network.disconnect(); // If we haven't already, it's too late
	network.shutDown();
	gusGame.unload();
#ifndef DEDICATED_ONLY
	OmfgGUI::menu.destroy();
#endif
	console.shutDown();
#ifndef DEDICATED_ONLY
	sfx.shutDown();
#endif
	gfx.shutDown();
	lua.close();

	allegro_exit();
}

<|endoftext|>


<commit_before>/*

Copyright (c) 2007, Arvid Norberg
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:

    * Redistributions of source code must retain the above copyright
      notice, this list of conditions and the following disclaimer.
    * Redistributions in binary form must reproduce the above copyright
      notice, this list of conditions and the following disclaimer in
      the documentation and/or other materials provided with the distribution.
    * Neither the name of the author nor the names of its
      contributors may be used to endorse or promote products derived
      from this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.

*/

#include "libtorrent/http_connection.hpp"

#include <boost/bind.hpp>
#include <boost/lexical_cast.hpp>
#include <asio/ip/tcp.hpp>
#include <string>

using boost::bind;

namespace libtorrent
{

void http_connection::get(std::string const& url, time_duration timeout
	, bool handle_redirect)
{
	m_redirect = handle_redirect;
	std::string protocol;
	std::string hostname;
	std::string path;
	int port;
	boost::tie(protocol, hostname, port, path) = parse_url_components(url);
	std::stringstream headers;
	headers << "GET " << path << " HTTP/1.0\r\n"
		"Host:" << hostname <<
		"Connection: close\r\n"
		"\r\n\r\n";
	sendbuffer = headers.str();
	start(hostname, boost::lexical_cast<std::string>(port), timeout);
}

void http_connection::start(std::string const& hostname, std::string const& port
	, time_duration timeout, bool handle_redirect)
{
	m_redirect = handle_redirect;
	m_timeout = timeout;
	m_timer.expires_from_now(m_timeout);
	m_timer.async_wait(bind(&http_connection::on_timeout
		, boost::weak_ptr<http_connection>(shared_from_this()), _1));
	m_called = false;
	if (m_sock.is_open() && m_hostname == hostname && m_port == port)
	{
		m_parser.reset();
		asio::async_write(m_sock, asio::buffer(sendbuffer)
			, bind(&http_connection::on_write, shared_from_this(), _1));
	}
	else
	{
		m_sock.close();
		tcp::resolver::query query(hostname, port);
		m_resolver.async_resolve(query, bind(&http_connection::on_resolve
			, shared_from_this(), _1, _2));
		m_hostname = hostname;
		m_port = port;
	}
}

void http_connection::on_timeout(boost::weak_ptr<http_connection> p
	, asio::error_code const& e)
{
	if (e == asio::error::operation_aborted) return;
	boost::shared_ptr<http_connection> c = p.lock();
	if (!c) return;
	if (c->m_bottled && c->m_called) return;

	if (c->m_last_receive + c->m_timeout < time_now())
	{
		c->m_called = true;
		c->m_handler(asio::error::timed_out, c->m_parser, 0, 0);
		return;
	}

	c->m_timer.expires_at(c->m_last_receive + c->m_timeout);
	c->m_timer.async_wait(bind(&http_connection::on_timeout, p, _1));
}

void http_connection::close()
{
	m_timer.cancel();
	m_limiter_timer.cancel();
	m_sock.close();
	m_hostname.clear();
	m_port.clear();
}

void http_connection::on_resolve(asio::error_code const& e
		, tcp::resolver::iterator i)
{
	if (e)
	{
		close();
		if (m_bottled && m_called) return;
		m_called = true;
		m_handler(e, m_parser, 0, 0);
		return;
	}
	assert(i != tcp::resolver::iterator());
	m_sock.async_connect(*i, boost::bind(&http_connection::on_connect
		, shared_from_this(), _1/*, ++i*/));
}

void http_connection::on_connect(asio::error_code const& e
	/*, tcp::resolver::iterator i*/)
{
	if (!e)
	{
		m_last_receive = time_now();
		asio::async_write(m_sock, asio::buffer(sendbuffer)
			, bind(&http_connection::on_write, shared_from_this(), _1));
	}
/*	else if (i != tcp::resolver::iterator())
	{
		// The connection failed. Try the next endpoint in the list.
		m_sock.close();
		m_sock.async_connect(*i, bind(&http_connection::on_connect
			, shared_from_this(), _1, ++i));
	}
*/	else
	{
		close();
		if (m_bottled && m_called) return;
		m_called = true;
		m_handler(e, m_parser, 0, 0);
	}
}

void http_connection::on_write(asio::error_code const& e)
{
	if (e)
	{
		close();
		if (m_bottled && m_called) return;
		m_called = true;
		m_handler(e, m_parser, 0, 0);
		return;
	}

	std::string().swap(sendbuffer);
	m_recvbuffer.resize(4096);

	int amount_to_read = m_recvbuffer.size() - m_read_pos;
	if (m_rate_limit > 0 && amount_to_read > m_download_quota)
	{
		amount_to_read = m_download_quota;
		if (m_download_quota == 0)
		{
			if (!m_limiter_timer_active)
				on_assign_bandwidth(asio::error_code());
			return;
		}
	}
	m_sock.async_read_some(asio::buffer(&m_recvbuffer[0] + m_read_pos
		, amount_to_read)
		, bind(&http_connection::on_read
		, shared_from_this(), _1, _2));
}

void http_connection::on_read(asio::error_code const& e
	, std::size_t bytes_transferred)
{
	if (m_rate_limit)
	{
		m_download_quota -= bytes_transferred;
		assert(m_download_quota >= 0);
	}

	if (e == asio::error::eof)
	{
		close();
		if (m_bottled && m_called) return;
		m_called = true;
		m_handler(asio::error_code(), m_parser, 0, 0);
		return;
	}

	if (e)
	{
		close();
		if (m_bottled && m_called) return;
		m_called = true;
		m_handler(e, m_parser, 0, 0);
		return;
	}

	m_read_pos += bytes_transferred;
	assert(m_read_pos <= int(m_recvbuffer.size()));

	// having a nonempty path means we should handle redirects
	if (m_redirect && m_parser.header_finished())
	{
		int code = m_parser.status_code();
		if (code >= 300 && code < 400)
		{
			// attempt a redirect
			std::string url = m_parser.header<std::string>("location");
			if (url.empty())
			{
				// missing location header
				if (m_bottled && m_called) return;
				m_called = true;
				m_handler(e, m_parser, 0, 0);
				return;
			}

			m_limiter_timer_active = false;
			m_timer.cancel();
			m_limiter_timer.cancel();
			m_sock.close();
			m_hostname.clear();
			m_port.clear();
			get(url, m_timeout);
			return;
		}

		m_redirect = false;
	}

	if (m_bottled || !m_parser.header_finished())
	{
		libtorrent::buffer::const_interval rcv_buf(&m_recvbuffer[0]
			, &m_recvbuffer[0] + m_read_pos);
		m_parser.incoming(rcv_buf);
		if (!m_bottled && m_parser.header_finished())
		{
			if (m_read_pos > m_parser.body_start())
				m_handler(e, m_parser, &m_recvbuffer[0] + m_parser.body_start()
					, m_read_pos - m_parser.body_start());
			m_read_pos = 0;
			m_last_receive = time_now();
		}
		else if (m_bottled && m_parser.finished())
		{
			m_timer.cancel();
			if (m_bottled && m_called) return;
			m_called = true;
			m_handler(e, m_parser, 0, 0);
		}
	}
	else
	{
		assert(!m_bottled);
		m_handler(e, m_parser, &m_recvbuffer[0], m_read_pos);
		m_read_pos = 0;
		m_last_receive = time_now();
	}

	if (int(m_recvbuffer.size()) == m_read_pos)
		m_recvbuffer.resize((std::min)(m_read_pos + 2048, 1024*500));
	if (m_read_pos == 1024 * 500)
	{
		close();
		if (m_bottled && m_called) return;
		m_called = true;
		m_handler(asio::error::eof, m_parser, 0, 0);
		return;
	}
	int amount_to_read = m_recvbuffer.size() - m_read_pos;
	if (m_rate_limit > 0 && amount_to_read > m_download_quota)
	{
		amount_to_read = m_download_quota;
		if (m_download_quota == 0)
		{
			if (!m_limiter_timer_active)
				on_assign_bandwidth(asio::error_code());
			return;
		}
	}
	m_sock.async_read_some(asio::buffer(&m_recvbuffer[0] + m_read_pos
		, amount_to_read)
		, bind(&http_connection::on_read
		, shared_from_this(), _1, _2));
}

void http_connection::on_assign_bandwidth(asio::error_code const& e)
{
	if ((e == asio::error::operation_aborted
		&& m_limiter_timer_active)
		|| !m_sock.is_open())
	{
		if (!m_bottled || !m_called)
			m_handler(e, m_parser, 0, 0);
		return;
	}
	m_limiter_timer_active = false;
	if (e) return;

	if (m_download_quota > 0) return;

	m_download_quota = m_rate_limit / 4;

	int amount_to_read = m_recvbuffer.size() - m_read_pos;
	if (amount_to_read > m_download_quota)
		amount_to_read = m_download_quota;

	m_sock.async_read_some(asio::buffer(&m_recvbuffer[0] + m_read_pos
		, amount_to_read)
		, bind(&http_connection::on_read
		, shared_from_this(), _1, _2));

	m_limiter_timer_active = true;
	m_limiter_timer.expires_from_now(milliseconds(250));
	m_limiter_timer.async_wait(bind(&http_connection::on_assign_bandwidth
		, shared_from_this(), _1));
}

void http_connection::rate_limit(int limit)
{
	if (!m_limiter_timer_active)
	{
		m_limiter_timer_active = true;
		m_limiter_timer.expires_from_now(milliseconds(250));
		m_limiter_timer.async_wait(bind(&http_connection::on_assign_bandwidth
			, shared_from_this(), _1));
	}
	m_rate_limit = limit;
}

}

<commit_msg>fixes for the GET request and passes that data through the data pointer in bottled mode too<commit_after>/*

Copyright (c) 2007, Arvid Norberg
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:

    * Redistributions of source code must retain the above copyright
      notice, this list of conditions and the following disclaimer.
    * Redistributions in binary form must reproduce the above copyright
      notice, this list of conditions and the following disclaimer in
      the documentation and/or other materials provided with the distribution.
    * Neither the name of the author nor the names of its
      contributors may be used to endorse or promote products derived
      from this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.

*/

#include "libtorrent/http_connection.hpp"

#include <boost/bind.hpp>
#include <boost/lexical_cast.hpp>
#include <asio/ip/tcp.hpp>
#include <string>

using boost::bind;

namespace libtorrent
{

void http_connection::get(std::string const& url, time_duration timeout
	, bool handle_redirect)
{
	m_redirect = handle_redirect;
	std::string protocol;
	std::string hostname;
	std::string path;
	int port;
	boost::tie(protocol, hostname, port, path) = parse_url_components(url);
	std::stringstream headers;
	headers << "GET " << path << " HTTP/1.0\r\n"
		"Host:" << hostname <<
		"\r\nConnection: close\r\n"
		"\r\n";
	sendbuffer = headers.str();
	start(hostname, boost::lexical_cast<std::string>(port), timeout);
}

void http_connection::start(std::string const& hostname, std::string const& port
	, time_duration timeout, bool handle_redirect)
{
	m_redirect = handle_redirect;
	m_timeout = timeout;
	m_timer.expires_from_now(m_timeout);
	m_timer.async_wait(bind(&http_connection::on_timeout
		, boost::weak_ptr<http_connection>(shared_from_this()), _1));
	m_called = false;
	if (m_sock.is_open() && m_hostname == hostname && m_port == port)
	{
		m_parser.reset();
		asio::async_write(m_sock, asio::buffer(sendbuffer)
			, bind(&http_connection::on_write, shared_from_this(), _1));
	}
	else
	{
		m_sock.close();
		tcp::resolver::query query(hostname, port);
		m_resolver.async_resolve(query, bind(&http_connection::on_resolve
			, shared_from_this(), _1, _2));
		m_hostname = hostname;
		m_port = port;
	}
}

void http_connection::on_timeout(boost::weak_ptr<http_connection> p
	, asio::error_code const& e)
{
	if (e == asio::error::operation_aborted) return;
	boost::shared_ptr<http_connection> c = p.lock();
	if (!c) return;
	if (c->m_bottled && c->m_called) return;

	if (c->m_last_receive + c->m_timeout < time_now())
	{
		c->m_called = true;
		c->m_handler(asio::error::timed_out, c->m_parser, 0, 0);
		return;
	}

	c->m_timer.expires_at(c->m_last_receive + c->m_timeout);
	c->m_timer.async_wait(bind(&http_connection::on_timeout, p, _1));
}

void http_connection::close()
{
	m_timer.cancel();
	m_limiter_timer.cancel();
	m_sock.close();
	m_hostname.clear();
	m_port.clear();
}

void http_connection::on_resolve(asio::error_code const& e
		, tcp::resolver::iterator i)
{
	if (e)
	{
		close();
		if (m_bottled && m_called) return;
		m_called = true;
		m_handler(e, m_parser, 0, 0);
		return;
	}
	assert(i != tcp::resolver::iterator());
	m_sock.async_connect(*i, boost::bind(&http_connection::on_connect
		, shared_from_this(), _1/*, ++i*/));
}

void http_connection::on_connect(asio::error_code const& e
	/*, tcp::resolver::iterator i*/)
{
	if (!e)
	{
		m_last_receive = time_now();
		asio::async_write(m_sock, asio::buffer(sendbuffer)
			, bind(&http_connection::on_write, shared_from_this(), _1));
	}
/*	else if (i != tcp::resolver::iterator())
	{
		// The connection failed. Try the next endpoint in the list.
		m_sock.close();
		m_sock.async_connect(*i, bind(&http_connection::on_connect
			, shared_from_this(), _1, ++i));
	}
*/	else
	{
		close();
		if (m_bottled && m_called) return;
		m_called = true;
		m_handler(e, m_parser, 0, 0);
	}
}

void http_connection::on_write(asio::error_code const& e)
{
	if (e)
	{
		close();
		if (m_bottled && m_called) return;
		m_called = true;
		m_handler(e, m_parser, 0, 0);
		return;
	}

	std::string().swap(sendbuffer);
	m_recvbuffer.resize(4096);

	int amount_to_read = m_recvbuffer.size() - m_read_pos;
	if (m_rate_limit > 0 && amount_to_read > m_download_quota)
	{
		amount_to_read = m_download_quota;
		if (m_download_quota == 0)
		{
			if (!m_limiter_timer_active)
				on_assign_bandwidth(asio::error_code());
			return;
		}
	}
	m_sock.async_read_some(asio::buffer(&m_recvbuffer[0] + m_read_pos
		, amount_to_read)
		, bind(&http_connection::on_read
		, shared_from_this(), _1, _2));
}

void http_connection::on_read(asio::error_code const& e
	, std::size_t bytes_transferred)
{
	if (m_rate_limit)
	{
		m_download_quota -= bytes_transferred;
		assert(m_download_quota >= 0);
	}

	if (e == asio::error::eof)
	{
		close();
		if (m_bottled && m_called) return;
		m_called = true;
		char const* data = 0;
		std::size_t size = 0;
		if (m_bottled)
		{
			data = m_parser.get_body().begin;
			size = m_parser.get_body().left();
		}
		m_handler(asio::error_code(), m_parser, data, size);
		return;
	}

	if (e)
	{
		close();
		if (m_bottled && m_called) return;
		m_called = true;
		m_handler(e, m_parser, 0, 0);
		return;
	}

	m_read_pos += bytes_transferred;
	assert(m_read_pos <= int(m_recvbuffer.size()));

	// having a nonempty path means we should handle redirects
	if (m_redirect && m_parser.header_finished())
	{
		int code = m_parser.status_code();
		if (code >= 300 && code < 400)
		{
			// attempt a redirect
			std::string url = m_parser.header<std::string>("location");
			if (url.empty())
			{
				// missing location header
				if (m_bottled && m_called) return;
				m_called = true;
				m_handler(e, m_parser, 0, 0);
				return;
			}

			m_limiter_timer_active = false;
			m_timer.cancel();
			m_limiter_timer.cancel();
			m_sock.close();
			m_hostname.clear();
			m_port.clear();
			get(url, m_timeout);
			return;
		}

		m_redirect = false;
	}

	if (m_bottled || !m_parser.header_finished())
	{
		libtorrent::buffer::const_interval rcv_buf(&m_recvbuffer[0]
			, &m_recvbuffer[0] + m_read_pos);
		m_parser.incoming(rcv_buf);
		if (!m_bottled && m_parser.header_finished())
		{
			if (m_read_pos > m_parser.body_start())
				m_handler(e, m_parser, &m_recvbuffer[0] + m_parser.body_start()
					, m_read_pos - m_parser.body_start());
			m_read_pos = 0;
			m_last_receive = time_now();
		}
		else if (m_bottled && m_parser.finished())
		{
			m_timer.cancel();
			if (m_bottled && m_called) return;
			m_called = true;
			m_handler(e, m_parser, m_parser.get_body().begin, m_parser.get_body().left());
		}
	}
	else
	{
		assert(!m_bottled);
		m_handler(e, m_parser, &m_recvbuffer[0], m_read_pos);
		m_read_pos = 0;
		m_last_receive = time_now();
	}

	if (int(m_recvbuffer.size()) == m_read_pos)
		m_recvbuffer.resize((std::min)(m_read_pos + 2048, 1024*500));
	if (m_read_pos == 1024 * 500)
	{
		close();
		if (m_bottled && m_called) return;
		m_called = true;
		m_handler(asio::error::eof, m_parser, 0, 0);
		return;
	}
	int amount_to_read = m_recvbuffer.size() - m_read_pos;
	if (m_rate_limit > 0 && amount_to_read > m_download_quota)
	{
		amount_to_read = m_download_quota;
		if (m_download_quota == 0)
		{
			if (!m_limiter_timer_active)
				on_assign_bandwidth(asio::error_code());
			return;
		}
	}
	m_sock.async_read_some(asio::buffer(&m_recvbuffer[0] + m_read_pos
		, amount_to_read)
		, bind(&http_connection::on_read
		, shared_from_this(), _1, _2));
}

void http_connection::on_assign_bandwidth(asio::error_code const& e)
{
	if ((e == asio::error::operation_aborted
		&& m_limiter_timer_active)
		|| !m_sock.is_open())
	{
		if (!m_bottled || !m_called)
			m_handler(e, m_parser, 0, 0);
		return;
	}
	m_limiter_timer_active = false;
	if (e) return;

	if (m_download_quota > 0) return;

	m_download_quota = m_rate_limit / 4;

	int amount_to_read = m_recvbuffer.size() - m_read_pos;
	if (amount_to_read > m_download_quota)
		amount_to_read = m_download_quota;

	m_sock.async_read_some(asio::buffer(&m_recvbuffer[0] + m_read_pos
		, amount_to_read)
		, bind(&http_connection::on_read
		, shared_from_this(), _1, _2));

	m_limiter_timer_active = true;
	m_limiter_timer.expires_from_now(milliseconds(250));
	m_limiter_timer.async_wait(bind(&http_connection::on_assign_bandwidth
		, shared_from_this(), _1));
}

void http_connection::rate_limit(int limit)
{
	if (!m_limiter_timer_active)
	{
		m_limiter_timer_active = true;
		m_limiter_timer.expires_from_now(milliseconds(250));
		m_limiter_timer.async_wait(bind(&http_connection::on_assign_bandwidth
			, shared_from_this(), _1));
	}
	m_rate_limit = limit;
}

}

<|endoftext|>


<commit_before>/*

Copyright (c) 2007, Arvid Norberg
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:

    * Redistributions of source code must retain the above copyright
      notice, this list of conditions and the following disclaimer.
    * Redistributions in binary form must reproduce the above copyright
      notice, this list of conditions and the following disclaimer in
      the documentation and/or other materials provided with the distribution.
    * Neither the name of the author nor the names of its
      contributors may be used to endorse or promote products derived
      from this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.

*/

#include "libtorrent/http_connection.hpp"

#include <boost/bind.hpp>
#include <boost/lexical_cast.hpp>
#include <asio/ip/tcp.hpp>
#include <string>

using boost::bind;

namespace libtorrent
{

void http_connection::get(std::string const& url, time_duration timeout
	, bool handle_redirect)
{
	m_redirect = handle_redirect;
	std::string protocol;
	std::string hostname;
	std::string path;
	int port;
	boost::tie(protocol, hostname, port, path) = parse_url_components(url);
	std::stringstream headers;
	headers << "GET " << path << " HTTP/1.0\r\n"
		"Host:" << hostname <<
		"Connection: close\r\n"
		"\r\n\r\n";
	m_path = path;
	sendbuffer = headers.str();
	start(hostname, boost::lexical_cast<std::string>(port), timeout);
}

void http_connection::start(std::string const& hostname, std::string const& port
	, time_duration timeout, bool handle_redirect = true)
{
	m_redirect = handle_redirect;
	m_timeout = timeout;
	m_timer.expires_from_now(m_timeout);
	m_timer.async_wait(bind(&http_connection::on_timeout
		, boost::weak_ptr<http_connection>(shared_from_this()), _1));
	m_called = false;
	if (m_sock.is_open() && m_hostname == hostname && m_port == port)
	{
		m_parser.reset();
		asio::async_write(m_sock, asio::buffer(sendbuffer)
			, bind(&http_connection::on_write, shared_from_this(), _1));
	}
	else
	{
		m_sock.close();
		tcp::resolver::query query(hostname, port);
		m_resolver.async_resolve(query, bind(&http_connection::on_resolve
			, shared_from_this(), _1, _2));
		m_hostname = hostname;
		m_port = port;
	}
}

void http_connection::on_timeout(boost::weak_ptr<http_connection> p
	, asio::error_code const& e)
{
	if (e == asio::error::operation_aborted) return;
	boost::shared_ptr<http_connection> c = p.lock();
	if (!c) return;
	if (c->m_bottled && c->m_called) return;

	if (c->m_last_receive + c->m_timeout < time_now())
	{
		c->m_called = true;
		c->m_handler(asio::error::timed_out, c->m_parser, 0, 0);
		return;
	}

	c->m_timer.expires_at(c->m_last_receive + c->m_timeout);
	c->m_timer.async_wait(bind(&http_connection::on_timeout, p, _1));
}

void http_connection::close()
{
	m_timer.cancel();
	m_limiter_timer.cancel();
	m_sock.close();
	m_hostname.clear();
	m_port.clear();
}

void http_connection::on_resolve(asio::error_code const& e
		, tcp::resolver::iterator i)
{
	if (e)
	{
		close();
		if (m_bottled && m_called) return;
		m_called = true;
		m_handler(e, m_parser, 0, 0);
		return;
	}
	assert(i != tcp::resolver::iterator());
	m_sock.async_connect(*i, boost::bind(&http_connection::on_connect
		, shared_from_this(), _1/*, ++i*/));
}

void http_connection::on_connect(asio::error_code const& e
	/*, tcp::resolver::iterator i*/)
{
	if (!e)
	{
		m_last_receive = time_now();
		asio::async_write(m_sock, asio::buffer(sendbuffer)
			, bind(&http_connection::on_write, shared_from_this(), _1));
	}
/*	else if (i != tcp::resolver::iterator())
	{
		// The connection failed. Try the next endpoint in the list.
		m_sock.close();
		m_sock.async_connect(*i, bind(&http_connection::on_connect
			, shared_from_this(), _1, ++i));
	}
*/	else
	{
		close();
		if (m_bottled && m_called) return;
		m_called = true;
		m_handler(e, m_parser, 0, 0);
	}
}

void http_connection::on_write(asio::error_code const& e)
{
	if (e)
	{
		close();
		if (m_bottled && m_called) return;
		m_called = true;
		m_handler(e, m_parser, 0, 0);
		return;
	}

	std::string().swap(sendbuffer);
	m_recvbuffer.resize(4096);

	int amount_to_read = m_recvbuffer.size() - m_read_pos;
	if (m_rate_limit > 0 && amount_to_read > m_download_quota)
	{
		amount_to_read = m_download_quota;
		if (m_download_quota == 0)
		{
			if (!m_limiter_timer_active)
				on_assign_bandwidth(asio::error_code());
			return;
		}
	}
	m_sock.async_read_some(asio::buffer(&m_recvbuffer[0] + m_read_pos
		, amount_to_read)
		, bind(&http_connection::on_read
		, shared_from_this(), _1, _2));
}

void http_connection::on_read(asio::error_code const& e
	, std::size_t bytes_transferred)
{
	if (m_rate_limit)
	{
		m_download_quota -= bytes_transferred;
		assert(m_download_quota >= 0);
	}

	if (e == asio::error::eof)
	{
		close();
		if (m_bottled && m_called) return;
		m_called = true;
		m_handler(asio::error_code(), m_parser, 0, 0);
		return;
	}

	if (e)
	{
		close();
		if (m_bottled && m_called) return;
		m_called = true;
		m_handler(e, m_parser, 0, 0);
		return;
	}

	m_read_pos += bytes_transferred;
	assert(m_read_pos <= int(m_recvbuffer.size()));

	// having a nonempty path means we should handle redirects
	if (m_redirect && m_parser.header_finished())
	{
		int code = m_parser.status_code();
		if (code >= 300 && code < 400)
		{
			// attempt a redirect
			std::string url = m_parser.header<std::string>("location");
			if (url.empty())
			{
				// missing location header
				if (m_bottled && m_called) return;
				m_called = true;
				m_handler(e, m_parser, 0, 0);
				return;
			}

			m_limiter_timer_active = false;
			m_timer.cancel();
			m_limiter_timer.cancel();
			m_sock.close();
			m_hostname.clear();
			m_port.clear();
			get(url, m_timeout);
			return;
		}

		m_redirect = false;
	}

	if (m_bottled || !m_parser.header_finished())
	{
		libtorrent::buffer::const_interval rcv_buf(&m_recvbuffer[0]
			, &m_recvbuffer[0] + m_read_pos);
		m_parser.incoming(rcv_buf);
		if (!m_bottled && m_parser.header_finished())
		{
			if (m_read_pos > m_parser.body_start())
				m_handler(e, m_parser, &m_recvbuffer[0] + m_parser.body_start()
					, m_read_pos - m_parser.body_start());
			m_read_pos = 0;
			m_last_receive = time_now();
		}
		else if (m_bottled && m_parser.finished())
		{
			m_timer.cancel();
			if (m_bottled && m_called) return;
			m_called = true;
			m_handler(e, m_parser, 0, 0);
		}
	}
	else
	{
		assert(!m_bottled);
		m_handler(e, m_parser, &m_recvbuffer[0], m_read_pos);
		m_read_pos = 0;
		m_last_receive = time_now();
	}

	if (int(m_recvbuffer.size()) == m_read_pos)
		m_recvbuffer.resize((std::min)(m_read_pos + 2048, 1024*500));
	if (m_read_pos == 1024 * 500)
	{
		close();
		if (m_bottled && m_called) return;
		m_called = true;
		m_handler(asio::error::eof, m_parser, 0, 0);
		return;
	}
	int amount_to_read = m_recvbuffer.size() - m_read_pos;
	if (m_rate_limit > 0 && amount_to_read > m_download_quota)
	{
		amount_to_read = m_download_quota;
		if (m_download_quota == 0)
		{
			if (!m_limiter_timer_active)
				on_assign_bandwidth(asio::error_code());
			return;
		}
	}
	m_sock.async_read_some(asio::buffer(&m_recvbuffer[0] + m_read_pos
		, amount_to_read)
		, bind(&http_connection::on_read
		, shared_from_this(), _1, _2));
}

void http_connection::on_assign_bandwidth(asio::error_code const& e)
{
	if ((e == asio::error::operation_aborted
		&& m_limiter_timer_active)
		|| !m_sock.is_open())
	{
		if (!m_bottled || !m_called)
			m_handler(e, m_parser, 0, 0);
		return;
	}
	m_limiter_timer_active = false;
	if (e) return;

	if (m_download_quota > 0) return;

	m_download_quota = m_rate_limit / 4;

	int amount_to_read = m_recvbuffer.size() - m_read_pos;
	if (amount_to_read > m_download_quota)
		amount_to_read = m_download_quota;

	m_sock.async_read_some(asio::buffer(&m_recvbuffer[0] + m_read_pos
		, amount_to_read)
		, bind(&http_connection::on_read
		, shared_from_this(), _1, _2));

	m_limiter_timer_active = true;
	m_limiter_timer.expires_from_now(milliseconds(250));
	m_limiter_timer.async_wait(bind(&http_connection::on_assign_bandwidth
		, shared_from_this(), _1));
}

void http_connection::rate_limit(int limit)
{
	if (!m_limiter_timer_active)
	{
		m_limiter_timer_active = true;
		m_limiter_timer.expires_from_now(milliseconds(250));
		m_limiter_timer.async_wait(bind(&http_connection::on_assign_bandwidth
			, shared_from_this(), _1));
	}
	m_rate_limit = limit;
}

}

<commit_msg>fix to previous check-in<commit_after>/*

Copyright (c) 2007, Arvid Norberg
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:

    * Redistributions of source code must retain the above copyright
      notice, this list of conditions and the following disclaimer.
    * Redistributions in binary form must reproduce the above copyright
      notice, this list of conditions and the following disclaimer in
      the documentation and/or other materials provided with the distribution.
    * Neither the name of the author nor the names of its
      contributors may be used to endorse or promote products derived
      from this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.

*/

#include "libtorrent/http_connection.hpp"

#include <boost/bind.hpp>
#include <boost/lexical_cast.hpp>
#include <asio/ip/tcp.hpp>
#include <string>

using boost::bind;

namespace libtorrent
{

void http_connection::get(std::string const& url, time_duration timeout
	, bool handle_redirect)
{
	m_redirect = handle_redirect;
	std::string protocol;
	std::string hostname;
	std::string path;
	int port;
	boost::tie(protocol, hostname, port, path) = parse_url_components(url);
	std::stringstream headers;
	headers << "GET " << path << " HTTP/1.0\r\n"
		"Host:" << hostname <<
		"Connection: close\r\n"
		"\r\n\r\n";
	sendbuffer = headers.str();
	start(hostname, boost::lexical_cast<std::string>(port), timeout);
}

void http_connection::start(std::string const& hostname, std::string const& port
	, time_duration timeout, bool handle_redirect)
{
	m_redirect = handle_redirect;
	m_timeout = timeout;
	m_timer.expires_from_now(m_timeout);
	m_timer.async_wait(bind(&http_connection::on_timeout
		, boost::weak_ptr<http_connection>(shared_from_this()), _1));
	m_called = false;
	if (m_sock.is_open() && m_hostname == hostname && m_port == port)
	{
		m_parser.reset();
		asio::async_write(m_sock, asio::buffer(sendbuffer)
			, bind(&http_connection::on_write, shared_from_this(), _1));
	}
	else
	{
		m_sock.close();
		tcp::resolver::query query(hostname, port);
		m_resolver.async_resolve(query, bind(&http_connection::on_resolve
			, shared_from_this(), _1, _2));
		m_hostname = hostname;
		m_port = port;
	}
}

void http_connection::on_timeout(boost::weak_ptr<http_connection> p
	, asio::error_code const& e)
{
	if (e == asio::error::operation_aborted) return;
	boost::shared_ptr<http_connection> c = p.lock();
	if (!c) return;
	if (c->m_bottled && c->m_called) return;

	if (c->m_last_receive + c->m_timeout < time_now())
	{
		c->m_called = true;
		c->m_handler(asio::error::timed_out, c->m_parser, 0, 0);
		return;
	}

	c->m_timer.expires_at(c->m_last_receive + c->m_timeout);
	c->m_timer.async_wait(bind(&http_connection::on_timeout, p, _1));
}

void http_connection::close()
{
	m_timer.cancel();
	m_limiter_timer.cancel();
	m_sock.close();
	m_hostname.clear();
	m_port.clear();
}

void http_connection::on_resolve(asio::error_code const& e
		, tcp::resolver::iterator i)
{
	if (e)
	{
		close();
		if (m_bottled && m_called) return;
		m_called = true;
		m_handler(e, m_parser, 0, 0);
		return;
	}
	assert(i != tcp::resolver::iterator());
	m_sock.async_connect(*i, boost::bind(&http_connection::on_connect
		, shared_from_this(), _1/*, ++i*/));
}

void http_connection::on_connect(asio::error_code const& e
	/*, tcp::resolver::iterator i*/)
{
	if (!e)
	{
		m_last_receive = time_now();
		asio::async_write(m_sock, asio::buffer(sendbuffer)
			, bind(&http_connection::on_write, shared_from_this(), _1));
	}
/*	else if (i != tcp::resolver::iterator())
	{
		// The connection failed. Try the next endpoint in the list.
		m_sock.close();
		m_sock.async_connect(*i, bind(&http_connection::on_connect
			, shared_from_this(), _1, ++i));
	}
*/	else
	{
		close();
		if (m_bottled && m_called) return;
		m_called = true;
		m_handler(e, m_parser, 0, 0);
	}
}

void http_connection::on_write(asio::error_code const& e)
{
	if (e)
	{
		close();
		if (m_bottled && m_called) return;
		m_called = true;
		m_handler(e, m_parser, 0, 0);
		return;
	}

	std::string().swap(sendbuffer);
	m_recvbuffer.resize(4096);

	int amount_to_read = m_recvbuffer.size() - m_read_pos;
	if (m_rate_limit > 0 && amount_to_read > m_download_quota)
	{
		amount_to_read = m_download_quota;
		if (m_download_quota == 0)
		{
			if (!m_limiter_timer_active)
				on_assign_bandwidth(asio::error_code());
			return;
		}
	}
	m_sock.async_read_some(asio::buffer(&m_recvbuffer[0] + m_read_pos
		, amount_to_read)
		, bind(&http_connection::on_read
		, shared_from_this(), _1, _2));
}

void http_connection::on_read(asio::error_code const& e
	, std::size_t bytes_transferred)
{
	if (m_rate_limit)
	{
		m_download_quota -= bytes_transferred;
		assert(m_download_quota >= 0);
	}

	if (e == asio::error::eof)
	{
		close();
		if (m_bottled && m_called) return;
		m_called = true;
		m_handler(asio::error_code(), m_parser, 0, 0);
		return;
	}

	if (e)
	{
		close();
		if (m_bottled && m_called) return;
		m_called = true;
		m_handler(e, m_parser, 0, 0);
		return;
	}

	m_read_pos += bytes_transferred;
	assert(m_read_pos <= int(m_recvbuffer.size()));

	// having a nonempty path means we should handle redirects
	if (m_redirect && m_parser.header_finished())
	{
		int code = m_parser.status_code();
		if (code >= 300 && code < 400)
		{
			// attempt a redirect
			std::string url = m_parser.header<std::string>("location");
			if (url.empty())
			{
				// missing location header
				if (m_bottled && m_called) return;
				m_called = true;
				m_handler(e, m_parser, 0, 0);
				return;
			}

			m_limiter_timer_active = false;
			m_timer.cancel();
			m_limiter_timer.cancel();
			m_sock.close();
			m_hostname.clear();
			m_port.clear();
			get(url, m_timeout);
			return;
		}

		m_redirect = false;
	}

	if (m_bottled || !m_parser.header_finished())
	{
		libtorrent::buffer::const_interval rcv_buf(&m_recvbuffer[0]
			, &m_recvbuffer[0] + m_read_pos);
		m_parser.incoming(rcv_buf);
		if (!m_bottled && m_parser.header_finished())
		{
			if (m_read_pos > m_parser.body_start())
				m_handler(e, m_parser, &m_recvbuffer[0] + m_parser.body_start()
					, m_read_pos - m_parser.body_start());
			m_read_pos = 0;
			m_last_receive = time_now();
		}
		else if (m_bottled && m_parser.finished())
		{
			m_timer.cancel();
			if (m_bottled && m_called) return;
			m_called = true;
			m_handler(e, m_parser, 0, 0);
		}
	}
	else
	{
		assert(!m_bottled);
		m_handler(e, m_parser, &m_recvbuffer[0], m_read_pos);
		m_read_pos = 0;
		m_last_receive = time_now();
	}

	if (int(m_recvbuffer.size()) == m_read_pos)
		m_recvbuffer.resize((std::min)(m_read_pos + 2048, 1024*500));
	if (m_read_pos == 1024 * 500)
	{
		close();
		if (m_bottled && m_called) return;
		m_called = true;
		m_handler(asio::error::eof, m_parser, 0, 0);
		return;
	}
	int amount_to_read = m_recvbuffer.size() - m_read_pos;
	if (m_rate_limit > 0 && amount_to_read > m_download_quota)
	{
		amount_to_read = m_download_quota;
		if (m_download_quota == 0)
		{
			if (!m_limiter_timer_active)
				on_assign_bandwidth(asio::error_code());
			return;
		}
	}
	m_sock.async_read_some(asio::buffer(&m_recvbuffer[0] + m_read_pos
		, amount_to_read)
		, bind(&http_connection::on_read
		, shared_from_this(), _1, _2));
}

void http_connection::on_assign_bandwidth(asio::error_code const& e)
{
	if ((e == asio::error::operation_aborted
		&& m_limiter_timer_active)
		|| !m_sock.is_open())
	{
		if (!m_bottled || !m_called)
			m_handler(e, m_parser, 0, 0);
		return;
	}
	m_limiter_timer_active = false;
	if (e) return;

	if (m_download_quota > 0) return;

	m_download_quota = m_rate_limit / 4;

	int amount_to_read = m_recvbuffer.size() - m_read_pos;
	if (amount_to_read > m_download_quota)
		amount_to_read = m_download_quota;

	m_sock.async_read_some(asio::buffer(&m_recvbuffer[0] + m_read_pos
		, amount_to_read)
		, bind(&http_connection::on_read
		, shared_from_this(), _1, _2));

	m_limiter_timer_active = true;
	m_limiter_timer.expires_from_now(milliseconds(250));
	m_limiter_timer.async_wait(bind(&http_connection::on_assign_bandwidth
		, shared_from_this(), _1));
}

void http_connection::rate_limit(int limit)
{
	if (!m_limiter_timer_active)
	{
		m_limiter_timer_active = true;
		m_limiter_timer.expires_from_now(milliseconds(250));
		m_limiter_timer.async_wait(bind(&http_connection::on_assign_bandwidth
			, shared_from_this(), _1));
	}
	m_rate_limit = limit;
}

}

<|endoftext|>


<commit_before>/* Copyright 2021 The TensorFlow Authors. All Rights Reserved.

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 "tensorflow_lite_support/c/task/vision/image_classifier.h"

#include <memory>

#include "tensorflow_lite_support/c/task/vision/utils/frame_buffer_cpp_c_utils.h"
#include "tensorflow_lite_support/cc/task/vision/image_classifier.h"
#include "tensorflow_lite_support/cc/task/vision/proto/classifications_proto_inc.h"
#include "tensorflow_lite_support/cc/task/vision/proto/image_classifier_options_proto_inc.h"

namespace {
using ::tflite::support::StatusOr;
using ClassificationResultCpp = ::tflite::task::vision::ClassificationResult;
using ClassificationsCpp = ::tflite::task::vision::Classifications;
using ClassCpp = ::tflite::task::vision::Class;
using BoundingBoxCpp = ::tflite::task::vision::BoundingBox;
using ImageClassifierCpp = ::tflite::task::vision::ImageClassifier;
using ImageClassifierOptionsCpp =
    ::tflite::task::vision::ImageClassifierOptions;
using FrameBufferCpp = ::tflite::task::vision::FrameBuffer;
}  // namespace

#ifdef __cplusplus
extern "C" {
#endif  // __cplusplus

struct TfLiteImageClassifier {
  std::unique_ptr<ImageClassifierCpp> impl;
};

TfLiteImageClassifierOptions TfLiteImageClassifierOptionsCreate() {
  TfLiteImageClassifierOptions options = {
      .classification_options.max_results = -1,
      .base_options.compute_settings.cpu_settings.num_threads = -1};
  return options;
}

std::unique_ptr<ImageClassifierOptionsCpp>
CreateImageClassifierCppOptionsFromCOptions(
    const TfLiteImageClassifierOptions* c_options) {
  std::unique_ptr<ImageClassifierOptionsCpp> cpp_options(
      new ImageClassifierOptionsCpp);

  // More file sources can be added in else ifs
  if (c_options->base_options.model_file.file_path)
    cpp_options->mutable_base_options()->mutable_model_file()->set_file_name(
        c_options->base_options.model_file.file_path);
  else
    return nullptr;

  // c_options->base_options.compute_settings.num_threads iis expected to be
  // set to value > 0 or -1. Otherwise invoking
  // ImageClassifierCpp::CreateFromOptions() results in a not ok status.
  cpp_options->mutable_base_options()
      ->mutable_compute_settings()
      ->mutable_tflite_settings()
      ->mutable_cpu_settings()
      ->set_num_threads(
          c_options->base_options.compute_settings.cpu_settings.num_threads);

  for (int i = 0; i < c_options->classification_options.label_denylist.length;
       i++)
    cpp_options->add_class_name_blacklist(
        c_options->classification_options.label_denylist.list[i]);

  for (int i = 0; i < c_options->classification_options.label_allowlist.length;
       i++)
    cpp_options->add_class_name_whitelist(
        c_options->classification_options.label_allowlist.list[i]);

  // Check needed since setting a nullptr for this field results in a segfault
  // on invocation of ImageClassifierCpp::CreateFromOptions().
  if (c_options->classification_options.display_names_local) {
    cpp_options->set_display_names_locale(
        c_options->classification_options.display_names_local);
  }

  // c_options->classification_options.max_results is expected to be set to -1
  // or any value > 0. Otherwise invoking
  // ImageClassifierCpp::CreateFromOptions() results in a not ok status.
  cpp_options->set_max_results(c_options->classification_options.max_results);

  cpp_options->set_score_threshold(
      c_options->classification_options.score_threshold);

  return cpp_options;
}

TfLiteImageClassifier* TfLiteImageClassifierFromOptions(
    const TfLiteImageClassifierOptions* options) {
  std::unique_ptr<ImageClassifierOptionsCpp> cpp_options =
      CreateImageClassifierCppOptionsFromCOptions(options);

  if (cpp_options == nullptr) {
    return nullptr;
  }

  auto classifier_status = ImageClassifierCpp::CreateFromOptions(*cpp_options);

  if (classifier_status.ok()) {
    return new TfLiteImageClassifier{.impl =
                                         std::move(classifier_status.value())};
  } else {
    return nullptr;
  }
}

TfLiteClassificationResult* GetClassificationResultCStruct(
    const ClassificationResultCpp& classification_result_cpp) {
  TfLiteClassifications* c_classifications =
      new TfLiteClassifications[classification_result_cpp
                                    .classifications_size()];

  for (int head = 0; head < classification_result_cpp.classifications_size();
       ++head) {
    const ClassificationsCpp& classifications =
        classification_result_cpp.classifications(head);
    c_classifications[head].head_index = head;

    TfLiteCategory* c_categories =
        new TfLiteCategory[classifications.classes_size()];
    c_classifications->size = classifications.classes_size();

    for (int rank = 0; rank < classifications.classes_size(); ++rank) {
      const ClassCpp& classification = classifications.classes(rank);
      c_categories[rank].index = classification.index();
      c_categories[rank].score = classification.score();

      if (classification.has_class_name())
        c_categories[rank].label = strdup(classification.class_name().c_str());
      else
        c_categories[rank].label = nullptr;

      if (classification.has_display_name())
        c_categories[rank].display_name =
            strdup(classification.display_name().c_str());
      else
        c_categories[rank].display_name = nullptr;
    }
    c_classifications[head].categories = c_categories;
  }

  TfLiteClassificationResult* c_classification_result =
      new TfLiteClassificationResult;
  c_classification_result->classifications = c_classifications;
  c_classification_result->size =
      classification_result_cpp.classifications_size();

  return c_classification_result;
}

TfLiteClassificationResult* TfLiteImageClassifierClassifyWithRoi(
    const TfLiteImageClassifier* classifier,
    const TfLiteFrameBuffer* frame_buffer, const TfLiteBoundingBox* roi) {
  if (classifier == nullptr || frame_buffer == nullptr) {
    return nullptr;
  }

  BoundingBoxCpp cc_roi;
  if (roi == nullptr) {
    cc_roi.set_width(frame_buffer->dimension.width);
    cc_roi.set_height(frame_buffer->dimension.height);
  } else {
    cc_roi.set_origin_x(roi->origin_x);
    cc_roi.set_origin_y(roi->origin_y);
    cc_roi.set_width(roi->width);
    cc_roi.set_height(roi->height);
  }

  StatusOr<std::unique_ptr<FrameBufferCpp>> cpp_frame_buffer_status =
      ::tflite::task::vision::CreateCppFrameBuffer(*frame_buffer);
  if (!cpp_frame_buffer_status.ok()) return nullptr;

  // fnc_sample(cpp_frame_buffer_status);
  StatusOr<ClassificationResultCpp> classification_result_cpp =
      classifier->impl->Classify(*std::move(cpp_frame_buffer_status.value()),
                                 cc_roi);

  if (!classification_result_cpp.ok()) return nullptr;

  return GetClassificationResultCStruct(classification_result_cpp.value());
}

TfLiteClassificationResult* TfLiteImageClassifierClassify(
    const TfLiteImageClassifier* classifier,
    const TfLiteFrameBuffer* frame_buffer) {
  return TfLiteImageClassifierClassifyWithRoi(classifier, frame_buffer,
                                              nullptr);
}

void TfLiteImageClassifierDelete(TfLiteImageClassifier* classifier) {
  delete classifier;
}

#ifdef __cplusplus
}  // extern "C"
#endif  // __cplusplus
<commit_msg>Formatting fixes<commit_after>/* Copyright 2021 The TensorFlow Authors. All Rights Reserved.

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 "tensorflow_lite_support/c/task/vision/image_classifier.h"

#include <memory>

#include "tensorflow_lite_support/c/task/vision/utils/frame_buffer_cpp_c_utils.h"
#include "tensorflow_lite_support/cc/task/vision/image_classifier.h"
#include "tensorflow_lite_support/cc/task/vision/proto/classifications_proto_inc.h"
#include "tensorflow_lite_support/cc/task/vision/proto/image_classifier_options_proto_inc.h"

namespace {
using ::tflite::support::StatusOr;
using ClassificationResultCpp = ::tflite::task::vision::ClassificationResult;
using ClassificationsCpp = ::tflite::task::vision::Classifications;
using ClassCpp = ::tflite::task::vision::Class;
using BoundingBoxCpp = ::tflite::task::vision::BoundingBox;
using ImageClassifierCpp = ::tflite::task::vision::ImageClassifier;
using ImageClassifierOptionsCpp =
    ::tflite::task::vision::ImageClassifierOptions;
using FrameBufferCpp = ::tflite::task::vision::FrameBuffer;
}  // namespace

#ifdef __cplusplus
extern "C" {
#endif  // __cplusplus

struct TfLiteImageClassifier {
  std::unique_ptr<ImageClassifierCpp> impl;
};

TfLiteImageClassifierOptions TfLiteImageClassifierOptionsCreate() {
  return {
      .classification_options.max_results = -1,
      .base_options.compute_settings.cpu_settings.num_threads = -1};
}

std::unique_ptr<ImageClassifierOptionsCpp>
CreateImageClassifierCppOptionsFromCOptions(
    const TfLiteImageClassifierOptions* c_options) {
  std::unique_ptr<ImageClassifierOptionsCpp> cpp_options(
      new ImageClassifierOptionsCpp);

  // More file sources can be added in else ifs
  if (c_options->base_options.model_file.file_path)
    cpp_options->mutable_base_options()->mutable_model_file()->set_file_name(
        c_options->base_options.model_file.file_path);
  else
    return nullptr;

  // c_options->base_options.compute_settings.num_threads iis expected to be
  // set to value > 0 or -1. Otherwise invoking
  // ImageClassifierCpp::CreateFromOptions() results in a not ok status.
  cpp_options->mutable_base_options()
      ->mutable_compute_settings()
      ->mutable_tflite_settings()
      ->mutable_cpu_settings()
      ->set_num_threads(
          c_options->base_options.compute_settings.cpu_settings.num_threads);

  for (int i = 0; i < c_options->classification_options.label_denylist.length;
       i++)
    cpp_options->add_class_name_blacklist(
        c_options->classification_options.label_denylist.list[i]);

  for (int i = 0; i < c_options->classification_options.label_allowlist.length;
       i++)
    cpp_options->add_class_name_whitelist(
        c_options->classification_options.label_allowlist.list[i]);

  // Check needed since setting a nullptr for this field results in a segfault
  // on invocation of ImageClassifierCpp::CreateFromOptions().
  if (c_options->classification_options.display_names_local) {
    cpp_options->set_display_names_locale(
        c_options->classification_options.display_names_local);
  }

  // c_options->classification_options.max_results is expected to be set to -1
  // or any value > 0. Otherwise invoking
  // ImageClassifierCpp::CreateFromOptions() results in a not ok status.
  cpp_options->set_max_results(c_options->classification_options.max_results);

  cpp_options->set_score_threshold(
      c_options->classification_options.score_threshold);

  return cpp_options;
}

TfLiteImageClassifier* TfLiteImageClassifierFromOptions(
    const TfLiteImageClassifierOptions* options) {
  std::unique_ptr<ImageClassifierOptionsCpp> cpp_options =
      CreateImageClassifierCppOptionsFromCOptions(options);

  if (cpp_options == nullptr) {
    return nullptr;
  }

  auto classifier_status = ImageClassifierCpp::CreateFromOptions(*cpp_options);

  if (classifier_status.ok()) {
    return new TfLiteImageClassifier{.impl =
                                         std::move(classifier_status.value())};
  } else {
    return nullptr;
  }
}

TfLiteClassificationResult* GetClassificationResultCStruct(
    const ClassificationResultCpp& classification_result_cpp) {
  TfLiteClassifications* c_classifications =
      new TfLiteClassifications[classification_result_cpp
                                    .classifications_size()];

  for (int head = 0; head < classification_result_cpp.classifications_size();
       ++head) {
    const ClassificationsCpp& classifications =
        classification_result_cpp.classifications(head);
    c_classifications[head].head_index = head;

    TfLiteCategory* c_categories =
        new TfLiteCategory[classifications.classes_size()];
    c_classifications->size = classifications.classes_size();

    for (int rank = 0; rank < classifications.classes_size(); ++rank) {
      const ClassCpp& classification = classifications.classes(rank);
      c_categories[rank].index = classification.index();
      c_categories[rank].score = classification.score();

      if (classification.has_class_name())
        c_categories[rank].label = strdup(classification.class_name().c_str());
      else
        c_categories[rank].label = nullptr;

      if (classification.has_display_name())
        c_categories[rank].display_name =
            strdup(classification.display_name().c_str());
      else
        c_categories[rank].display_name = nullptr;
    }
    c_classifications[head].categories = c_categories;
  }

  TfLiteClassificationResult* c_classification_result =
      new TfLiteClassificationResult;
  c_classification_result->classifications = c_classifications;
  c_classification_result->size =
      classification_result_cpp.classifications_size();

  return c_classification_result;
}

TfLiteClassificationResult* TfLiteImageClassifierClassifyWithRoi(
    const TfLiteImageClassifier* classifier,
    const TfLiteFrameBuffer* frame_buffer, const TfLiteBoundingBox* roi) {
  if (classifier == nullptr || frame_buffer == nullptr) {
    return nullptr;
  }

  BoundingBoxCpp cc_roi;
  if (roi == nullptr) {
    cc_roi.set_width(frame_buffer->dimension.width);
    cc_roi.set_height(frame_buffer->dimension.height);
  } else {
    cc_roi.set_origin_x(roi->origin_x);
    cc_roi.set_origin_y(roi->origin_y);
    cc_roi.set_width(roi->width);
    cc_roi.set_height(roi->height);
  }

  StatusOr<std::unique_ptr<FrameBufferCpp>> cpp_frame_buffer_status =
      ::tflite::task::vision::CreateCppFrameBuffer(*frame_buffer);
  if (!cpp_frame_buffer_status.ok()) return nullptr;

  // fnc_sample(cpp_frame_buffer_status);
  StatusOr<ClassificationResultCpp> classification_result_cpp =
      classifier->impl->Classify(*std::move(cpp_frame_buffer_status.value()),
                                 cc_roi);

  if (!classification_result_cpp.ok()) return nullptr;

  return GetClassificationResultCStruct(classification_result_cpp.value());
}

TfLiteClassificationResult* TfLiteImageClassifierClassify(
    const TfLiteImageClassifier* classifier,
    const TfLiteFrameBuffer* frame_buffer) {
  return TfLiteImageClassifierClassifyWithRoi(classifier, frame_buffer,
                                              nullptr);
}

void TfLiteImageClassifierDelete(TfLiteImageClassifier* classifier) {
  delete classifier;
}

#ifdef __cplusplus
}  // extern "C"
#endif  // __cplusplus
<|endoftext|>