code:
/*
Minetest
Copyright (C) 2013 sapier, sapier at gmx dot net
Copyright (C) 2016 est31, <MTest31@outlook.com>

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) 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 Lesser General Public License for more details.

You should have received a copy of the GNU Lesser General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/

/******************************************************************************/
/* Includes                                                                   */
/******************************************************************************/

#include "pathfinder.h"
#include "serverenvironment.h"
#include "server.h"
#include "nodedef.h"

//#define PATHFINDER_DEBUG
//#define PATHFINDER_CALC_TIME

#ifdef PATHFINDER_DEBUG
	#include <string>
#endif
#ifdef PATHFINDER_DEBUG
	#include <iomanip>
#endif
#ifdef PATHFINDER_CALC_TIME
	#include <sys/time.h>
#endif

/******************************************************************************/
/* Typedefs and macros                                                        */
/******************************************************************************/

#define LVL "(" << level << ")" <<

#ifdef PATHFINDER_DEBUG
#define DEBUG_OUT(a)     std::cout << a
#define INFO_TARGET      std::cout
#define VERBOSE_TARGET   std::cout
#define ERROR_TARGET     std::cout
#else
#define DEBUG_OUT(a)     while(0)
#define INFO_TARGET      infostream << "Pathfinder: "
#define VERBOSE_TARGET   verbosestream << "Pathfinder: "
#define ERROR_TARGET     warningstream << "Pathfinder: "
#endif

/******************************************************************************/
/* Class definitions                                                          */
/******************************************************************************/

/** representation of cost in specific direction */
class PathCost {
public:

	/** default constructor */
	PathCost();

	/** copy constructor */
	PathCost(const PathCost &b);

	/** assignment operator */
	PathCost &operator= (const PathCost &b);

	bool valid;              /**< movement is possible         */
	int  value;              /**< cost of movement             */
	int  direction;          /**< y-direction of movement      */
	bool updated;            /**< this cost has ben calculated */

};

/** representation of a mapnode to be used for pathfinding */
class PathGridnode {

public:
	/** default constructor */
	PathGridnode();

	/** copy constructor */
	PathGridnode(const PathGridnode &b);

	/**
	 * assignment operator
	 * @param b node to copy
	 */
	PathGridnode &operator= (const PathGridnode &b);

	/**
	 * read cost in a specific direction
	 * @param dir direction of cost to fetch
	 */
	PathCost getCost(v3s16 dir);

	/**
	 * set cost value for movement
	 * @param dir direction to set cost for
	 * @cost cost to set
	 */
	void      setCost(v3s16 dir, const PathCost &cost);

	bool      valid;               /**< node is on surface                    */
	bool      target;              /**< node is target position               */
	bool      source;              /**< node is stating position              */
	int       totalcost;           /**< cost to move here from starting point */
	v3s16     sourcedir;           /**< origin of movement for current cost   */
	v3s16     pos;                 /**< real position of node                 */
	PathCost directions[4];        /**< cost in different directions          */

	/* debug values */
	bool      is_element;          /**< node is element of path detected      */
	char      type;                /**< type of node                          */
};

class Pathfinder;

/** Abstract class to manage the map data */
class GridNodeContainer {
public:
	virtual PathGridnode &access(v3s16 p)=0;
	virtual ~GridNodeContainer() {}
protected:
	Pathfinder *m_pathf;

	void initNode(v3s16 ipos, PathGridnode *p_node);
};

class ArrayGridNodeContainer : public GridNodeContainer {
public:
	virtual ~ArrayGridNodeContainer() {}
	ArrayGridNodeContainer(Pathfinder *pathf, v3s16 dimensions);
	virtual PathGridnode &access(v3s16 p);
private:
	v3s16 m_dimensions;

	int m_x_stride;
	int m_y_stride;
	std::vector<PathGridnode> m_nodes_array;
};

class MapGridNodeContainer : public GridNodeContainer {
public:
	virtual ~MapGridNodeContainer() {}
	MapGridNodeContainer(Pathfinder *pathf);
	virtual PathGridnode &access(v3s16 p);
private:
	std::map<v3s16, PathGridnode> m_nodes;
};

/** class doing pathfinding */
class Pathfinder {

public:
	/**
	 * default constructor
	 */
	Pathfinder();

	~Pathfinder();

	/**
	 * path evaluation function
	 * @param env environment to look for path
	 * @param source origin of path
	 * @param destination end position of path
	 * @param searchdistance maximum number of nodes to look in each direction
	 * @param max_jump maximum number of blocks a path may jump up
	 * @param max_drop maximum number of blocks a path may drop
	 * @param algo Algorithm to use for finding a path
	 */
	std::vector<v3s16> getPath(ServerEnvironment *env,
			v3s16 source,
			v3s16 destination,
			unsigned int searchdistance,
			unsigned int max_jump,
			unsigned int max_drop,
			PathAlgorithm algo);

private:
	/* helper functions */

	/**
	 * transform index pos to mappos
	 * @param ipos a index position
	 * @return map position
	 */
	v3s16          getRealPos(v3s16 ipos);

	/**
	 * transform mappos to index pos
	 * @param pos a real pos
	 * @return index position
	 */
	v3s16          getIndexPos(v3s16 pos);

	/**
	 * get gridnode at a specific index position
	 * @param ipos index position
	 * @return gridnode for index
	 */
	PathGridnode &getIndexElement(v3s16 ipos);

	/**
	 * Get gridnode at a specific index position
	 * @return gridnode for index
	 */
	PathGridnode &getIdxElem(s16 x, s16 y, s16 z);

	/**
	 * invert a 3d position
	 * @param pos 3d position
	 * @return pos *-1
	 */
	v3s16          invert(v3s16 pos);

	/**
	 * check if a index is within current search area
	 * @param index position to validate
	 * @return true/false
	 */
	bool           isValidIndex(v3s16 index);

	/**
	 * translate position to float position
	 * @param pos integer position
	 * @return float position
	 */
	v3f            tov3f(v3s16 pos);

	/* algorithm functions */

	/**
	 * calculate 2d manahttan distance to target on the xz plane
	 * @param pos position to calc distance
	 * @return integer distance
	 */
	int           getXZManhattanDist(v3s16 pos);

	/**
	 * get best direction based uppon heuristics
	 * @param directions list of unchecked directions
	 * @param g_pos mapnode to start from
	 * @return direction to check
	 */
	v3s16         getDirHeuristic(std::vector<v3s16> &directions, PathGridnode &g_pos);

	/**
	 * build internal data representation of search area
	 * @return true/false if costmap creation was successfull
	 */
	bool          buildCostmap();

	/**
	 * calculate cost of movement
	 * @param pos real world position to start movement
	 * @param dir direction to move to
	 * @return cost information
	 */
	PathCost     calcCost(v3s16 pos, v3s16 dir);

	/**
	 * recursive update whole search areas total cost information
	 * @param ipos position to check next
	 * @param srcdir positionc checked last time
	 * @param total_cost cost of moving to ipos
	 * @param level current recursion depth
	 * @return true/false path to destination has been found
	 */
	bool          updateAllCosts(v3s16 ipos, v3s16 srcdir, int total_cost, int level);

	/**
	 * recursive try to find a patrh to destionation
	 * @param ipos position to check next
	 * @param srcdir positionc checked last time
	 * @param total_cost cost of moving to ipos
	 * @param level current recursion depth
	 * @return true/false path to destination has been found
	 */
	bool          updateCostHeuristic(v3s16 ipos, v3s16 srcdir, int current_cost, int level);

	/**
	 * recursive build a vector containing all nodes from source to destination
	 * @param path vector to add nodes to
	 * @param pos pos to check next
	 * @param level recursion depth
	 */
	void          buildPath(std::vector<v3s16> &path, v3s16 pos, int level);

	/* variables */
	int m_max_index_x;            /**< max index of search area in x direction  */
	int m_max_index_y;            /**< max index of search area in y direction  */
	int m_max_index_z;            /**< max index of search area in z direction  */

	int m_searchdistance;         /**< max distance to search in each direction */
	int m_maxdrop;                /**< maximum number of blocks a path may drop */
	int m_maxjump;                /**< maximum number of blocks a path may jump */
	int m_min_target_distance;    /**< current smalest path to target           */

	bool m_prefetch;              /**< prefetch cost data                       */

	v3s16 m_start;                /**< source position                          */
	v3s16 m_destination;          /**< destination position                     */

	core::aabbox3d<s16> m_limits; /**< position limits in real map coordinates  */

	/** contains all map data already collected and analyzed.
		Access it via the getIndexElement/getIdxElem methods. */
	friend class GridNodeContainer;
	GridNodeContainer *m_nodes_container;

	ServerEnvironment *m_env;     /**< minetest environment pointer             */

#ifdef PATHFINDER_DEBUG

	/**
	 * print collected cost information
	 */
	void printCost();

	/**
	 * print collected cost information in a specific direction
	 * @param dir direction to print
	 */
	void printCost(PathDirections dir);

	/**
	 * print type of node as evaluated
	 */
	void printType();

	/**
	 * print pathlenght for all nodes in search area
	 */
	void printPathLen();

	/**
	 * print a path
	 * @param path path to show
	 */
	void printPath(std::vector<v3s16> path);

	/**
	 * print y direction for all movements
	 */
	void printYdir();

	/**
	 * print y direction for moving in a specific direction
	 * @param dir direction to show data
	 */
	void printYdir(PathDirections dir);

	/**
	 * helper function to translate a direction to speaking text
	 * @param dir direction to translate
	 * @return textual name of direction
	 */
	std::string dirToName(PathDirections dir);
#endif
};

/******************************************************************************/
/* implementation                                                             */
/******************************************************************************/

std::vector<v3s16> get_path(ServerEnvironment* env,
							v3s16 source,
							v3s16 destination,
							unsigned int searchdistance,
							unsigned int max_jump,
							unsigned int max_drop,
							PathAlgorithm algo)
{
	Pathfinder searchclass;

	return searchclass.getPath(env,
				source, destination,
				searchdistance, max_jump, max_drop, algo);
}

/******************************************************************************/
PathCost::PathCost()
:	valid(false),
	value(0),
	direction(0),
	updated(false)
{
	//intentionaly empty
}

/******************************************************************************/
PathCost::PathCost(const PathCost &b)
{
	valid     = b.valid;
	direction = b.direction;
	value     = b.value;
	updated   = b.updated;
}

/******************************************************************************/
PathCost &PathCost::operator= (const PathCost &b)
{
	valid     = b.valid;
	direction = b.direction;
	value     = b.value;
	updated   = b.updated;

	return *this;
}

/******************************************************************************/
PathGridnode::PathGridnode()
:	valid(false),
	target(false),
	source(false),
	totalcost(-1),
	sourcedir(v3s16(0, 0, 0)),
	pos(v3s16(0, 0, 0)),
	is_element(false),
	type('u')
{
	//intentionaly empty
}

/******************************************************************************/
PathGridnode::PathGridnode(const PathGridnode &b)
:	valid(b.valid),
	target(b.target),
	source(b.source),
	totalcost(b.totalcost),
	sourcedir(b.sourcedir),
	pos(b.pos),
	is_element(b.is_element),
	type(b.type)
{

	directions[DIR_XP] = b.directions[DIR_XP];
	directions[DIR_XM] = b.directions[DIR_XM];
	directions[DIR_ZP] = b.directions[DIR_ZP];
	directions[DIR_ZM] = b.directions[DIR_ZM];
}

/******************************************************************************/
PathGridnode &PathGridnode::operator= (const PathGridnode &b)
{
	valid      = b.valid;
	target     = b.target;
	source     = b.source;
	is_element = b.is_element;
	totalcost  = b.totalcost;
	sourcedir  = b.sourcedir;
	pos        = b.pos;
	type       = b.type;

	directions[DIR_XP] = b.directions[DIR_XP];
	directions[DIR_XM] = b.directions[DIR_XM];
	directions[DIR_ZP] = b.directions[DIR_ZP];
	directions[DIR_ZM] = b.directions[DIR_ZM];

	return *this;
}

/******************************************************************************/
PathCost PathGridnode::getCost(v3s16 dir)
{
	if (dir.X > 0) {
		return directions[DIR_XP];
	}
	if (dir.X < 0) {
		return directions[DIR_XM];
	}
	if (dir.Z > 0) {
		return directions[DIR_ZP];
	}
	if (dir.Z < 0) {
		return directions[DIR_ZM];
	}
	PathCost retval;
	return retval;
}

/******************************************************************************/
void PathGridnode::setCost(v3s16 dir, const PathCost &cost)
{
	if (dir.X > 0) {
		directions[DIR_XP] = cost;
	}
	if (dir.X < 0) {
		directions[DIR_XM] = cost;
	}
	if (dir.Z > 0) {
		directions[DIR_ZP] = cost;
	}
	if (dir.Z < 0) {
		directions[DIR_ZM] = cost;
	}
}

void GridNodeContainer::initNode(v3s16 ipos, PathGridnode *p_node)
{
	INodeDefManager *ndef = m_pathf->m_env->getGameDef()->ndef();
	PathGridnode &elem = *p_node;

	v3s16 realpos = m_pathf->getRealPos(ipos);

	MapNode current = m_pathf->m_env->getMap().getNodeNoEx(realpos);
	MapNode below   = m_pathf->m_env->getMap().getNodeNoEx(realpos + v3s16(0, -1, 0));

	if ((current.param0 == CONTENT_IGNORE) ||
			(below.param0 == CONTENT_IGNORE)) {
		DEBUG_OUT("Pathfinder: " << PP(realpos) <<
			" current or below is invalid element" << std::endl);
		if (current.param0 == CONTENT_IGNORE) {
			elem.type = 'i';
			DEBUG_OUT(PP(ipos) << ": " << 'i' << std::endl);
		}
		return;
	}

	//don't add anything if it isn't an air node
	if (ndef->get(current).walkable || !ndef->get(below).walkable) {
			DEBUG_OUT("Pathfinder: " << PP(realpos)
				<< " not on surface" << std::endl);
			if (ndef->get(current).walkable) {
				elem.type = 's';
				DEBUG_OUT(PP(ipos) << ": " << 's' << std::endl);
			} else {
				elem.type = '-';
				DEBUG_OUT(PP(ipos) << ": " << '-' << std::endl);
			}
			return;
	}

	elem.valid = true;
	elem.pos   = realpos;
	elem.type  = 'g';
	DEBUG_OUT(PP(ipos) << ": " << 'a' << std::endl);

	if (m_pathf->m_prefetch) {
		elem.directions[DIR_XP] = m_pathf->calcCost(realpos, v3s16( 1, 0, 0));
		elem.directions[DIR_XM] = m_pathf->calcCost(realpos, v3s16(-1, 0, 0));
		elem.directions[DIR_ZP] = m_pathf->calcCost(realpos, v3s16( 0, 0, 1));
		elem.directions[DIR_ZM] = m_pathf->calcCost(realpos, v3s16( 0, 0,-1));
	}
}

ArrayGridNodeContainer::ArrayGridNodeContainer(Pathfinder *pathf, v3s16 dimensions) :
	m_x_stride(dimensions.Y * dimensions.Z),
	m_y_stride(dimensions.Z)
{
	m_pathf = pathf;

	m_nodes_array.resize(dimensions.X * dimensions.Y * dimensions.Z);
	INFO_TARGET << "Pathfinder ArrayGridNodeContainer constructor." << std::endl;
	for (int x = 0; x < dimensions.X; x++) {
		for (int y = 0; y < dimensions.Y; y++) {
			for (int z= 0; z < dimensions.Z; z++) {
				v3s16 ipos(x, y, z);
				initNode(ipos, &access(ipos));
			}
		}
	}
}

PathGridnode &ArrayGridNodeContainer::access(v3s16 p)
{
	return m_nodes_array[p.X * m_x_stride + p.Y * m_y_stride + p.Z];
}

MapGridNodeContainer::MapGridNodeContainer(Pathfinder *pathf)
{
	m_pathf = pathf;
}

PathGridnode &MapGridNodeContainer::access(v3s16 p)
{
	std::map<v3s16, PathGridnode>::iterator it = m_nodes.find(p);
	if (it != m_nodes.end()) {
		return it->second;
	}
	PathGridnode &n = m_nodes[p];
	initNode(p, &n);
	return n;
}

/******************************************************************************/
std::vector<v3s16> Pathfinder::getPath(ServerEnvironment *env,
							v3s16 source,
							v3s16 destination,
							unsigned int searchdistance,
							unsigned int max_jump,
							unsigned int max_drop,
							PathAlgorithm algo)
{
#ifdef PATHFINDER_CALC_TIME
	timespec ts;
	clock_gettime(CLOCK_REALTIME, &ts);
#endif
	std::vector<v3s16> retval;

	//check parameters
	if (env == 0) {
		ERROR_TARGET << "missing environment pointer" << std::endl;
		return retval;
	}

	m_searchdistance = searchdistance;
	m_env = env;
	m_maxjump = max_jump;
	m_maxdrop = max_drop;
	m_start       = source;
	m_destination = destination;
	m_min_target_distance = -1;
	m_prefetch = true;

	if (algo == PA_PLAIN_NP) {
		m_prefetch = false;
	}

	int min_x = MYMIN(source.X, destination.X);
	int max_x = MYMAX(source.X, destination.X);

	int min_y = MYMIN(source.Y, destination.Y);
	int max_y = MYMAX(source.Y, destination.Y);

	int min_z = MYMIN(source.Z, destination.Z);
	int max_z = MYMAX(source.Z, destination.Z);

	m_limits.MinEdge.X = min_x - searchdistance;
	m_limits.MinEdge.Y = min_y - searchdistance;
	m_limits.MinEdge.Z = min_z - searchdistance;

	m_limits.MaxEdge.X = max_x + searchdistance;
	m_limits.MaxEdge.Y = max_y + searchdistance;
	m_limits.MaxEdge.Z = max_z + searchdistance;

	v3s16 diff = m_limits.MaxEdge - m_limits.MinEdge;

	m_max_index_x = diff.X;
	m_max_index_y = diff.Y;
	m_max_index_z = diff.Z;

	delete m_nodes_container;
	if (diff.getLength() > 5) {
		m_nodes_container = new MapGridNodeContainer(this);
	} else {
		m_nodes_container = new ArrayGridNodeContainer(this, diff);
	}
#ifdef PATHFINDER_DEBUG
	printType();
	printCost();
	printYdir();
#endif

	//validate and mark start and end pos
	v3s16 StartIndex  = getIndexPos(source);
	v3s16 EndIndex    = getIndexPos(destination);

	PathGridnode &startpos = getIndexElement(StartIndex);
	PathGridnode &endpos   = getIndexElement(EndIndex);

	if (!startpos.valid) {
		VERBOSE_TARGET << "invalid startpos" <<
				"Index: " << PP(StartIndex) <<
				"Realpos: " << PP(getRealPos(StartIndex)) << std::endl;
		return retval;
	}
	if (!endpos.valid) {
		VERBOSE_TARGET << "invalid stoppos" <<
				"Index: " << PP(EndIndex) <<
				"Realpos: " << PP(getRealPos(EndIndex)) << std::endl;
		return retval;
	}

	endpos.target      = true;
	startpos.source    = true;
	startpos.totalcost = 0;

	bool update_cost_retval = false;

	switch (algo) {
		case PA_DIJKSTRA:
			update_cost_retval = updateAllCosts(StartIndex, v3s16(0, 0, 0), 0, 0);
			break;
		case PA_PLAIN_NP:
		case PA_PLAIN:
			update_cost_retval = updateCostHeuristic(StartIndex, v3s16(0, 0, 0), 0, 0);
			break;
		default:
			ERROR_TARGET << "missing PathAlgorithm"<< std::endl;
			break;
	}

	if (update_cost_retval) {

#ifdef PATHFINDER_DEBUG
		std::cout << "Path to target found!" << std::endl;
		printPathLen();
#endif

		//find path
		std::vector<v3s16> path;
		buildPath(path, EndIndex, 0);

#ifdef PATHFINDER_DEBUG
		std::cout << "Full index path:" << std::endl;
		printPath(path);
#endif

		//finalize path
		std::vector<v3s16> full_path;
		for (std::vector<v3s16>::iterator i = path.begin();
					i != path.end(); ++i) {
			full_path.push_back(getIndexElement(*i).pos);
		}

#ifdef PATHFINDER_DEBUG
		std::cout << "full path:" << std::endl;
		printPath(full_path);
#endif
#ifdef PATHFINDER_CALC_TIME
		timespec ts2;
		clock_gettime(CLOCK_REALTIME, &ts2);

		int ms = (ts2.tv_nsec - ts.tv_nsec)/(1000*1000);
		int us = ((ts2.tv_nsec - ts.tv_nsec) - (ms*1000*1000))/1000;
		int ns = ((ts2.tv_nsec - ts.tv_nsec) - ( (ms*1000*1000) + (us*1000)));

		std::cout << "Calculating path took: " << (ts2.tv_sec - ts.tv_sec) <<
				"s " << ms << "ms " << us << "us " << ns << "ns " << std::endl;
#endif
		return full_path;
	}
	else {
#ifdef PATHFINDER_DEBUG
		printPathLen();
#endif
		ERROR_TARGET << "failed to update cost map"<< std::endl;
	}

	//return
	return retval;
}

/******************************************************************************/
Pathfinder::Pathfinder() :
	m_max_index_x(0),
	m_max_index_y(0),
	m_max_index_z(0),
	m_searchdistance(0),
	m_maxdrop(0),
	m_maxjump(0),
	m_min_target_distance(0),
	m_prefetch(true),
	m_start(0, 0, 0),
	m_destination(0, 0, 0),
	m_nodes_container(NULL),
	m_env(0)
{
	//intentionaly empty
}

Pathfinder::~Pathfinder()
{
	delete m_nodes_container;
}
/******************************************************************************/
v3s16 Pathfinder::getRealPos(v3s16 ipos)
{
	return m_limits.MinEdge + ipos;
}

/******************************************************************************/
PathCost Pathfinder::calcCost(v3s16 pos, v3s16 dir)
{
	INodeDefManager *ndef = m_env->getGameDef()->ndef();
	PathCost retval;

	retval.updated = true;

	v3s16 pos2 = pos + dir;

	//check limits
	if (!m_limits.isPointInside(pos2)) {
		DEBUG_OUT("Pathfinder: " << PP(pos2) <<
				" no cost -> out of limits" << std::endl);
		return retval;
	}

	MapNode node_at_pos2 = m_env->getMap().getNodeNoEx(pos2);

	//did we get information about node?
	if (node_at_pos2.param0 == CONTENT_IGNORE ) {
			VERBOSE_TARGET << "Pathfinder: (1) area at pos: "
					<< PP(pos2) << " not loaded";
			return retval;
	}

	if (!ndef->get(node_at_pos2).walkable) {
		MapNode node_below_pos2 =
							m_env->getMap().getNodeNoEx(pos2 + v3s16(0, -1, 0));

		//did we get information about node?
		if (node_below_pos2.param0 == CONTENT_IGNORE ) {
				VERBOSE_TARGET << "Pathfinder: (2) area at pos: "
					<< PP((pos2 + v3s16(0, -1, 0))) << " not loaded";
				return retval;
		}

		if (ndef->get(node_below_pos2).walkable) {
			retval.valid = true;
			retval.value = 1;
			retval.direction = 0;
			DEBUG_OUT("Pathfinder: "<< PP(pos)
					<< " cost same height found" << std::endl);
		}
		else {
			v3s16 testpos = pos2 - v3s16(0, -1, 0);
			MapNode node_at_pos = m_env->getMap().getNodeNoEx(testpos);

			while ((node_at_pos.param0 != CONTENT_IGNORE) &&
					(!ndef->get(node_at_pos).walkable) &&
					(testpos.Y > m_limits.MinEdge.Y)) {
				testpos += v3s16(0, -1, 0);
				node_at_pos = m_env->getMap().getNodeNoEx(testpos);
			}

			//did we find surface?
			if ((testpos.Y >= m_limits.MinEdge.Y) &&
					(node_at_pos.param0 != CONTENT_IGNORE) &&
					(ndef->get(node_at_pos).walkable)) {
				if ((pos2.Y - testpos.Y - 1) <= m_maxdrop) {
					retval.valid = true;
					retval.value = 2;
					//difference of y-pos +1 (target node is ABOVE solid node)
					retval.direction = ((testpos.Y - pos2.Y) +1);
					DEBUG_OUT("Pathfinder cost below height found" << std::endl);
				}
				else {
					INFO_TARGET << "Pathfinder:"
							" distance to surface below to big: "
							<< (testpos.Y - pos2.Y) << " max: " << m_maxdrop
							<< std::endl;
				}
			}
			else {
				DEBUG_OUT("Pathfinder: no surface below found" << std::endl);
			}
		}
	}
	else {
		v3s16 testpos = pos2;
		MapNode node_at_pos = m_env->getMap().getNodeNoEx(testpos);

		while ((node_at_pos.param0 != CONTENT_IGNORE) &&
				(ndef->get(node_at_pos).walkable) &&
				(testpos.Y < m_limits.MaxEdge.Y)) {
			testpos += v3s16(0, 1, 0);
			node_at_pos = m_env->getMap().getNodeNoEx(testpos);
		}

		//did we find surface?
		if ((testpos.Y <= m_limits.MaxEdge.Y) &&
				(!ndef->get(node_at_pos).walkable)) {

			if (testpos.Y - pos2.Y <= m_maxjump) {
				retval.valid = true;
				retval.value = 2;
				retval.direction = (testpos.Y - pos2.Y);
				DEBUG_OUT("Pathfinder cost above found" << std::endl);
			}
			else {
				DEBUG_OUT("Pathfinder: distance to surface above to big: "
						<< (testpos.Y - pos2.Y) << " max: " << m_maxjump
						<< std::endl);
			}
		}
		else {
			DEBUG_OUT("Pathfinder: no surface above found" << std::endl);
		}
	}
	return retval;
}

/******************************************************************************/
v3s16 Pathfinder::getIndexPos(v3s16 pos)
{
	return pos - m_limits.MinEdge;
}

/******************************************************************************/
PathGridnode &Pathfinder::getIndexElement(v3s16 ipos)
{
	return m_nodes_container->access(ipos);
}

/******************************************************************************/
inline PathGridnode &Pathfinder::getIdxElem(s16 x, s16 y, s16 z)
{
	return m_nodes_container->access(v3s16(x,y,z));
}

/******************************************************************************/
bool Pathfinder::isValidIndex(v3s16 index)
{
	if (	(index.X < m_max_index_x) &&
			(index.Y < m_max_index_y) &&
			(index.Z < m_max_index_z) &&
			(index.X >= 0) &&
			(index.Y >= 0) &&
			(index.Z >= 0))
		return true;

	return false;
}

/******************************************************************************/
v3s16 Pathfinder::invert(v3s16 pos)
{
	v3s16 retval = pos;

	retval.X *=-1;
	retval.Y *=-1;
	retval.Z *=-1;

	return retval;
}

/******************************************************************************/
bool Pathfinder::updateAllCosts(v3s16 ipos,
								v3s16 srcdir,
								int current_cost,
								int level)
{
	PathGridnode &g_pos = getIndexElement(ipos);
	g_pos.totalcost = current_cost;
	g_pos.sourcedir = srcdir;

	level ++;

	//check if target has been found
	if (g_pos.target) {
		m_min_target_distance = current_cost;
		DEBUG_OUT(LVL " Pathfinder: target found!" << std::endl);
		return true;
	}

	bool retval = false;

	std::vector<v3s16> directions;

	directions.push_back(v3s16( 1,0, 0));
	directions.push_back(v3s16(-1,0, 0));
	directions.push_back(v3s16( 0,0, 1));
	directions.push_back(v3s16( 0,0,-1));

	for (unsigned int i=0; i < directions.size(); i++) {
		if (directions[i] != srcdir) {
			PathCost cost = g_pos.getCost(directions[i]);

			if (cost.valid) {
				directions[i].Y = cost.direction;

				v3s16 ipos2 = ipos + directions[i];

				if (!isValidIndex(ipos2)) {
					DEBUG_OUT(LVL " Pathfinder: " << PP(ipos2) <<
						" out of range, max=" << PP(m_limits.MaxEdge) << std::endl);
					continue;
				}

				PathGridnode &g_pos2 = getIndexElement(ipos2);

				if (!g_pos2.valid) {
					VERBOSE_TARGET << LVL "Pathfinder: no data for new position: "
												<< PP(ipos2) << std::endl;
					continue;
				}

				assert(cost.value > 0);

				int new_cost = current_cost + cost.value;

				// check if there already is a smaller path
				if ((m_min_target_distance > 0) &&
						(m_min_target_distance < new_cost)) {
					return false;
				}

				if ((g_pos2.totalcost < 0) ||
						(g_pos2.totalcost > new_cost)) {
					DEBUG_OUT(LVL "Pathfinder: updating path at: "<<
							PP(ipos2) << " from: " << g_pos2.totalcost << " to "<<
							new_cost << std::endl);
					if (updateAllCosts(ipos2, invert(directions[i]),
											new_cost, level)) {
						retval = true;
						}
					}
				else {
					DEBUG_OUT(LVL "Pathfinder:"
							" already found shorter path to: "
							<< PP(ipos2) << std::endl);
				}
			}
			else {
				DEBUG_OUT(LVL "Pathfinder:"
						" not moving to invalid direction: "
						<< PP(directions[i]) << std::endl);
			}
		}
	}
	return retval;
}

/******************************************************************************/
int Pathfinder::getXZManhattanDist(v3s16 pos)
{
	int min_x = MYMIN(pos.X, m_destination.X);
	int max_x = MYMAX(pos.X, m_destination.X);
	int min_z = MYMIN(pos.Z, m_destination.Z);
	int max_z = MYMAX(pos.Z, m_destination.Z);

	return (max_x - min_x) + (max_z - min_z);
}

/******************************************************************************/
v3s16 Pathfinder::getDirHeuristic(std::vector<v3s16> &directions, PathGridnode &g_pos)
{
	int   minscore = -1;
	v3s16 retdir   = v3s16(0, 0, 0);
	v3s16 srcpos = g_pos.pos;
	DEBUG_OUT("Pathfinder: remaining dirs at beginning:"
				<< directions.size() << std::endl);

	for (std::vector<v3s16>::iterator iter = directions.begin();
			iter != directions.end();
			++iter) {

		v3s16 pos1 = v3s16(srcpos.X + iter->X, 0, srcpos.Z+iter->Z);

		int cur_manhattan = getXZManhattanDist(pos1);
		PathCost cost    = g_pos.getCost(*iter);

		if (!cost.updated) {
			cost = calcCost(g_pos.pos, *iter);
			g_pos.setCost(*iter, cost);
		}

		if (cost.valid) {
			int score = cost.value + cur_manhattan;

			if ((minscore < 0)|| (score < minscore)) {
				minscore = score;
				retdir = *iter;
			}
		}
	}

	if (retdir != v3s16(0, 0, 0)) {
		for (std::vector<v3s16>::iterator iter = directions.begin();
					iter != directions.end();
					++iter) {
			if(*iter == retdir) {
				DEBUG_OUT("Pathfinder: removing return direction" << std::endl);
				directions.erase(iter);
				break;
			}
		}
	}
	else {
		DEBUG_OUT("Pathfinder: didn't find any valid direction clearing"
					<< std::endl);
		directions.clear();
	}
	DEBUG_OUT("Pathfinder: remaining dirs at end:" << directions.size()
				<< std::endl);
	return retdir;
}

/******************************************************************************/
bool Pathfinder::updateCostHeuristic(	v3s16 ipos,
										v3s16 srcdir,
										int current_cost,
										int level)
{

	PathGridnode &g_pos = getIndexElement(ipos);
	g_pos.totalcost = current_cost;
	g_pos.sourcedir = srcdir;

	level ++;

	//check if target has been found
	if (g_pos.target) {
		m_min_target_distance = current_cost;
		DEBUG_OUT(LVL " Pathfinder: target found!" << std::endl);
		return true;
	}

	bool retval = false;

	std::vector<v3s16> directions;

	directions.push_back(v3s16( 1, 0,  0));
	directions.push_back(v3s16(-1, 0,  0));
	directions.push_back(v3s16( 0, 0,  1));
	directions.push_back(v3s16( 0, 0, -1));

	v3s16 direction = getDirHeuristic(directions, g_pos);

	while (direction != v3s16(0, 0, 0) && (!retval)) {

		if (direction != srcdir) {
			PathCost cost = g_pos.getCost(direction);

			if (cost.valid) {
				direction.Y = cost.direction;

				v3s16 ipos2 = ipos + direction;

				if (!isValidIndex(ipos2)) {
					DEBUG_OUT(LVL " Pathfinder: " << PP(ipos2) <<
						" out of range, max=" << PP(m_limits.MaxEdge) << std::endl);
					direction = getDirHeuristic(directions, g_pos);
					continue;
				}

				PathGridnode &g_pos2 = getIndexElement(ipos2);

				if (!g_pos2.valid) {
					VERBOSE_TARGET << LVL "Pathfinder: no data for new position: "
												<< PP(ipos2) << std::endl;
					direction = getDirHeuristic(directions, g_pos);
					continue;
				}

				assert(cost.value > 0);

				int new_cost = current_cost + cost.value;

				// check if there already is a smaller path
				if ((m_min_target_distance > 0) &&
						(m_min_target_distance < new_cost)) {
					DEBUG_OUT(LVL "Pathfinder:"
							" already longer than best already found path "
							<< PP(ipos2) << std::endl);
					return false;
				}

				if ((g_pos2.totalcost < 0) ||
						(g_pos2.totalcost > new_cost)) {
					DEBUG_OUT(LVL "Pathfinder: updating path at: "<<
							PP(ipos2) << " from: " << g_pos2.totalcost << " to "<<
							new_cost << " srcdir=" <<
							PP(invert(direction))<< std::endl);
					if (updateCostHeuristic(ipos2, invert(direction),
											new_cost, level)) {
						retval = true;
						}
					}
				else {
					DEBUG_OUT(LVL "Pathfinder:"
							" already found shorter path to: "
							<< PP(ipos2) << std::endl);
				}
			}
			else {
				DEBUG_OUT(LVL "Pathfinder:"
						" not moving to invalid direction: "
						<< PP(direction) << std::endl);
			}
		}
		else {
			DEBUG_OUT(LVL "Pathfinder:"
							" skipping srcdir: "
							<< PP(direction) << std::endl);
		}
		direction = getDirHeuristic(directions, g_pos);
	}
	return retval;
}

/******************************************************************************/
void Pathfinder::buildPath(std::vector<v3s16> &path, v3s16 pos, int level)
{
	level ++;
	if (level > 700) {
		ERROR_TARGET
			<< LVL "Pathfinder: path is too long aborting" << std::endl;
		return;
	}

	PathGridnode &g_pos = getIndexElement(pos);
	if (!g_pos.valid) {
		ERROR_TARGET
			<< LVL "Pathfinder: invalid next pos detected aborting" << std::endl;
		return;
	}

	g_pos.is_element = true;

	//check if source reached
	if (g_pos.source) {
		path.push_back(pos);
		return;
	}

	buildPath(path, pos + g_pos.sourcedir, level);
	path.push_back(pos);
}

/******************************************************************************/
v3f Pathfinder::tov3f(v3s16 pos)
{
	return v3f(BS * pos.X, BS * pos.Y, BS * pos.Z);
}

#ifdef PATHFINDER_DEBUG

/******************************************************************************/
void Pathfinder::printCost()
{
	printCost(DIR_XP);
	printCost(DIR_XM);
	printCost(DIR_ZP);
	printCost(DIR_ZM);
}

/******************************************************************************/
void Pathfinder::printYdir()
{
	printYdir(DIR_XP);
	printYdir(DIR_XM);
	printYdir(DIR_ZP);
	printYdir(DIR_ZM);
}

/******************************************************************************/
void Pathfinder::printCost(PathDirections dir)
{
	std::cout << "Cost in direction: " << dirToName(dir) << std::endl;
	std::cout << std::setfill('-') << std::setw(80) << "-" << std::endl;
	std::cout << std::setfill(' ');
	for (int y = 0; y < m_max_index_y; y++) {

		std::cout << "Level: " << y << std::endl;

		std::cout << std::setw(4) << " " << "  ";
		for (int x = 0; x < m_max_index_x; x++) {
			std::cout << std::setw(4) << x;
		}
		std::cout << std::endl;

		for (int z = 0; z < m_max_index_z; z++) {
			std::cout << std::setw(4) << z <<": ";
			for (int x = 0; x < m_max_index_x; x++) {
				if (getIdxElem(x, y, z).directions[dir].valid)
					std::cout << std::setw(4)
						<< getIdxElem(x, y, z).directions[dir].value;
				else
					std::cout << std::setw(4) << "-";
				}
			std::cout << std::endl;
		}
		std::cout << std::endl;
	}
}

/******************************************************************************/
void Pathfinder::printYdir(PathDirections dir)
{
	std::cout << "Height difference in direction: " << dirToName(dir) << std::endl;
	std::cout << std::setfill('-') << std::setw(80) << "-" << std::endl;
	std::cout << std::setfill(' ');
	for (int y = 0; y < m_max_index_y; y++) {

		std::cout << "Level: " << y << std::endl;

		std::cout << std::setw(4) << " " << "  ";
		for (int x = 0; x < m_max_index_x; x++) {
			std::cout << std::setw(4) << x;
		}
		std::cout << std::endl;

		for (int z = 0; z < m_max_index_z; z++) {
			std::cout << std::setw(4) << z <<": ";
			for (int x = 0; x < m_max_index_x; x++) {
				if (getIdxElem(x, y, z).directions[dir].valid)
					std::cout << std::setw(4)
						<< getIdxElem(x, y, z).directions[dir].direction;
				else
					std::cout << std::setw(4) << "-";
				}
			std::cout << std::endl;
		}
		std::cout << std::endl;
	}
}

/******************************************************************************/
void Pathfinder::printType()
{
	std::cout << "Type of node:" << std::endl;
	std::cout << std::setfill('-') << std::setw(80) << "-" << std::endl;
	std::cout << std::setfill(' ');
	for (int y = 0; y < m_max_index_y; y++) {

		std::cout << "Level: " << y << std::endl;

		std::cout << std::setw(3) << " " << "  ";
		for (int x = 0; x < m_max_index_x; x++) {
			std::cout << std::setw(3) << x;
		}
		std::cout << std::endl;

		for (int z = 0; z < m_max_index_z; z++) {
			std::cout << std::setw(3) << z <<": ";
			for (int x = 0; x < m_max_index_x; x++) {
				char toshow = getIdxElem(x, y, z).type;
				std::cout << std::setw(3) << toshow;
			}
			std::cout << std::endl;
		}
		std::cout << std::endl;
	}
	std::cout << std::endl;
}

/******************************************************************************/
void Pathfinder::printPathLen()
{
	std::cout << "Pathlen:" << std::endl;
		std::cout << std::setfill('-') << std::setw(80) << "-" << std::endl;
		std::cout << std::setfill(' ');
		for (int y = 0; y < m_max_index_y; y++) {

			std::cout << "Level: " << y << std::endl;

			std::cout << std::setw(3) << " " << "  ";
			for (int x = 0; x < m_max_index_x; x++) {
				std::cout << std::setw(3) << x;
			}
			std::cout << std::endl;

			for (int z = 0; z < m_max_index_z; z++) {
				std::cout << std::setw(3) << z <<": ";
				for (int x = 0; x < m_max_index_x; x++) {
					std::cout << std::setw(3) << getIdxElem(x, y, z).totalcost;
				}
				std::cout << std::endl;
			}
			std::cout << std::endl;
		}
		std::cout << std::endl;
}

/******************************************************************************/
std::string Pathfinder::dirToName(PathDirections dir)
{
	switch (dir) {
	case DIR_XP:
		return "XP";
		break;
	case DIR_XM:
		return "XM";
		break;
	case DIR_ZP:
		return "ZP";
		break;
	case DIR_ZM:
		return "ZM";
		break;
	default:
		return "UKN";
	}
}

/******************************************************************************/
void Pathfinder::printPath(std::vector<v3s16> path)
{
	unsigned int current = 0;
	for (std::vector<v3s16>::iterator i = path.begin();
			i != path.end(); ++i) {
		std::cout << std::setw(3) << current << ":" << PP((*i)) << std::endl;
		current++;
	}
}

#endif


code:
/*
Minetest
Copyright (C) 2013 sapier, sapier at gmx dot net

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) 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 Lesser General Public License for more details.

You should have received a copy of the GNU Lesser General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/

#ifndef PATHFINDER_H_
#define PATHFINDER_H_

/******************************************************************************/
/* Includes                                                                   */
/******************************************************************************/
#include <vector>
#include "irr_v3d.h"

/******************************************************************************/
/* Forward declarations                                                       */
/******************************************************************************/

class ServerEnvironment;

/******************************************************************************/
/* Typedefs and macros                                                        */
/******************************************************************************/

typedef enum {
	DIR_XP,
	DIR_XM,
	DIR_ZP,
	DIR_ZM
} PathDirections;

/** List of supported algorithms */
typedef enum {
	PA_DIJKSTRA,           /**< Dijkstra shortest path algorithm             */
	PA_PLAIN,            /**< A* algorithm using heuristics to find a path */
	PA_PLAIN_NP          /**< A* algorithm without prefetching of map data */
} PathAlgorithm;

/******************************************************************************/
/* declarations                                                               */
/******************************************************************************/

/** c wrapper function to use from scriptapi */
std::vector<v3s16> get_path(ServerEnvironment *env,
							v3s16 source,
							v3s16 destination,
							unsigned int searchdistance,
							unsigned int max_jump,
							unsigned int max_drop,
							PathAlgorithm algo);

#endif /* PATHFINDER_H_ */

repo_name: pgimeno/minetest

license: unknown


code:
/*
Minetest
Copyright (C) 2010-2013 celeron55, Perttu Ahola <celeron55@gmail.com>

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) 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 Lesser General Public License for more details.

You should have received a copy of the GNU Lesser General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/

#include "player.h"

#include "threading/mutex_auto_lock.h"
#include "util/numeric.h"
#include "hud.h"
#include "constants.h"
#include "gamedef.h"
#include "settings.h"
#include "log.h"
#include "porting.h"  // strlcpy

Player::Player(const char *name, IItemDefManager *idef):
	inventory(idef),
	peer_id(PEER_ID_INEXISTENT),
	keyPressed(0),
// protected
	m_speed(0,0,0)
{
	strlcpy(m_name, name, PLAYERNAME_SIZE);

	inventory.clear();
	inventory.addList("main", PLAYER_INVENTORY_SIZE);
	inventory.addList("hand", 1);
	InventoryList *craft = inventory.addList("craft", 9);
	craft->setWidth(3);
	inventory.addList("craftpreview", 1);
	inventory.addList("craftresult", 1);
	inventory.setModified(false);

	// Can be redefined via Lua
	inventory_formspec = "size[8,7.5]"
		//"image[1,0.6;1,2;player.png]"
		"list[current_player;main;0,3.5;8,4;]"
		"list[current_player;craft;3,0;3,3;]"
		"listring[]"
		"list[current_player;craftpreview;7,1;1,1;]";

	// Initialize movement settings at default values, so movement can work
	// if the server fails to send them
	movement_acceleration_default   = 3    * BS;
	movement_acceleration_air       = 2    * BS;
	movement_acceleration_fast      = 10   * BS;
	movement_speed_walk             = 4    * BS;
	movement_speed_crouch           = 1.35 * BS;
	movement_speed_fast             = 20   * BS;
	movement_speed_climb            = 2    * BS;
	movement_speed_jump             = 6.5  * BS;
	movement_liquid_fluidity        = 1    * BS;
	movement_liquid_fluidity_smooth = 0.5  * BS;
	movement_liquid_sink            = 10   * BS;
	movement_gravity                = 9.81 * BS;
	local_animation_speed           = 0.0;

	hud_flags =
		HUD_FLAG_HOTBAR_VISIBLE    | HUD_FLAG_HEALTHBAR_VISIBLE |
		HUD_FLAG_CROSSHAIR_VISIBLE | HUD_FLAG_WIELDITEM_VISIBLE |
		HUD_FLAG_BREATHBAR_VISIBLE | HUD_FLAG_MINIMAP_VISIBLE;

	hud_hotbar_itemcount = HUD_HOTBAR_ITEMCOUNT_DEFAULT;
}

Player::~Player()
{
	clearHud();
}

u32 Player::addHud(HudElement *toadd)
{
	MutexAutoLock lock(m_mutex);

	u32 id = getFreeHudID();

	if (id < hud.size())
		hud[id] = toadd;
	else
		hud.push_back(toadd);

	return id;
}

HudElement* Player::getHud(u32 id)
{
	MutexAutoLock lock(m_mutex);

	if (id < hud.size())
		return hud[id];

	return NULL;
}

HudElement* Player::removeHud(u32 id)
{
	MutexAutoLock lock(m_mutex);

	HudElement* retval = NULL;
	if (id < hud.size()) {
		retval = hud[id];
		hud[id] = NULL;
	}
	return retval;
}

void Player::clearHud()
{
	MutexAutoLock lock(m_mutex);

	while(!hud.empty()) {
		delete hud.back();
		hud.pop_back();
	}
}


code:
/*
Minetest
Copyright (C) 2010-2013 celeron55, Perttu Ahola <celeron55@gmail.com>

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) 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 Lesser General Public License for more details.

You should have received a copy of the GNU Lesser General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/

#ifndef PLAYER_HEADER
#define PLAYER_HEADER

#include "irrlichttypes_bloated.h"
#include "inventory.h"
#include "threading/mutex.h"
#include <list>

#define PLAYERNAME_SIZE 20

#define PLAYERNAME_ALLOWED_CHARS "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_"
#define PLAYERNAME_ALLOWED_CHARS_USER_EXPL "'a' to 'z', 'A' to 'Z', '0' to '9', '-', '_'"

struct PlayerControl
{
	PlayerControl()
	{
		up = false;
		down = false;
		left = false;
		right = false;
		jump = false;
		aux1 = false;
		sneak = false;
		LMB = false;
		RMB = false;
		pitch = 0;
		yaw = 0;
		sidew_move_joystick_axis = .0f;
		forw_move_joystick_axis = .0f;
	}

	PlayerControl(
		bool a_up,
		bool a_down,
		bool a_left,
		bool a_right,
		bool a_jump,
		bool a_aux1,
		bool a_sneak,
		bool a_zoom,
		bool a_LMB,
		bool a_RMB,
		float a_pitch,
		float a_yaw,
		float a_sidew_move_joystick_axis,
		float a_forw_move_joystick_axis
	)
	{
		up = a_up;
		down = a_down;
		left = a_left;
		right = a_right;
		jump = a_jump;
		aux1 = a_aux1;
		sneak = a_sneak;
		zoom = a_zoom;
		LMB = a_LMB;
		RMB = a_RMB;
		pitch = a_pitch;
		yaw = a_yaw;
		sidew_move_joystick_axis = a_sidew_move_joystick_axis;
		forw_move_joystick_axis = a_forw_move_joystick_axis;
	}
	bool up;
	bool down;
	bool left;
	bool right;
	bool jump;
	bool aux1;
	bool sneak;
	bool zoom;
	bool LMB;
	bool RMB;
	float pitch;
	float yaw;
	float sidew_move_joystick_axis;
	float forw_move_joystick_axis;
};

class Map;
struct CollisionInfo;
struct HudElement;
class Environment;

// IMPORTANT:
// Do *not* perform an assignment or copy operation on a Player or
// RemotePlayer object!  This will copy the lock held for HUD synchronization
class Player
{
public:

	Player(const char *name, IItemDefManager *idef);
	virtual ~Player() = 0;

	virtual void move(f32 dtime, Environment *env, f32 pos_max_d)
	{}
	virtual void move(f32 dtime, Environment *env, f32 pos_max_d,
			std::vector<CollisionInfo> *collision_info)
	{}

	v3f getSpeed()
	{
		return m_speed;
	}

	void setSpeed(v3f speed)
	{
		m_speed = speed;
	}

	const char *getName() const { return m_name; }

	u32 getFreeHudID()
	{
		size_t size = hud.size();
		for (size_t i = 0; i != size; i++) {
			if (!hud[i])
				return i;
		}
		return size;
	}

	v3f eye_offset_first;
	v3f eye_offset_third;

	Inventory inventory;

	f32 movement_acceleration_default;
	f32 movement_acceleration_air;
	f32 movement_acceleration_fast;
	f32 movement_speed_walk;
	f32 movement_speed_crouch;
	f32 movement_speed_fast;
	f32 movement_speed_climb;
	f32 movement_speed_jump;
	f32 movement_liquid_fluidity;
	f32 movement_liquid_fluidity_smooth;
	f32 movement_liquid_sink;
	f32 movement_gravity;

	v2s32 local_animations[4];
	float local_animation_speed;

	u16 peer_id;

	std::string inventory_formspec;

	PlayerControl control;
	const PlayerControl& getPlayerControl() { return control; }

	u32 keyPressed;

	HudElement* getHud(u32 id);
	u32         addHud(HudElement* hud);
	HudElement* removeHud(u32 id);
	void        clearHud();

	u32 hud_flags;
	s32 hud_hotbar_itemcount;
protected:
	char m_name[PLAYERNAME_SIZE];
	v3f m_speed;

	std::vector<HudElement *> hud;
private:
	// Protect some critical areas
	// hud for example can be modified by EmergeThread
	// and ServerThread
	Mutex m_mutex;
};

#endif


code:
/*
Minetest
Copyright (C) 2013 celeron55, Perttu Ahola <celeron55@gmail.com>

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) 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 Lesser General Public License for more details.

You should have received a copy of the GNU Lesser General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/

/*
	Random portability stuff

	See comments in porting.h
*/

#include "porting.h"

#if defined(__FreeBSD__)
	#include <sys/types.h>
	#include <sys/sysctl.h>
#elif defined(_WIN32)
	#include <windows.h>
	#include <wincrypt.h>
	#include <algorithm>
	#include <shlwapi.h>
#endif
#if !defined(_WIN32)
	#include <unistd.h>
	#include <sys/utsname.h>
#endif
#if defined(__hpux)
	#define _PSTAT64
	#include <sys/pstat.h>
#endif
#if !defined(_WIN32) && !defined(__APPLE__) && \
	!defined(__ANDROID__) && !defined(SERVER)
	#define XORG_USED
#endif
#ifdef XORG_USED
	#include <X11/Xlib.h>
	#include <X11/Xutil.h>
#endif

#include "config.h"
#include "debug.h"
#include "filesys.h"
#include "log.h"
#include "util/string.h"
#include "settings.h"
#include <list>

namespace porting
{

/*
	Signal handler (grabs Ctrl-C on POSIX systems)
*/

bool g_killed = false;

bool * signal_handler_killstatus(void)
{
	return &g_killed;
}

#if !defined(_WIN32) // POSIX
	#include <signal.h>

void signal_handler(int sig)
{
	if (!g_killed) {
		if (sig == SIGINT) {
			dstream << "INFO: signal_handler(): "
				<< "Ctrl-C pressed, shutting down." << std::endl;
		} else if (sig == SIGTERM) {
			dstream << "INFO: signal_handler(): "
				<< "got SIGTERM, shutting down." << std::endl;
		}

		// Comment out for less clutter when testing scripts
		/*dstream << "INFO: sigint_handler(): "
				<< "Printing debug stacks" << std::endl;
		debug_stacks_print();*/

		g_killed = true;
	} else {
		(void)signal(sig, SIG_DFL);
	}
}

void signal_handler_init(void)
{
	(void)signal(SIGINT, signal_handler);
	(void)signal(SIGTERM, signal_handler);
}

#else // _WIN32
	#include <signal.h>

BOOL WINAPI event_handler(DWORD sig)
{
	switch (sig) {
	case CTRL_C_EVENT:
	case CTRL_CLOSE_EVENT:
	case CTRL_LOGOFF_EVENT:
	case CTRL_SHUTDOWN_EVENT:
		if (!g_killed) {
			dstream << "INFO: event_handler(): "
				<< "Ctrl+C, Close Event, Logoff Event or Shutdown Event,"
				" shutting down." << std::endl;
			g_killed = true;
		} else {
			(void)signal(SIGINT, SIG_DFL);
		}
		break;
	case CTRL_BREAK_EVENT:
		break;
	}

	return TRUE;
}

void signal_handler_init(void)
{
	SetConsoleCtrlHandler((PHANDLER_ROUTINE)event_handler, TRUE);
}

#endif

/*
	Path mangler
*/

// Default to RUN_IN_PLACE style relative paths
std::string path_share = "..";
std::string path_user = "..";
std::string path_locale = path_share + DIR_DELIM + "locale";
std::string path_cache = path_user + DIR_DELIM + "cache";

std::string getDataPath(const char *subpath)
{
	return path_share + DIR_DELIM + subpath;
}

void pathRemoveFile(char *path, char delim)
{
	// Remove filename and path delimiter
	int i;
	for(i = strlen(path)-1; i>=0; i--)
	{
		if(path[i] == delim)
			break;
	}
	path[i] = 0;
}

bool detectMSVCBuildDir(const std::string &path)
{
	const char *ends[] = {
		"bin\\Release",
		"bin\\MinSizeRel",
		"bin\\RelWithDebInfo",
		"bin\\Debug",
		"bin\\Build",
		NULL
	};
	return (removeStringEnd(path, ends) != "");
}

std::string get_sysinfo()
{
#ifdef _WIN32

	std::ostringstream oss;
	LPSTR filePath = new char[MAX_PATH];
	UINT blockSize;
	VS_FIXEDFILEINFO *fixedFileInfo;

	GetSystemDirectoryA(filePath, MAX_PATH);
	PathAppendA(filePath, "kernel32.dll");

	DWORD dwVersionSize = GetFileVersionInfoSizeA(filePath, NULL);
	LPBYTE lpVersionInfo = new BYTE[dwVersionSize];

	GetFileVersionInfoA(filePath, 0, dwVersionSize, lpVersionInfo);
	VerQueryValueA(lpVersionInfo, "\\", (LPVOID *)&fixedFileInfo, &blockSize);

	oss << "Windows/"
		<< HIWORD(fixedFileInfo->dwProductVersionMS) << '.' // Major
		<< LOWORD(fixedFileInfo->dwProductVersionMS) << '.' // Minor
		<< HIWORD(fixedFileInfo->dwProductVersionLS) << ' '; // Build

	#ifdef _WIN64
	oss << "x86_64";
	#else
	BOOL is64 = FALSE;
	if (IsWow64Process(GetCurrentProcess(), &is64) && is64)
		oss << "x86_64"; // 32-bit app on 64-bit OS
	else
		oss << "x86";
	#endif

	delete[] lpVersionInfo;
	delete[] filePath;

	return oss.str();
#else
	struct utsname osinfo;
	uname(&osinfo);
	return std::string(osinfo.sysname) + "/"
		+ osinfo.release + " " + osinfo.machine;
#endif
}

bool getCurrentWorkingDir(char *buf, size_t len)
{
#ifdef _WIN32
	DWORD ret = GetCurrentDirectory(len, buf);
	return (ret != 0) && (ret <= len);
#else
	return getcwd(buf, len);
#endif
}

bool getExecPathFromProcfs(char *buf, size_t buflen)
{
#ifndef _WIN32
	buflen--;

	ssize_t len;
	if ((len = readlink("/proc/self/exe",     buf, buflen)) == -1 &&
		(len = readlink("/proc/curproc/file", buf, buflen)) == -1 &&
		(len = readlink("/proc/curproc/exe",  buf, buflen)) == -1)
		return false;

	buf[len] = '\0';
	return true;
#else
	return false;
#endif
}

//// Windows
#if defined(_WIN32)

bool getCurrentExecPath(char *buf, size_t len)
{
	DWORD written = GetModuleFileNameA(NULL, buf, len);
	if (written == 0 || written == len)
		return false;

	return true;
}

//// Linux
#elif defined(__linux__)

bool getCurrentExecPath(char *buf, size_t len)
{
	if (!getExecPathFromProcfs(buf, len))
		return false;

	return true;
}

//// Mac OS X, Darwin
#elif defined(__APPLE__)

bool getCurrentExecPath(char *buf, size_t len)
{
	uint32_t lenb = (uint32_t)len;
	if (_NSGetExecutablePath(buf, &lenb) == -1)
		return false;

	return true;
}

//// FreeBSD, NetBSD, DragonFlyBSD
#elif defined(__FreeBSD__) || defined(__NetBSD__) || defined(__DragonFly__)

bool getCurrentExecPath(char *buf, size_t len)
{
	// Try getting path from procfs first, since valgrind
	// doesn't work with the latter
	if (getExecPathFromProcfs(buf, len))
		return true;

	int mib[4];

	mib[0] = CTL_KERN;
	mib[1] = KERN_PROC;
	mib[2] = KERN_PROC_PATHNAME;
	mib[3] = -1;

	if (sysctl(mib, 4, buf, &len, NULL, 0) == -1)
		return false;

	return true;
}

//// Solaris
#elif defined(__sun) || defined(sun)

bool getCurrentExecPath(char *buf, size_t len)
{
	const char *exec = getexecname();
	if (exec == NULL)
		return false;

	if (strlcpy(buf, exec, len) >= len)
		return false;

	return true;
}

// HP-UX
#elif defined(__hpux)

bool getCurrentExecPath(char *buf, size_t len)
{
	struct pst_status psts;

	if (pstat_getproc(&psts, sizeof(psts), 0, getpid()) == -1)
		return false;

	if (pstat_getpathname(buf, len, &psts.pst_fid_text) == -1)
		return false;

	return true;
}

#else

bool getCurrentExecPath(char *buf, size_t len)
{
	return false;
}

#endif

//// Windows
#if defined(_WIN32)

bool setSystemPaths()
{
	char buf[BUFSIZ];

	// Find path of executable and set path_share relative to it
	FATAL_ERROR_IF(!getCurrentExecPath(buf, sizeof(buf)),
		"Failed to get current executable path");
	pathRemoveFile(buf, '\\');

	// Use ".\bin\.."
	path_share = std::string(buf) + "\\..";

	// Use "C:\Documents and Settings\user\Application Data\<PROJECT_NAME>"
	DWORD len = GetEnvironmentVariable("APPDATA", buf, sizeof(buf));
	FATAL_ERROR_IF(len == 0 || len > sizeof(buf), "Failed to get APPDATA");

	path_user = std::string(buf) + DIR_DELIM + PROJECT_NAME;
	return true;
}

//// Linux
#elif defined(__linux__)

bool setSystemPaths()
{
	char buf[BUFSIZ];

	if (!getCurrentExecPath(buf, sizeof(buf))) {
#ifdef __ANDROID__
		errorstream << "Unable to read bindir "<< std::endl;
#else
		FATAL_ERROR("Unable to read bindir");
#endif
		return false;
	}

	pathRemoveFile(buf, '/');
	std::string bindir(buf);

	// Find share directory from these.
	// It is identified by containing the subdirectory "builtin".
	std::list<std::string> trylist;
	std::string static_sharedir = STATIC_SHAREDIR;
	if (static_sharedir != "" && static_sharedir != ".")
		trylist.push_back(static_sharedir);

	trylist.push_back(bindir + DIR_DELIM ".." DIR_DELIM "share"
		DIR_DELIM + PROJECT_NAME);
	trylist.push_back(bindir + DIR_DELIM "..");

#ifdef __ANDROID__
	trylist.push_back(path_user);
#endif

	for (std::list<std::string>::const_iterator
			i = trylist.begin(); i != trylist.end(); ++i) {
		const std::string &trypath = *i;
		if (!fs::PathExists(trypath) ||
			!fs::PathExists(trypath + DIR_DELIM + "builtin")) {
			warningstream << "system-wide share not found at \""
					<< trypath << "\""<< std::endl;
			continue;
		}

		// Warn if was not the first alternative
		if (i != trylist.begin()) {
			warningstream << "system-wide share found at \""
					<< trypath << "\"" << std::endl;
		}

		path_share = trypath;
		break;
	}

#ifndef __ANDROID__
	path_user = std::string(getenv("HOME")) + DIR_DELIM "."
		+ PROJECT_NAME;
#endif

	return true;
}

//// Mac OS X
#elif defined(__APPLE__)

bool setSystemPaths()
{
	CFBundleRef main_bundle = CFBundleGetMainBundle();
	CFURLRef resources_url = CFBundleCopyResourcesDirectoryURL(main_bundle);
	char path[PATH_MAX];
	if (CFURLGetFileSystemRepresentation(resources_url,
			TRUE, (UInt8 *)path, PATH_MAX)) {
		path_share = std::string(path);
	} else {
		warningstream << "Could not determine bundle resource path" << std::endl;
	}
	CFRelease(resources_url);

	path_user = std::string(getenv("HOME"))
		+ "/Library/Application Support/"
		+ PROJECT_NAME;
	return true;
}

#else

bool setSystemPaths()
{
	path_share = STATIC_SHAREDIR;
	path_user  = std::string(getenv("HOME")) + DIR_DELIM "."
		+ lowercase(PROJECT_NAME);
	return true;
}

#endif

void migrateCachePath()
{
	const std::string local_cache_path = path_user + DIR_DELIM + "cache";

	// Delete tmp folder if it exists (it only ever contained
	// a temporary ogg file, which is no longer used).
	if (fs::PathExists(local_cache_path + DIR_DELIM + "tmp"))
		fs::RecursiveDelete(local_cache_path + DIR_DELIM + "tmp");

	// Bail if migration impossible
	if (path_cache == local_cache_path || !fs::PathExists(local_cache_path)
			|| fs::PathExists(path_cache)) {
		return;
	}
	if (!fs::Rename(local_cache_path, path_cache)) {
		errorstream << "Failed to migrate local cache path "
			"to system path!" << std::endl;
	}
}

void initializePaths()
{
#if RUN_IN_PLACE
	char buf[BUFSIZ];

	infostream << "Using relative paths (RUN_IN_PLACE)" << std::endl;

	bool success =
		getCurrentExecPath(buf, sizeof(buf)) ||
		getExecPathFromProcfs(buf, sizeof(buf));

	if (success) {
		pathRemoveFile(buf, DIR_DELIM_CHAR);
		std::string execpath(buf);

		path_share = execpath + DIR_DELIM "..";
		path_user  = execpath + DIR_DELIM "..";

		if (detectMSVCBuildDir(execpath)) {
			path_share += DIR_DELIM "..";
			path_user  += DIR_DELIM "..";
		}
	} else {
		errorstream << "Failed to get paths by executable location, "
			"trying cwd" << std::endl;

		if (!getCurrentWorkingDir(buf, sizeof(buf)))
			FATAL_ERROR("Ran out of methods to get paths");

		size_t cwdlen = strlen(buf);
		if (cwdlen >= 1 && buf[cwdlen - 1] == DIR_DELIM_CHAR) {
			cwdlen--;
			buf[cwdlen] = '\0';
		}

		if (cwdlen >= 4 && !strcmp(buf + cwdlen - 4, DIR_DELIM "bin"))
			pathRemoveFile(buf, DIR_DELIM_CHAR);

		std::string execpath(buf);

		path_share = execpath;
		path_user  = execpath;
	}
	path_cache = path_user + DIR_DELIM + "cache";
#else
	infostream << "Using system-wide paths (NOT RUN_IN_PLACE)" << std::endl;

	if (!setSystemPaths())
		errorstream << "Failed to get one or more system-wide path" << std::endl;

	// Initialize path_cache
	// First try $XDG_CACHE_HOME/PROJECT_NAME
	const char *cache_dir = getenv("XDG_CACHE_HOME");
	const char *home_dir = getenv("HOME");
	if (cache_dir) {
		path_cache = std::string(cache_dir) + DIR_DELIM + PROJECT_NAME;
	} else if (home_dir) {
		// Then try $HOME/.cache/PROJECT_NAME
		path_cache = std::string(home_dir) + DIR_DELIM + ".cache"
			+ DIR_DELIM + PROJECT_NAME;
	} else {
		// If neither works, use $PATH_USER/cache
		path_cache = path_user + DIR_DELIM + "cache";
	}
	// Migrate cache folder to new location if possible
	migrateCachePath();
#endif

	infostream << "Detected share path: " << path_share << std::endl;
	infostream << "Detected user path: " << path_user << std::endl;
	infostream << "Detected cache path: " << path_cache << std::endl;

#ifdef USE_GETTEXT
	bool found_localedir = false;
#  ifdef STATIC_LOCALEDIR
	if (STATIC_LOCALEDIR[0] && fs::PathExists(STATIC_LOCALEDIR)) {
		found_localedir = true;
		path_locale = STATIC_LOCALEDIR;
		infostream << "Using locale directory " << STATIC_LOCALEDIR << std::endl;
	} else {
		path_locale = getDataPath("locale");
		if (fs::PathExists(path_locale)) {
			found_localedir = true;
			infostream << "Using in-place locale directory " << path_locale
				<< " even though a static one was provided "
				<< "(RUN_IN_PLACE or CUSTOM_LOCALEDIR)." << std::endl;
		}
	}
#  else
	path_locale = getDataPath("locale");
	if (fs::PathExists(path_locale)) {
		found_localedir = true;
	}
#  endif
	if (!found_localedir) {
		warningstream << "Couldn't find a locale directory!" << std::endl;
	}
#endif  // USE_GETTEXT
}

void setXorgClassHint(const video::SExposedVideoData &video_data,
	const std::string &name)
{
#ifdef XORG_USED
	if (video_data.OpenGLLinux.X11Display == NULL)
		return;

	XClassHint *classhint = XAllocClassHint();
	classhint->res_name  = (char *)name.c_str();
	classhint->res_class = (char *)name.c_str();

	XSetClassHint((Display *)video_data.OpenGLLinux.X11Display,
		video_data.OpenGLLinux.X11Window, classhint);
	XFree(classhint);
#endif
}

bool setWindowIcon(IrrlichtDevice *device)
{
#if defined(XORG_USED)
#	if RUN_IN_PLACE
	return setXorgWindowIconFromPath(device,
			path_share + "/misc/" PROJECT_NAME "-xorg-icon-128.png");
#	else
	// We have semi-support for reading in-place data if we are
	// compiled with RUN_IN_PLACE. Don't break with this and
	// also try the path_share location.
	return
		setXorgWindowIconFromPath(device,
			ICON_DIR "/hicolor/128x128/apps/" PROJECT_NAME ".png") ||
		setXorgWindowIconFromPath(device,
			path_share + "/misc/" PROJECT_NAME "-xorg-icon-128.png");
#	endif
#elif defined(_WIN32)
	const video::SExposedVideoData exposedData = device->getVideoDriver()->getExposedVideoData();
	HWND hWnd; // Window handle

	switch (device->getVideoDriver()->getDriverType()) {
	case video::EDT_DIRECT3D8:
		hWnd = reinterpret_cast<HWND>(exposedData.D3D8.HWnd);
		break;
	case video::EDT_DIRECT3D9:
		hWnd = reinterpret_cast<HWND>(exposedData.D3D9.HWnd);
		break;
	case video::EDT_OPENGL:
		hWnd = reinterpret_cast<HWND>(exposedData.OpenGLWin32.HWnd);
		break;
	default:
		return false;
	}

	// Load the ICON from resource file
	const HICON hicon = LoadIcon(
		GetModuleHandle(NULL),
		MAKEINTRESOURCE(130) // The ID of the ICON defined in winresource.rc
	);

	if (hicon) {
		SendMessage(hWnd, WM_SETICON, ICON_BIG, reinterpret_cast<LPARAM>(hicon));
		SendMessage(hWnd, WM_SETICON, ICON_SMALL, reinterpret_cast<LPARAM>(hicon));
		return true;
	}
	return false;
#else
	return false;
#endif
}

bool setXorgWindowIconFromPath(IrrlichtDevice *device,
	const std::string &icon_file)
{
#ifdef XORG_USED

	video::IVideoDriver *v_driver = device->getVideoDriver();

	video::IImageLoader *image_loader = NULL;
	u32 cnt = v_driver->getImageLoaderCount();
	for (u32 i = 0; i < cnt; i++) {
		if (v_driver->getImageLoader(i)->isALoadableFileExtension(icon_file.c_str())) {
			image_loader = v_driver->getImageLoader(i);
			break;
		}
	}

	if (!image_loader) {
		warningstream << "Could not find image loader for file '"
			<< icon_file << "'" << std::endl;
		return false;
	}

	io::IReadFile *icon_f = device->getFileSystem()->createAndOpenFile(icon_file.c_str());

	if (!icon_f) {
		warningstream << "Could not load icon file '"
			<< icon_file << "'" << std::endl;
		return false;
	}

	video::IImage *img = image_loader->loadImage(icon_f);

	if (!img) {
		warningstream << "Could not load icon file '"
			<< icon_file << "'" << std::endl;
		icon_f->drop();
		return false;
	}

	u32 height = img->getDimension().Height;
	u32 width = img->getDimension().Width;

	size_t icon_buffer_len = 2 + height * width;
	long *icon_buffer = new long[icon_buffer_len];

	icon_buffer[0] = width;
	icon_buffer[1] = height;

	for (u32 x = 0; x < width; x++) {
		for (u32 y = 0; y < height; y++) {
			video::SColor col = img->getPixel(x, y);
			long pixel_val = 0;
			pixel_val |= (u8)col.getAlpha() << 24;
			pixel_val |= (u8)col.getRed() << 16;
			pixel_val |= (u8)col.getGreen() << 8;
			pixel_val |= (u8)col.getBlue();
			icon_buffer[2 + x + y * width] = pixel_val;
		}
	}

	img->drop();
	icon_f->drop();

	const video::SExposedVideoData &video_data = v_driver->getExposedVideoData();

	Display *x11_dpl = (Display *)video_data.OpenGLLinux.X11Display;

	if (x11_dpl == NULL) {
		warningstream << "Could not find x11 display for setting its icon."
			<< std::endl;
		delete [] icon_buffer;
		return false;
	}

	Window x11_win = (Window)video_data.OpenGLLinux.X11Window;

	Atom net_wm_icon = XInternAtom(x11_dpl, "_NET_WM_ICON", False);
	Atom cardinal = XInternAtom(x11_dpl, "CARDINAL", False);
	XChangeProperty(x11_dpl, x11_win,
		net_wm_icon, cardinal, 32,
		PropModeReplace, (const unsigned char *)icon_buffer,
		icon_buffer_len);

	delete [] icon_buffer;

#endif
	return true;
}

////
//// Video/Display Information (Client-only)
////

#ifndef SERVER

static irr::IrrlichtDevice *device;

void initIrrlicht(irr::IrrlichtDevice *device_)
{
	device = device_;
}

v2u32 getWindowSize()
{
	return device->getVideoDriver()->getScreenSize();
}

std::vector<core::vector3d<u32> > getSupportedVideoModes()
{
	IrrlichtDevice *nulldevice = createDevice(video::EDT_NULL);
	sanity_check(nulldevice != NULL);

	std::vector<core::vector3d<u32> > mlist;
	video::IVideoModeList *modelist = nulldevice->getVideoModeList();

	u32 num_modes = modelist->getVideoModeCount();
	for (u32 i = 0; i != num_modes; i++) {
		core::dimension2d<u32> mode_res = modelist->getVideoModeResolution(i);
		s32 mode_depth = modelist->getVideoModeDepth(i);
		mlist.push_back(core::vector3d<u32>(mode_res.Width, mode_res.Height, mode_depth));
	}

	nulldevice->drop();

	return mlist;
}

std::vector<irr::video::E_DRIVER_TYPE> getSupportedVideoDrivers()
{
	std::vector<irr::video::E_DRIVER_TYPE> drivers;

	for (int i = 0; i != irr::video::EDT_COUNT; i++) {
		if (irr::IrrlichtDevice::isDriverSupported((irr::video::E_DRIVER_TYPE)i))
			drivers.push_back((irr::video::E_DRIVER_TYPE)i);
	}

	return drivers;
}

const char *getVideoDriverName(irr::video::E_DRIVER_TYPE type)
{
	static const char *driver_ids[] = {
		"null",
		"software",
		"burningsvideo",
		"direct3d8",
		"direct3d9",
		"opengl",
		"ogles1",
		"ogles2",
	};

	return driver_ids[type];
}

const char *getVideoDriverFriendlyName(irr::video::E_DRIVER_TYPE type)
{
	static const char *driver_names[] = {
		"NULL Driver",
		"Software Renderer",
		"Burning's Video",
		"Direct3D 8",
		"Direct3D 9",
		"OpenGL",
		"OpenGL ES1",
		"OpenGL ES2",
	};

	return driver_names[type];
}

#	ifndef __ANDROID__
#		ifdef XORG_USED

static float calcDisplayDensity()
{
	const char *current_display = getenv("DISPLAY");

	if (current_display != NULL) {
		Display *x11display = XOpenDisplay(current_display);

		if (x11display != NULL) {
			/* try x direct */
			float dpi_height = floor(DisplayHeight(x11display, 0) /
							(DisplayHeightMM(x11display, 0) * 0.039370) + 0.5);
			float dpi_width = floor(DisplayWidth(x11display, 0) /
							(DisplayWidthMM(x11display, 0) * 0.039370) + 0.5);

			XCloseDisplay(x11display);

			return std::max(dpi_height,dpi_width) / 96.0;
		}
	}

	/* return manually specified dpi */
	return g_settings->getFloat("screen_dpi")/96.0;
}

float getDisplayDensity()
{
	static float cached_display_density = calcDisplayDensity();
	return cached_display_density;
}

#		else // XORG_USED
float getDisplayDensity()
{
	return g_settings->getFloat("screen_dpi")/96.0;
}
#		endif // XORG_USED

v2u32 getDisplaySize()
{
	IrrlichtDevice *nulldevice = createDevice(video::EDT_NULL);

	core::dimension2d<u32> deskres = nulldevice->getVideoModeList()->getDesktopResolution();
	nulldevice -> drop();

	return deskres;
}
#	endif // __ANDROID__
#endif // SERVER

////
//// OS-specific Secure Random
////

#ifdef WIN32

bool secure_rand_fill_buf(void *buf, size_t len)
{
	HCRYPTPROV wctx;

	if (!CryptAcquireContext(&wctx, NULL, NULL, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT))
		return false;

	CryptGenRandom(wctx, len, (BYTE *)buf);
	CryptReleaseContext(wctx, 0);
	return true;
}

#else

bool secure_rand_fill_buf(void *buf, size_t len)
{
	// N.B.  This function checks *only* for /dev/urandom, because on most
	// common OSes it is non-blocking, whereas /dev/random is blocking, and it
	// is exceptionally uncommon for there to be a situation where /dev/random
	// exists but /dev/urandom does not.  This guesswork is necessary since
	// random devices are not covered by any POSIX standard...
	FILE *fp = fopen("/dev/urandom", "rb");
	if (!fp)
		return false;

	bool success = fread(buf, len, 1, fp) == 1;

	fclose(fp);
	return success;
}

#endif

void attachOrCreateConsole(void)
{
#ifdef _WIN32
	static bool consoleAllocated = false;
	const bool redirected = (_fileno(stdout) == -2 || _fileno(stdout) == -1); // If output is redirected to e.g a file
	if (!consoleAllocated && redirected && (AttachConsole(ATTACH_PARENT_PROCESS) || AllocConsole())) {
		freopen("CONOUT$", "w", stdout);
		freopen("CONOUT$", "w", stderr);
		consoleAllocated = true;
	}
#endif
}

// Load performance counter frequency only once at startup
#ifdef _WIN32

inline double get_perf_freq()
{
	LARGE_INTEGER freq;
	QueryPerformanceFrequency(&freq);
	return freq.QuadPart;
}

double perf_freq = get_perf_freq();

#endif

} //namespace porting


code:
/*
Minetest
Copyright (C) 2013 celeron55, Perttu Ahola <celeron55@gmail.com>

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) 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 Lesser General Public License for more details.

You should have received a copy of the GNU Lesser General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/

/*
	Random portability stuff
*/

#ifndef PORTING_HEADER
#define PORTING_HEADER

#ifdef _WIN32
	#ifdef _WIN32_WINNT
		#undef _WIN32_WINNT
	#endif
	#define _WIN32_WINNT 0x0501 // We need to do this before any other headers
		// because those might include sdkddkver.h which defines _WIN32_WINNT if not already set
#endif

#include <string>
#include <vector>
#include "irrlicht.h"
#include "irrlichttypes.h" // u32
#include "irrlichttypes_extrabloated.h"
#include "debug.h"
#include "constants.h"
#include "gettime.h"
#include "threads.h"

#ifdef _MSC_VER
	#define SWPRINTF_CHARSTRING L"%S"
#else
	#define SWPRINTF_CHARSTRING L"%s"
#endif

//currently not needed
//template<typename T> struct alignment_trick { char c; T member; };
//#define ALIGNOF(type) offsetof (alignment_trick<type>, member)

#ifdef _WIN32
	#include <windows.h>

	#define sleep_ms(x) Sleep(x)
#else
	#include <unistd.h>
	#include <stdint.h> //for uintptr_t

	// Use standard Posix macro for Linux
	#if (defined(linux) || defined(__linux)) && !defined(__linux__)
		#define __linux__
	#endif
	#if (defined(__linux__) || defined(__GNU__)) && !defined(_GNU_SOURCE)
		#define _GNU_SOURCE
	#endif

	#define sleep_ms(x) usleep(x*1000)
#endif

#ifdef _MSC_VER
	#define ALIGNOF(x) __alignof(x)
	#define strtok_r(x, y, z) strtok_s(x, y, z)
	#define strtof(x, y) (float)strtod(x, y)
	#define strtoll(x, y, z) _strtoi64(x, y, z)
	#define strtoull(x, y, z) _strtoui64(x, y, z)
	#define strcasecmp(x, y) stricmp(x, y)
	#define strncasecmp(x, y, n) strnicmp(x, y, n)
#else
	#define ALIGNOF(x) __alignof__(x)
#endif

#ifdef __MINGW32__
	#define strtok_r(x, y, z) mystrtok_r(x, y, z)
#endif

// strlcpy is missing from glibc.  thanks a lot, drepper.
// strlcpy is also missing from AIX and HP-UX because they aim to be weird.
// We can't simply alias strlcpy to MSVC's strcpy_s, since strcpy_s by
// default raises an assertion error and aborts the program if the buffer is
// too small.
#if defined(__FreeBSD__) || defined(__NetBSD__)    || \
	defined(__OpenBSD__) || defined(__DragonFly__) || \
	defined(__APPLE__)   ||                           \
	defined(__sun)       || defined(sun)           || \
	defined(__QNX__)     || defined(__QNXNTO__)
	#define HAVE_STRLCPY
#endif

// So we need to define our own.
#ifndef HAVE_STRLCPY
	#define strlcpy(d, s, n) mystrlcpy(d, s, n)
#endif

#define PADDING(x, y) ((ALIGNOF(y) - ((uintptr_t)(x) & (ALIGNOF(y) - 1))) & (ALIGNOF(y) - 1))

#if defined(__APPLE__)
	#include <mach-o/dyld.h>
	#include <CoreFoundation/CoreFoundation.h>
#endif

#ifndef _WIN32 // Posix
	#include <sys/time.h>
	#include <time.h>
	#if defined(__MACH__) && defined(__APPLE__)
		#include <mach/clock.h>
		#include <mach/mach.h>
	#endif
#endif

namespace porting
{

/*
	Signal handler (grabs Ctrl-C on POSIX systems)
*/

void signal_handler_init(void);
// Returns a pointer to a bool.
// When the bool is true, program should quit.
bool * signal_handler_killstatus(void);

/*
	Path of static data directory.
*/
extern std::string path_share;

/*
	Directory for storing user data. Examples:
	Windows: "C:\Documents and Settings\user\Application Data\<PROJECT_NAME>"
	Linux: "~/.<PROJECT_NAME>"
	Mac: "~/Library/Application Support/<PROJECT_NAME>"
*/
extern std::string path_user;

/*
	Path to gettext locale files
*/
extern std::string path_locale;

/*
	Path to directory for storing caches.
*/
extern std::string path_cache;

/*
	Get full path of stuff in data directory.
	Example: "stone.png" -> "../data/stone.png"
*/
std::string getDataPath(const char *subpath);

/*
	Move cache folder from path_user to the
	system cache location if possible.
*/
void migrateCachePath();

/*
	Initialize path_*.
*/
void initializePaths();

/*
	Return system information
	e.g. "Linux/3.12.7 x86_64"
*/
std::string get_sysinfo();

void initIrrlicht(irr::IrrlichtDevice * );

// Monotonic counter getters.

#ifdef _WIN32 // Windows

extern double perf_freq;

inline u64 os_get_time(double mult)
{
	LARGE_INTEGER t;
	QueryPerformanceCounter(&t);
	return static_cast<double>(t.QuadPart) / (perf_freq / mult);
}

// Resolution is <1us.
inline u64 getTimeS() { return os_get_time(1); }
inline u64 getTimeMs() { return os_get_time(1000); }
inline u64 getTimeUs() { return os_get_time(1000*1000); }
inline u64 getTimeNs() { return os_get_time(1000*1000*1000); }

#else // Posix

inline void os_get_clock(struct timespec *ts)
{
#if defined(__MACH__) && defined(__APPLE__)
// From http://stackoverflow.com/questions/5167269/clock-gettime-alternative-in-mac-os-x
// OS X does not have clock_gettime, use clock_get_time
	clock_serv_t cclock;
	mach_timespec_t mts;
	host_get_clock_service(mach_host_self(), CALENDAR_CLOCK, &cclock);
	clock_get_time(cclock, &mts);
	mach_port_deallocate(mach_task_self(), cclock);
	ts->tv_sec = mts.tv_sec;
	ts->tv_nsec = mts.tv_nsec;
#elif defined(CLOCK_MONOTONIC_RAW)
	clock_gettime(CLOCK_MONOTONIC_RAW, ts);
#elif defined(_POSIX_MONOTONIC_CLOCK)
	clock_gettime(CLOCK_MONOTONIC, ts);
#else
	struct timeval tv;
	gettimeofday(&tv, NULL);
	TIMEVAL_TO_TIMESPEC(&tv, ts);
#endif
}

inline u64 getTimeS()
{
	struct timespec ts;
	os_get_clock(&ts);
	return ts.tv_sec;
}

inline u64 getTimeMs()
{
	struct timespec ts;
	os_get_clock(&ts);
	return ts.tv_sec * 1000 + ts.tv_nsec / 1000000;
}

inline u64 getTimeUs()
{
	struct timespec ts;
	os_get_clock(&ts);
	return ts.tv_sec * 1000000 + ts.tv_nsec / 1000;
}

inline u64 getTimeNs()
{
	struct timespec ts;
	os_get_clock(&ts);
	return ts.tv_sec * 1000000000 + ts.tv_nsec;
}

#endif

inline u64 getTime(TimePrecision prec)
{
	switch (prec) {
	case PRECISION_SECONDS: return getTimeS();
	case PRECISION_MILLI:   return getTimeMs();
	case PRECISION_MICRO:   return getTimeUs();
	case PRECISION_NANO:    return getTimeNs();
	}
	FATAL_ERROR("Called getTime with invalid time precision");
}

/**
 * Delta calculation function arguments.
 * @param old_time_ms old time for delta calculation
 * @param new_time_ms new time for delta calculation
 * @return positive delta value
 */
inline u64 getDeltaMs(u64 old_time_ms, u64 new_time_ms)
{
	if (new_time_ms >= old_time_ms) {
		return (new_time_ms - old_time_ms);
	} else {
		return (old_time_ms - new_time_ms);
	}
}

#ifndef SERVER
float getDisplayDensity();

v2u32 getDisplaySize();
v2u32 getWindowSize();

std::vector<core::vector3d<u32> > getSupportedVideoModes();
std::vector<irr::video::E_DRIVER_TYPE> getSupportedVideoDrivers();
const char *getVideoDriverName(irr::video::E_DRIVER_TYPE type);
const char *getVideoDriverFriendlyName(irr::video::E_DRIVER_TYPE type);
#endif

inline const char *getPlatformName()
{
	return
#if defined(ANDROID)
	"Android"
#elif defined(__linux__)
	"Linux"
#elif defined(_WIN32) || defined(_WIN64)
	"Windows"
#elif defined(__DragonFly__) || defined(__FreeBSD__) || \
		defined(__NetBSD__) || defined(__OpenBSD__)
	"BSD"
#elif defined(__APPLE__) && defined(__MACH__)
	#if TARGET_OS_MAC
		"OSX"
	#elif TARGET_OS_IPHONE
		"iOS"
	#else
		"Apple"
	#endif
#elif defined(_AIX)
	"AIX"
#elif defined(__hpux)
	"HP-UX"
#elif defined(__sun) || defined(sun)
	#if defined(__SVR4)
		"Solaris"
	#else
		"SunOS"
	#endif
#elif defined(__CYGWIN__)
	"Cygwin"
#elif defined(__unix__) || defined(__unix)
	#if defined(_POSIX_VERSION)
		"Posix"
	#else
		"Unix"
	#endif
#else
	"?"
#endif
	;
}

void setXorgClassHint(const video::SExposedVideoData &video_data,
	const std::string &name);

bool setWindowIcon(IrrlichtDevice *device);

bool setXorgWindowIconFromPath(IrrlichtDevice *device,
	const std::string &icon_file);

// This only needs to be called at the start of execution, since all future
// threads in the process inherit this exception handler
void setWin32ExceptionHandler();

bool secure_rand_fill_buf(void *buf, size_t len);

// This attaches to the parents process console, or creates a new one if it doesnt exist.
void attachOrCreateConsole(void);
} // namespace porting

#ifdef __ANDROID__
#include "porting_android.h"
#endif

#endif // PORTING_HEADER


code:
/*
Minetest
Copyright (C) 2014 celeron55, Perttu Ahola <celeron55@gmail.com>

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) 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 Lesser General Public License for more details.

You should have received a copy of the GNU Lesser General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/

#ifndef __ANDROID__
#error This file may only be compiled for android!
#endif

#include "util/numeric.h"
#include "porting.h"
#include "porting_android.h"
#include "threading/thread.h"
#include "config.h"
#include "filesys.h"
#include "log.h"

#include <sstream>
#include <exception>
#include <stdlib.h>

#ifdef GPROF
#include "prof.h"
#endif

extern int main(int argc, char *argv[]);

void android_main(android_app *app)
{
	int retval = 0;
	porting::app_global = app;

	Thread::setName("Main");

	try {
		app_dummy();
		char *argv[] = {strdup(PROJECT_NAME), NULL};
		main(ARRLEN(argv) - 1, argv);
		free(argv[0]);
	} catch (std::exception &e) {
		errorstream << "Uncaught exception in main thread: " << e.what() << std::endl;
		retval = -1;
	} catch (...) {
		errorstream << "Uncaught exception in main thread!" << std::endl;
		retval = -1;
	}

	porting::cleanupAndroid();
	infostream << "Shutting down." << std::endl;
	exit(retval);
}

/* handler for finished message box input */
/* Intentionally NOT in namespace porting */
/* TODO this doesn't work as expected, no idea why but there's a workaround   */
/* for it right now */
extern "C" {
	JNIEXPORT void JNICALL Java_net_minetest_MtNativeActivity_putMessageBoxResult(
			JNIEnv * env, jclass thiz, jstring text)
	{
		errorstream << "Java_net_minetest_MtNativeActivity_putMessageBoxResult got: "
				<< std::string((const char*)env->GetStringChars(text,0))
				<< std::endl;
	}
}

namespace porting {

std::string path_storage = DIR_DELIM "sdcard" DIR_DELIM;

android_app* app_global;
JNIEnv*      jnienv;
jclass       nativeActivity;

jclass findClass(std::string classname)
{
	if (jnienv == 0) {
		return 0;
	}

	jclass nativeactivity = jnienv->FindClass("android/app/NativeActivity");
	jmethodID getClassLoader =
			jnienv->GetMethodID(nativeactivity,"getClassLoader",
					"()Ljava/lang/ClassLoader;");
	jobject cls =
			jnienv->CallObjectMethod(app_global->activity->clazz, getClassLoader);
	jclass classLoader = jnienv->FindClass("java/lang/ClassLoader");
	jmethodID findClass =
			jnienv->GetMethodID(classLoader, "loadClass",
					"(Ljava/lang/String;)Ljava/lang/Class;");
	jstring strClassName =
			jnienv->NewStringUTF(classname.c_str());
	return (jclass) jnienv->CallObjectMethod(cls, findClass, strClassName);
}

void copyAssets()
{
	jmethodID assetcopy = jnienv->GetMethodID(nativeActivity,"copyAssets","()V");

	if (assetcopy == 0) {
		assert("porting::copyAssets unable to find copy assets method" == 0);
	}

	jnienv->CallVoidMethod(app_global->activity->clazz, assetcopy);
}

void initAndroid()
{
	porting::jnienv = NULL;
	JavaVM *jvm = app_global->activity->vm;
	JavaVMAttachArgs lJavaVMAttachArgs;
	lJavaVMAttachArgs.version = JNI_VERSION_1_6;
	lJavaVMAttachArgs.name = PROJECT_NAME_C "NativeThread";
	lJavaVMAttachArgs.group = NULL;
#ifdef NDEBUG
	// This is a ugly hack as arm v7a non debuggable builds crash without this
	// printf ... if someone finds out why please fix it!
	infostream << "Attaching native thread. " << std::endl;
#endif
	if ( jvm->AttachCurrentThread(&porting::jnienv, &lJavaVMAttachArgs) == JNI_ERR) {
		errorstream << "Failed to attach native thread to jvm" << std::endl;
		exit(-1);
	}

	nativeActivity = findClass("net/minetest/minetest/MtNativeActivity");
	if (nativeActivity == 0) {
		errorstream <<
			"porting::initAndroid unable to find java native activity class" <<
			std::endl;
	}

#ifdef GPROF
	/* in the start-up code */
	__android_log_print(ANDROID_LOG_ERROR, PROJECT_NAME_C,
			"Initializing GPROF profiler");
	monstartup("libminetest.so");
#endif
}

void cleanupAndroid()
{

#ifdef GPROF
	errorstream << "Shutting down GPROF profiler" << std::endl;
	setenv("CPUPROFILE", (path_user + DIR_DELIM + "gmon.out").c_str(), 1);
	moncleanup();
#endif

	JavaVM *jvm = app_global->activity->vm;
	jvm->DetachCurrentThread();
}

static std::string javaStringToUTF8(jstring js)
{
	std::string str;
	// Get string as a UTF-8 c-string
	const char *c_str = jnienv->GetStringUTFChars(js, NULL);
	// Save it
	str = c_str;
	// And free the c-string
	jnienv->ReleaseStringUTFChars(js, c_str);
	return str;
}

// Calls static method if obj is NULL
static std::string getAndroidPath(jclass cls, jobject obj, jclass cls_File,
		jmethodID mt_getAbsPath, const char *getter)
{
	// Get getter method
	jmethodID mt_getter;
	if (obj)
		mt_getter = jnienv->GetMethodID(cls, getter,
				"()Ljava/io/File;");
	else
		mt_getter = jnienv->GetStaticMethodID(cls, getter,
				"()Ljava/io/File;");

	// Call getter
	jobject ob_file;
	if (obj)
		ob_file = jnienv->CallObjectMethod(obj, mt_getter);
	else
		ob_file = jnienv->CallStaticObjectMethod(cls, mt_getter);

	// Call getAbsolutePath
	jstring js_path = (jstring) jnienv->CallObjectMethod(ob_file,
			mt_getAbsPath);

	return javaStringToUTF8(js_path);
}

void initializePathsAndroid()
{
	// Get Environment class
	jclass cls_Env = jnienv->FindClass("android/os/Environment");
	// Get File class
	jclass cls_File = jnienv->FindClass("java/io/File");
	// Get getAbsolutePath method
	jmethodID mt_getAbsPath = jnienv->GetMethodID(cls_File,
				"getAbsolutePath", "()Ljava/lang/String;");

	path_cache   = getAndroidPath(nativeActivity, app_global->activity->clazz,
			cls_File, mt_getAbsPath, "getCacheDir");
	path_storage = getAndroidPath(cls_Env, NULL, cls_File, mt_getAbsPath,
			"getExternalStorageDirectory");
	path_user    = path_storage + DIR_DELIM + PROJECT_NAME_C;
	path_share   = path_storage + DIR_DELIM + PROJECT_NAME_C;

	migrateCachePath();
}

void showInputDialog(const std::string& acceptButton, const  std::string& hint,
		const std::string& current, int editType)
{
	jmethodID showdialog = jnienv->GetMethodID(nativeActivity,"showDialog",
		"(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;I)V");

	if (showdialog == 0) {
		assert("porting::showInputDialog unable to find java show dialog method" == 0);
	}

	jstring jacceptButton = jnienv->NewStringUTF(acceptButton.c_str());
	jstring jhint         = jnienv->NewStringUTF(hint.c_str());
	jstring jcurrent      = jnienv->NewStringUTF(current.c_str());
	jint    jeditType     = editType;

	jnienv->CallVoidMethod(app_global->activity->clazz, showdialog,
			jacceptButton, jhint, jcurrent, jeditType);
}

int getInputDialogState()
{
	jmethodID dialogstate = jnienv->GetMethodID(nativeActivity,
			"getDialogState", "()I");

	if (dialogstate == 0) {
		assert("porting::getInputDialogState unable to find java dialog state method" == 0);
	}

	return jnienv->CallIntMethod(app_global->activity->clazz, dialogstate);
}

std::string getInputDialogValue()
{
	jmethodID dialogvalue = jnienv->GetMethodID(nativeActivity,
			"getDialogValue", "()Ljava/lang/String;");

	if (dialogvalue == 0) {
		assert("porting::getInputDialogValue unable to find java dialog value method" == 0);
	}

	jobject result = jnienv->CallObjectMethod(app_global->activity->clazz,
			dialogvalue);

	const char* javachars = jnienv->GetStringUTFChars((jstring) result,0);
	std::string text(javachars);
	jnienv->ReleaseStringUTFChars((jstring) result, javachars);

	return text;
}

#ifndef SERVER
float getDisplayDensity()
{
	static bool firstrun = true;
	static float value = 0;

	if (firstrun) {
		jmethodID getDensity = jnienv->GetMethodID(nativeActivity, "getDensity",
					"()F");

		if (getDensity == 0) {
			assert("porting::getDisplayDensity unable to find java getDensity method" == 0);
		}

		value = jnienv->CallFloatMethod(app_global->activity->clazz, getDensity);
		firstrun = false;
	}
	return value;
}

v2u32 getDisplaySize()
{
	static bool firstrun = true;
	static v2u32 retval;

	if (firstrun) {
		jmethodID getDisplayWidth = jnienv->GetMethodID(nativeActivity,
				"getDisplayWidth", "()I");

		if (getDisplayWidth == 0) {
			assert("porting::getDisplayWidth unable to find java getDisplayWidth method" == 0);
		}

		retval.X = jnienv->CallIntMethod(app_global->activity->clazz,
				getDisplayWidth);

		jmethodID getDisplayHeight = jnienv->GetMethodID(nativeActivity,
				"getDisplayHeight", "()I");

		if (getDisplayHeight == 0) {
			assert("porting::getDisplayHeight unable to find java getDisplayHeight method" == 0);
		}

		retval.Y = jnienv->CallIntMethod(app_global->activity->clazz,
				getDisplayHeight);

		firstrun = false;
	}
	return retval;
}
#endif // ndef SERVER
}


code:
/*
Minetest
Copyright (C) 2014 celeron55, Perttu Ahola <celeron55@gmail.com>

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) 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 Lesser General Public License for more details.

You should have received a copy of the GNU Lesser General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#ifndef __PORTING_ANDROID_H__
#define __PORTING_ANDROID_H__

#ifndef __ANDROID__
#error this include has to be included on android port only!
#endif

#include <jni.h>
#include <android_native_app_glue.h>
#include <android/log.h>

#include <string>

namespace porting {
/** java app **/
extern android_app *app_global;

/** java <-> c++ interaction interface **/
extern JNIEnv *jnienv;

/**
 * do initialization required on android only
 */
void initAndroid();
void cleanupAndroid();

/**
 * Initializes path_* variables for Android
 * @param env Android JNI environment
 */
void initializePathsAndroid();

/**
 * use java function to copy media from assets to external storage
 */
void copyAssets();

/**
 * show text input dialog in java
 * @param acceptButton text to display on accept button
 * @param hint hint to show
 * @param current initial value to display
 * @param editType type of texfield
 * (1==multiline text input; 2==single line text input; 3=password field)
 */
void showInputDialog(const std::string& acceptButton,
		const  std::string& hint, const std::string& current, int editType);

/**
 * WORKAROUND for not working callbacks from java -> c++
 * get current state of input dialog
 */
int getInputDialogState();

/**
 * WORKAROUND for not working callbacks from java -> c++
 * get text in current input dialog
 */
std::string getInputDialogValue();

}

#endif


code:
/*
Minetest
Copyright (C) 2015 celeron55, Perttu Ahola <celeron55@gmail.com>

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) 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 Lesser General Public License for more details.

You should have received a copy of the GNU Lesser General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/

#include "profiler.h"

static Profiler main_profiler;
Profiler *g_profiler = &main_profiler;
ScopeProfiler::ScopeProfiler(
		Profiler *profiler, const std::string &name, ScopeProfilerType type)
    : m_profiler(profiler), m_name(name), m_timer(NULL), m_type(type)
{
	if (m_profiler)
		m_timer = new TimeTaker(m_name);
}

ScopeProfiler::~ScopeProfiler()
{
	if (!m_timer)
		return;

	float duration_ms = m_timer->stop(true);
	float duration = duration_ms / 1000.0;
	if (m_profiler) {
		switch (m_type) {
		case SPT_ADD:
			m_profiler->add(m_name, duration);
			break;
		case SPT_AVG:
			m_profiler->avg(m_name, duration);
			break;
		case SPT_GRAPH_ADD:
			m_profiler->graphAdd(m_name, duration);
			break;
		}
	}
	delete m_timer;
}


code:
/*
Minetest
Copyright (C) 2013 celeron55, Perttu Ahola <celeron55@gmail.com>

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) 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 Lesser General Public License for more details.

You should have received a copy of the GNU Lesser General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/

#ifndef PROFILER_HEADER
#define PROFILER_HEADER

#include "irrlichttypes.h"
#include <string>
#include <map>

#include "threading/mutex.h"
#include "threading/mutex_auto_lock.h"
#include "util/timetaker.h"
#include "util/numeric.h"      // paging()
#include "debug.h"             // assert()

#define MAX_PROFILER_TEXT_ROWS 20

// Global profiler
class Profiler;
extern Profiler *g_profiler;

/*
	Time profiler
*/

class Profiler
{
public:
	Profiler()
	{
	}

	void add(const std::string &name, float value)
	{
		MutexAutoLock lock(m_mutex);
		{
			/* No average shall have been used; mark add used as -2 */
			std::map<std::string, int>::iterator n = m_avgcounts.find(name);
			if(n == m_avgcounts.end())
				m_avgcounts[name] = -2;
			else{
				if(n->second == -1)
					n->second = -2;
				assert(n->second == -2);
			}
		}
		{
			std::map<std::string, float>::iterator n = m_data.find(name);
			if(n == m_data.end())
				m_data[name] = value;
			else
				n->second += value;
		}
	}

	void avg(const std::string &name, float value)
	{
		MutexAutoLock lock(m_mutex);
		int &count = m_avgcounts[name];

		assert(count != -2);
		count = MYMAX(count, 0) + 1;
		m_data[name] += value;
	}

	void clear()
	{
		MutexAutoLock lock(m_mutex);
		for(std::map<std::string, float>::iterator
				i = m_data.begin();
				i != m_data.end(); ++i)
		{
			i->second = 0;
		}
		m_avgcounts.clear();
	}

	void print(std::ostream &o)
	{
		printPage(o, 1, 1);
	}

	float getValue(const std::string &name) const
	{
		std::map<std::string, float>::const_iterator numerator = m_data.find(name);
		if (numerator == m_data.end())
			return 0.f;

		std::map<std::string, int>::const_iterator denominator = m_avgcounts.find(name);
		if (denominator != m_avgcounts.end()){
			if (denominator->second >= 1)
				return numerator->second / denominator->second;
		}

		return numerator->second;
	}

	void printPage(std::ostream &o, u32 page, u32 pagecount)
	{
		MutexAutoLock lock(m_mutex);

		u32 minindex, maxindex;
		paging(m_data.size(), page, pagecount, minindex, maxindex);

		for (std::map<std::string, float>::const_iterator i = m_data.begin();
				i != m_data.end(); ++i) {
			if (maxindex == 0)
				break;
			maxindex--;

			if (minindex != 0) {
				minindex--;
				continue;
			}

			int avgcount = 1;
			std::map<std::string, int>::const_iterator n = m_avgcounts.find(i->first);
			if (n != m_avgcounts.end()) {
				if(n->second >= 1)
					avgcount = n->second;
			}
			o << "  " << i->first << ": ";
			s32 clampsize = 40;
			s32 space = clampsize - i->first.size();
			for(s32 j = 0; j < space; j++) {
				if (j % 2 == 0 && j < space - 1)
					o << "-";
				else
					o << " ";
			}
			o << (i->second / avgcount);
			o << std::endl;
		}
	}

	typedef std::map<std::string, float> GraphValues;

	void graphAdd(const std::string &id, float value)
	{
		MutexAutoLock lock(m_mutex);
		std::map<std::string, float>::iterator i =
				m_graphvalues.find(id);
		if(i == m_graphvalues.end())
			m_graphvalues[id] = value;
		else
			i->second += value;
	}
	void graphGet(GraphValues &result)
	{
		MutexAutoLock lock(m_mutex);
		result = m_graphvalues;
		m_graphvalues.clear();
	}

	void remove(const std::string& name)
	{
		MutexAutoLock lock(m_mutex);
		m_avgcounts.erase(name);
		m_data.erase(name);
	}

private:
	Mutex m_mutex;
	std::map<std::string, float> m_data;
	std::map<std::string, int> m_avgcounts;
	std::map<std::string, float> m_graphvalues;
};

enum ScopeProfilerType{
	SPT_ADD,
	SPT_AVG,
	SPT_GRAPH_ADD
};

class ScopeProfiler
{
public:
	ScopeProfiler(Profiler *profiler, const std::string &name,
			ScopeProfilerType type = SPT_ADD);
	~ScopeProfiler();
private:
	Profiler *m_profiler;
	std::string m_name;
	TimeTaker *m_timer;
	enum ScopeProfilerType m_type;
};

#endif


code:
/*
Minetest
Copyright (C) 2013 celeron55, Perttu Ahola <celeron55@gmail.com>

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) 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 Lesser General Public License for more details.

You should have received a copy of the GNU Lesser General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/

#include "quicktune.h"
#include "threading/mutex.h"
#include "threading/mutex_auto_lock.h"
#include "util/string.h"

std::string QuicktuneValue::getString()
{
	switch(type){
	case QVT_NONE:
		return "(none)";
	case QVT_FLOAT:
		return ftos(value_QVT_FLOAT.current);
	}
	return "<invalid type>";
}
void QuicktuneValue::relativeAdd(float amount)
{
	switch(type){
	case QVT_NONE:
		break;
	case QVT_FLOAT:
		value_QVT_FLOAT.current += amount * (value_QVT_FLOAT.max - value_QVT_FLOAT.min);
		if(value_QVT_FLOAT.current > value_QVT_FLOAT.max)
			value_QVT_FLOAT.current = value_QVT_FLOAT.max;
		if(value_QVT_FLOAT.current < value_QVT_FLOAT.min)
			value_QVT_FLOAT.current = value_QVT_FLOAT.min;
		break;
	}
}

static std::map<std::string, QuicktuneValue> g_values;
static std::vector<std::string> g_names;
Mutex *g_mutex = NULL;

static void makeMutex()
{
	if(!g_mutex){
		g_mutex = new Mutex();
	}
}

std::vector<std::string> getQuicktuneNames()
{
	return g_names;
}

QuicktuneValue getQuicktuneValue(const std::string &name)
{
	makeMutex();
	MutexAutoLock lock(*g_mutex);
	std::map<std::string, QuicktuneValue>::iterator i = g_values.find(name);
	if(i == g_values.end()){
		QuicktuneValue val;
		val.type = QVT_NONE;
		return val;
	}
	return i->second;
}

void setQuicktuneValue(const std::string &name, const QuicktuneValue &val)
{
	makeMutex();
	MutexAutoLock lock(*g_mutex);
	g_values[name] = val;
	g_values[name].modified = true;
}

void updateQuicktuneValue(const std::string &name, QuicktuneValue &val)
{
	makeMutex();
	MutexAutoLock lock(*g_mutex);
	std::map<std::string, QuicktuneValue>::iterator i = g_values.find(name);
	if(i == g_values.end()){
		g_values[name] = val;
		g_names.push_back(name);
		return;
	}
	QuicktuneValue &ref = i->second;
	if(ref.modified)
		val = ref;
	else{
		ref = val;
		ref.modified = false;
	}
}