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 CONTENT_SAO_HEADER
#define CONTENT_SAO_HEADER

#include <util/numeric.h>
#include "serverobject.h"
#include "itemgroup.h"
#include "object_properties.h"

class UnitSAO: public ServerActiveObject
{
public:
	UnitSAO(ServerEnvironment *env, v3f pos);
	virtual ~UnitSAO() {}

	virtual void setYaw(const float yaw) { m_yaw = yaw; }
	float getYaw() const { return m_yaw; };
	f32 getRadYaw() const { return m_yaw * core::DEGTORAD; }
	// Deprecated
	f32 getRadYawDep() const { return (m_yaw + 90.) * core::DEGTORAD; }

	s16 getHP() const { return m_hp; }
	// Use a function, if isDead can be defined by other conditions
	bool isDead() const { return m_hp == 0; }

	bool isAttached() const;
	void setArmorGroups(const ItemGroupList &armor_groups);
	const ItemGroupList &getArmorGroups();
	void setAnimation(v2f frame_range, float frame_speed, float frame_blend, bool frame_loop);
	void getAnimation(v2f *frame_range, float *frame_speed, float *frame_blend, bool *frame_loop);
	void setBonePosition(const std::string &bone, v3f position, v3f rotation);
	void getBonePosition(const std::string &bone, v3f *position, v3f *rotation);
	void setAttachment(int parent_id, const std::string &bone, v3f position, v3f rotation);
	void getAttachment(int *parent_id, std::string *bone, v3f *position, v3f *rotation);
	void addAttachmentChild(int child_id);
	void removeAttachmentChild(int child_id);
	const UNORDERED_SET<int> &getAttachmentChildIds();
	ObjectProperties* accessObjectProperties();
	void notifyObjectPropertiesModified();
protected:
	s16 m_hp;
	float m_yaw;

	bool m_properties_sent;
	struct ObjectProperties m_prop;

	ItemGroupList m_armor_groups;
	bool m_armor_groups_sent;

	v2f m_animation_range;
	float m_animation_speed;
	float m_animation_blend;
	bool m_animation_loop;
	bool m_animation_sent;

	// Stores position and rotation for each bone name
	UNORDERED_MAP<std::string, core::vector2d<v3f> > m_bone_position;
	bool m_bone_position_sent;

	int m_attachment_parent_id;
	UNORDERED_SET<int> m_attachment_child_ids;
	std::string m_attachment_bone;
	v3f m_attachment_position;
	v3f m_attachment_rotation;
	bool m_attachment_sent;
};

/*
	LuaEntitySAO needs some internals exposed.
*/

class LuaEntitySAO : public UnitSAO
{
public:
	LuaEntitySAO(ServerEnvironment *env, v3f pos,
	             const std::string &name, const std::string &state);
	~LuaEntitySAO();
	ActiveObjectType getType() const
	{ return ACTIVEOBJECT_TYPE_LUAENTITY; }
	ActiveObjectType getSendType() const
	{ return ACTIVEOBJECT_TYPE_GENERIC; }
	virtual void addedToEnvironment(u32 dtime_s);
	static ServerActiveObject* create(ServerEnvironment *env, v3f pos,
			const std::string &data);
	void step(float dtime, bool send_recommended);
	std::string getClientInitializationData(u16 protocol_version);
	void getStaticData(std::string *result) const;
	int punch(v3f dir,
			const ToolCapabilities *toolcap=NULL,
			ServerActiveObject *puncher=NULL,
			float time_from_last_punch=1000000);
	void rightClick(ServerActiveObject *clicker);
	void setPos(const v3f &pos);
	void moveTo(v3f pos, bool continuous);
	float getMinimumSavedMovement();
	std::string getDescription();
	void setHP(s16 hp);
	s16 getHP() const;
	/* LuaEntitySAO-specific */
	void setVelocity(v3f velocity);
	v3f getVelocity();
	void setAcceleration(v3f acceleration);
	v3f getAcceleration();

	void setTextureMod(const std::string &mod);
	std::string getTextureMod() const;
	void setSprite(v2s16 p, int num_frames, float framelength,
			bool select_horiz_by_yawpitch);
	std::string getName();
	bool getCollisionBox(aabb3f *toset) const;
	bool collideWithObjects() const;
private:
	std::string getPropertyPacket();
	void sendPosition(bool do_interpolate, bool is_movement_end);

	std::string m_init_name;
	std::string m_init_state;
	bool m_registered;

	v3f m_velocity;
	v3f m_acceleration;

	float m_last_sent_yaw;
	v3f m_last_sent_position;
	v3f m_last_sent_velocity;
	float m_last_sent_position_timer;
	float m_last_sent_move_precision;
	std::string m_current_texture_modifier;
};

/*
	PlayerSAO needs some internals exposed.
*/

class LagPool
{
	float m_pool;
	float m_max;
public:
	LagPool(): m_pool(15), m_max(15)
	{}

	void setMax(float new_max)
	{
		m_max = new_max;
		if(m_pool > new_max)
			m_pool = new_max;
	}

	void add(float dtime)
	{
		m_pool -= dtime;
		if(m_pool < 0)
			m_pool = 0;
	}

	void empty()
	{
		m_pool = m_max;
	}

	bool grab(float dtime)
	{
		if(dtime <= 0)
			return true;
		if(m_pool + dtime > m_max)
			return false;
		m_pool += dtime;
		return true;
	}
};

typedef UNORDERED_MAP<std::string, std::string> PlayerAttributes;
class RemotePlayer;

class PlayerSAO : public UnitSAO
{
public:
	PlayerSAO(ServerEnvironment *env_, RemotePlayer *player_, u16 peer_id_, bool is_singleplayer);
	~PlayerSAO();
	ActiveObjectType getType() const
	{ return ACTIVEOBJECT_TYPE_PLAYER; }
	ActiveObjectType getSendType() const
	{ return ACTIVEOBJECT_TYPE_GENERIC; }
	std::string getDescription();

	/*
		Active object <-> environment interface
	*/

	void addedToEnvironment(u32 dtime_s);
	void removingFromEnvironment();
	bool isStaticAllowed() const { return false; }
	std::string getClientInitializationData(u16 protocol_version);
	void getStaticData(std::string *result) const;
	void step(float dtime, bool send_recommended);
	void setBasePosition(const v3f &position);
	void setPos(const v3f &pos);
	void moveTo(v3f pos, bool continuous);
	void setYaw(const float yaw);
	// Data should not be sent at player initialization
	void setYawAndSend(const float yaw);
	void setPitch(const float pitch);
	// Data should not be sent at player initialization
	void setPitchAndSend(const float pitch);
	f32 getPitch() const { return m_pitch; }
	f32 getRadPitch() const { return m_pitch * core::DEGTORAD; }
	// Deprecated
	f32 getRadPitchDep() const { return -1.0 * m_pitch * core::DEGTORAD; }
	void setFov(const float pitch);
	f32 getFov() const { return m_fov; }
	void setWantedRange(const s16 range);
	s16 getWantedRange() const { return m_wanted_range; }

	/*
		Interaction interface
	*/

	int punch(v3f dir,
		const ToolCapabilities *toolcap,
		ServerActiveObject *puncher,
		float time_from_last_punch);
	void rightClick(ServerActiveObject *clicker) {}
	void setHP(s16 hp);
	void setHPRaw(s16 hp) { m_hp = hp; }
	s16 readDamage();
	u16 getBreath() const { return m_breath; }
	void setBreath(const u16 breath, bool send = true);

	/*
		Inventory interface
	*/

	Inventory* getInventory();
	const Inventory* getInventory() const;
	InventoryLocation getInventoryLocation() const;
	std::string getWieldList() const;
	ItemStack getWieldedItem() const;
	ItemStack getWieldedItemOrHand() const;
	bool setWieldedItem(const ItemStack &item);
	int getWieldIndex() const;
	void setWieldIndex(int i);

	/*
		Modding interface
	*/
	inline void setExtendedAttribute(const std::string &attr, const std::string &value)
	{
		m_extra_attributes[attr] = value;
		m_extended_attributes_modified = true;
	}

	inline bool getExtendedAttribute(const std::string &attr, std::string *value)
	{
		if (m_extra_attributes.find(attr) == m_extra_attributes.end())
			return false;

		*value = m_extra_attributes[attr];
		return true;
	}

	inline void removeExtendedAttribute(const std::string &attr)
	{
		PlayerAttributes::iterator it = m_extra_attributes.find(attr);
		if (it == m_extra_attributes.end())
			return;

		m_extra_attributes.erase(it);
		m_extended_attributes_modified = true;
	}

	inline const PlayerAttributes &getExtendedAttributes()
	{
		return m_extra_attributes;
	}

	inline bool extendedAttributesModified() const
	{
		return m_extended_attributes_modified;
	}

	inline void setExtendedAttributeModified(bool v)
	{
		m_extended_attributes_modified = v;
	}

	/*
		PlayerSAO-specific
	*/

	void disconnected();

	RemotePlayer *getPlayer() { return m_player; }
	u16 getPeerID() const { return m_peer_id; }

	// Cheat prevention

	v3f getLastGoodPosition() const
	{
		return m_last_good_position;
	}
	float resetTimeFromLastPunch()
	{
		float r = m_time_from_last_punch;
		m_time_from_last_punch = 0.0;
		return r;
	}
	void noCheatDigStart(v3s16 p)
	{
		m_nocheat_dig_pos = p;
		m_nocheat_dig_time = 0;
	}
	v3s16 getNoCheatDigPos()
	{
		return m_nocheat_dig_pos;
	}
	float getNoCheatDigTime()
	{
		return m_nocheat_dig_time;
	}
	void noCheatDigEnd()
	{
		m_nocheat_dig_pos = v3s16(32767, 32767, 32767);
	}
	LagPool& getDigPool()
	{
		return m_dig_pool;
	}
	// Returns true if cheated
	bool checkMovementCheat();

	// Other

	void updatePrivileges(const std::set<std::string> &privs,
			bool is_singleplayer)
	{
		m_privs = privs;
		m_is_singleplayer = is_singleplayer;
	}

	bool getCollisionBox(aabb3f *toset) const;
	bool collideWithObjects() const { return true; }

	void finalize(RemotePlayer *player, const std::set<std::string> &privs);

	v3f getEyePosition() const { return m_base_position + getEyeOffset(); }
	v3f getEyeOffset() const;

private:
	std::string getPropertyPacket();
	void unlinkPlayerSessionAndSave();

	RemotePlayer *m_player;
	u16 m_peer_id;
	Inventory *m_inventory;
	s16 m_damage;

	// Cheat prevention
	LagPool m_dig_pool;
	LagPool m_move_pool;
	v3f m_last_good_position;
	float m_time_from_last_teleport;
	float m_time_from_last_punch;
	v3s16 m_nocheat_dig_pos;
	float m_nocheat_dig_time;

	// Timers
	IntervalLimiter m_breathing_interval;
	IntervalLimiter m_drowning_interval;
	IntervalLimiter m_node_hurt_interval;

	int m_wield_index;
	bool m_position_not_sent;

	// Cached privileges for enforcement
	std::set<std::string> m_privs;
	bool m_is_singleplayer;

	u16 m_breath;
	f32 m_pitch;
	f32 m_fov;
	s16 m_wanted_range;

	PlayerAttributes m_extra_attributes;
	bool m_extended_attributes_modified;
public:
	float m_physics_override_speed;
	float m_physics_override_jump;
	float m_physics_override_gravity;
	bool m_physics_override_sneak;
	bool m_physics_override_sneak_glitch;
	bool m_physics_override_new_move;
	bool m_physics_override_sent;
};

#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 <vector>
#include <iostream>
#include <sstream>

#include "convert_json.h"
#include "mods.h"
#include "config.h"
#include "log.h"
#include "settings.h"
#include "httpfetch.h"
#include "porting.h"

Json::Value fetchJsonValue(const std::string &url,
		std::vector<std::string> *extra_headers)
{
	HTTPFetchRequest fetch_request;
	HTTPFetchResult fetch_result;
	fetch_request.url = url;
	fetch_request.caller = HTTPFETCH_SYNC;

	if (extra_headers != NULL)
		fetch_request.extra_headers = *extra_headers;

	httpfetch_sync(fetch_request, fetch_result);

	if (!fetch_result.succeeded) {
		return Json::Value();
	}
	Json::Value root;
	Json::Reader reader;
	std::istringstream stream(fetch_result.data);

	if (!reader.parse(stream, root)) {
		errorstream << "URL: " << url << std::endl;
		errorstream << "Failed to parse json data " << reader.getFormattedErrorMessages();
		if (fetch_result.data.size() > 100) {
			errorstream << "Data (" << fetch_result.data.size()
				<< " bytes) printed to warningstream." << std::endl;
			warningstream << "data: \"" << fetch_result.data << "\"" << std::endl;
		} else {
			errorstream << "data: \"" << fetch_result.data << "\"" << std::endl;
		}
		return Json::Value();
	}

	return root;
}

std::vector<ModStoreMod>    readModStoreList(Json::Value& modlist) {
		std::vector<ModStoreMod> retval;

	if (modlist.isArray()) {
		for (unsigned int i = 0; i < modlist.size(); i++)
		{
			ModStoreMod toadd;
			toadd.valid = true;

			//id
			if (modlist[i]["id"].asString().size()) {
				std::string id_raw = modlist[i]["id"].asString();
				char* endptr = 0;
				int numbervalue = strtol(id_raw.c_str(),&endptr,10);

				if ((id_raw != "") && (*endptr == 0)) {
					toadd.id = numbervalue;
				}
				else {
					errorstream << "readModStoreList: missing id" << std::endl;
					toadd.valid = false;
				}
			}
			else {
				errorstream << "readModStoreList: missing id" << std::endl;
				toadd.valid = false;
			}

			//title
			if (modlist[i]["title"].asString().size()) {
				toadd.title = modlist[i]["title"].asString();
			}
			else {
				errorstream << "readModStoreList: missing title" << std::endl;
				toadd.valid = false;
			}

			//basename
			if (modlist[i]["basename"].asString().size()) {
				toadd.basename = modlist[i]["basename"].asString();
			}
			else {
				errorstream << "readModStoreList: missing basename" << std::endl;
				toadd.valid = false;
			}

			//author

			//rating

			//version

			if (toadd.valid) {
				retval.push_back(toadd);
			}
		}
	}
	return retval;
}

ModStoreModDetails          readModStoreModDetails(Json::Value& details) {

	ModStoreModDetails retval;

	retval.valid = true;

	//version set
	if (details["version_set"].isArray()) {
		for (unsigned int i = 0; i < details["version_set"].size(); i++)
		{
			ModStoreVersionEntry toadd;

			if (details["version_set"][i]["id"].asString().size()) {
				std::string id_raw = details["version_set"][i]["id"].asString();
				char* endptr = 0;
				int numbervalue = strtol(id_raw.c_str(),&endptr,10);

				if ((id_raw != "") && (*endptr == 0)) {
					toadd.id = numbervalue;
				}
			}
			else {
				errorstream << "readModStoreModDetails: missing version_set id" << std::endl;
				retval.valid = false;
			}

			//date
			if (details["version_set"][i]["date"].asString().size()) {
				toadd.date = details["version_set"][i]["date"].asString();
			}

			//file
			if (details["version_set"][i]["file"].asString().size()) {
				toadd.file = details["version_set"][i]["file"].asString();
			}
			else {
				errorstream << "readModStoreModDetails: missing version_set file" << std::endl;
				retval.valid = false;
			}

			//approved

			//mtversion

			if( retval.valid ) {
				retval.versions.push_back(toadd);
			}
			else {
				break;
			}
		}
	}

	if (retval.versions.size() < 1) {
		infostream << "readModStoreModDetails: not a single version specified!" << std::endl;
		retval.valid = false;
	}

	//categories
	if (details["categories"].isObject()) {
		for (unsigned int i = 0; i < details["categories"].size(); i++) {
			ModStoreCategoryInfo toadd;

			if (details["categories"][i]["id"].asString().size()) {

				std::string id_raw = details["categories"][i]["id"].asString();
				char* endptr = 0;
				int numbervalue = strtol(id_raw.c_str(),&endptr,10);

				if ((id_raw != "") && (*endptr == 0)) {
					toadd.id = numbervalue;
				}
			}
			else {
				errorstream << "readModStoreModDetails: missing categories id" << std::endl;
				retval.valid = false;
			}
			if (details["categories"][i]["title"].asString().size()) {
				toadd.name = details["categories"][i]["title"].asString();
			}
			else {
				errorstream << "readModStoreModDetails: missing categories title" << std::endl;
				retval.valid = false;
			}

			if( retval.valid ) {
				retval.categories.push_back(toadd);
			}
			else {
				break;
			}
		}
	}

	//author
	if (details["author"].isObject()) {
		if (details["author"]["id"].asString().size()) {

			std::string id_raw = details["author"]["id"].asString();
			char* endptr = 0;
			int numbervalue = strtol(id_raw.c_str(),&endptr,10);

			if ((id_raw != "") && (*endptr == 0)) {
				retval.author.id = numbervalue;
			}
			else {
				errorstream << "readModStoreModDetails: missing author id (convert)" << std::endl;
				retval.valid = false;
			}
		}
		else {
			errorstream << "readModStoreModDetails: missing author id" << std::endl;
			retval.valid = false;
		}

		if (details["author"]["username"].asString().size()) {
			retval.author.username = details["author"]["username"].asString();
		}
		else {
			errorstream << "readModStoreModDetails: missing author username" << std::endl;
			retval.valid = false;
		}
	}
	else {
		errorstream << "readModStoreModDetails: missing author" << std::endl;
		retval.valid = false;
	}

	//license
	if (details["license"].isObject()) {
		if (details["license"]["id"].asString().size()) {

			std::string id_raw = details["license"]["id"].asString();
			char* endptr = 0;
			int numbervalue = strtol(id_raw.c_str(),&endptr,10);

			if ((id_raw != "") && (*endptr == 0)) {
				retval.license.id = numbervalue;
			}
		}
		else {
			errorstream << "readModStoreModDetails: missing license id" << std::endl;
			retval.valid = false;
		}

		if (details["license"]["short"].asString().size()) {
			retval.license.shortinfo = details["license"]["short"].asString();
		}
		else {
			errorstream << "readModStoreModDetails: missing license short" << std::endl;
			retval.valid = false;
		}

		if (details["license"]["link"].asString().size()) {
			retval.license.url = details["license"]["link"].asString();
		}

	}

	//titlepic
	if (details["titlepic"].isObject()) {
		if (details["titlepic"]["id"].asString().size()) {

			std::string id_raw = details["titlepic"]["id"].asString();
			char* endptr = 0;
			int numbervalue = strtol(id_raw.c_str(),&endptr,10);

			if ((id_raw != "") && (*endptr == 0)) {
				retval.titlepic.id = numbervalue;
			}
		}

		if (details["titlepic"]["file"].asString().size()) {
			retval.titlepic.file = details["titlepic"]["file"].asString();
		}

		if (details["titlepic"]["desc"].asString().size()) {
			retval.titlepic.description = details["titlepic"]["desc"].asString();
		}

		if (details["titlepic"]["mod"].asString().size()) {

			std::string mod_raw = details["titlepic"]["mod"].asString();
			char* endptr = 0;
			int numbervalue = strtol(mod_raw.c_str(),&endptr,10);

			if ((mod_raw != "") && (*endptr == 0)) {
				retval.titlepic.mod = numbervalue;
			}
		}
	}

	//id
	if (details["id"].asString().size()) {

		std::string id_raw = details["id"].asString();
		char* endptr = 0;
		int numbervalue = strtol(id_raw.c_str(),&endptr,10);

		if ((id_raw != "") && (*endptr == 0)) {
			retval.id = numbervalue;
		}
	}
	else {
		errorstream << "readModStoreModDetails: missing id" << std::endl;
		retval.valid = false;
	}

	//title
	if (details["title"].asString().size()) {
		retval.title = details["title"].asString();
	}
	else {
		errorstream << "readModStoreModDetails: missing title" << std::endl;
		retval.valid = false;
	}

	//basename
	if (details["basename"].asString().size()) {
		retval.basename = details["basename"].asString();
	}
	else {
		errorstream << "readModStoreModDetails: missing basename" << std::endl;
		retval.valid = false;
	}

	//description
	if (details["desc"].asString().size()) {
		retval.description = details["desc"].asString();
	}

	//repository
	if (details["replink"].asString().size()) {
		retval.repository = details["replink"].asString();
	}

	//value
	if (details["value"].isInt()) {
		retval.rating = details["value"].asInt();
	} else {
		retval.rating = 0;
	}

	//depends
	if (details["depends"].isArray()) {
		//TODO
	}

	//softdepends
	if (details["softdep"].isArray()) {
		//TODO
	}

	//screenshot url
	if (details["screenshot_url"].asString().size()) {
		retval.screenshot_url = details["screenshot_url"].asString();
	}

	return retval;
}

repo_name: pgimeno/minetest

license: unknown


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 __CONVERT_JSON_H__
#define __CONVERT_JSON_H__

#include <json/json.h>

struct ModStoreMod;
struct ModStoreModDetails;

std::vector<ModStoreMod>    readModStoreList(Json::Value& modlist);
ModStoreModDetails          readModStoreModDetails(Json::Value& details);

Json::Value                 fetchJsonValue(const std::string &url,
                                           std::vector<std::string> *extra_headers);

#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 "craftdef.h"

#include "irrlichttypes.h"
#include "log.h"
#include <sstream>
#include <set>
#include <algorithm>
#include "gamedef.h"
#include "inventory.h"
#include "util/serialize.h"
#include "util/string.h"
#include "util/numeric.h"
#include "util/strfnd.h"
#include "exceptions.h"

inline bool isGroupRecipeStr(const std::string &rec_name)
{
	return str_starts_with(rec_name, std::string("group:"));
}

inline u64 getHashForString(const std::string &recipe_str)
{
	/*errorstream << "Hashing craft string  \"" << recipe_str << '"';*/
	return murmur_hash_64_ua(recipe_str.data(), recipe_str.length(), 0xdeadbeef);
}

static u64 getHashForGrid(CraftHashType type, const std::vector<std::string> &grid_names)
{
	switch (type) {
		case CRAFT_HASH_TYPE_ITEM_NAMES: {
			std::ostringstream os;
			bool is_first = true;
			for (size_t i = 0; i < grid_names.size(); i++) {
				if (grid_names[i] != "") {
					os << (is_first ? "" : "\n") << grid_names[i];
					is_first = false;
				}
			}
			return getHashForString(os.str());
		} case CRAFT_HASH_TYPE_COUNT: {
			u64 cnt = 0;
			for (size_t i = 0; i < grid_names.size(); i++)
				if (grid_names[i] != "")
					cnt++;
			return cnt;
		} case CRAFT_HASH_TYPE_UNHASHED:
			return 0;
	}
	// invalid CraftHashType
	assert(false);
	return 0;
}

// Check if input matches recipe
// Takes recipe groups into account
static bool inputItemMatchesRecipe(const std::string &inp_name,
		const std::string &rec_name, IItemDefManager *idef)
{
	// Exact name
	if (inp_name == rec_name)
		return true;

	// Group
	if (isGroupRecipeStr(rec_name) && idef->isKnown(inp_name)) {
		const struct ItemDefinition &def = idef->get(inp_name);
		Strfnd f(rec_name.substr(6));
		bool all_groups_match = true;
		do {
			std::string check_group = f.next(",");
			if (itemgroup_get(def.groups, check_group) == 0) {
				all_groups_match = false;
				break;
			}
		} while (!f.at_end());
		if (all_groups_match)
			return true;
	}

	// Didn't match
	return false;
}

// Deserialize an itemstring then return the name of the item
static std::string craftGetItemName(const std::string &itemstring, IGameDef *gamedef)
{
	ItemStack item;
	item.deSerialize(itemstring, gamedef->idef());
	return item.name;
}

// (mapcar craftGetItemName itemstrings)
static std::vector<std::string> craftGetItemNames(
		const std::vector<std::string> &itemstrings, IGameDef *gamedef)
{
	std::vector<std::string> result;
	for (std::vector<std::string>::size_type i = 0;
			i < itemstrings.size(); i++) {
		result.push_back(craftGetItemName(itemstrings[i], gamedef));
	}
	return result;
}

// Get name of each item, and return them as a new list.
static std::vector<std::string> craftGetItemNames(
		const std::vector<ItemStack> &items, IGameDef *gamedef)
{
	std::vector<std::string> result;
	for (std::vector<ItemStack>::size_type i = 0;
			i < items.size(); i++) {
		result.push_back(items[i].name);
	}
	return result;
}

// convert a list of item names, to ItemStacks.
static std::vector<ItemStack> craftGetItems(
		const std::vector<std::string> &items, IGameDef *gamedef)
{
	std::vector<ItemStack> result;
	for (std::vector<std::string>::size_type i = 0;
			i < items.size(); i++) {
		result.push_back(ItemStack(std::string(items[i]), (u16)1,
			(u16)0, gamedef->getItemDefManager()));
	}
	return result;
}

// Compute bounding rectangle given a matrix of items
// Returns false if every item is ""
static bool craftGetBounds(const std::vector<std::string> &items, unsigned int width,
		unsigned int &min_x, unsigned int &max_x,
		unsigned int &min_y, unsigned int &max_y)
{
	bool success = false;
	unsigned int x = 0;
	unsigned int y = 0;
	for (std::vector<std::string>::size_type i = 0;
			i < items.size(); i++) {
		// Is this an actual item?
		if (items[i] != "") {
			if (!success) {
				// This is the first nonempty item
				min_x = max_x = x;
				min_y = max_y = y;
				success = true;
			} else {
				if (x < min_x) min_x = x;
				if (x > max_x) max_x = x;
				if (y < min_y) min_y = y;
				if (y > max_y) max_y = y;
			}
		}

		// Step coordinate
		x++;
		if (x == width) {
			x = 0;
			y++;
		}
	}
	return success;
}

// Removes 1 from each item stack
static void craftDecrementInput(CraftInput &input, IGameDef *gamedef)
{
	for (std::vector<ItemStack>::size_type i = 0;
			i < input.items.size(); i++) {
		if (input.items[i].count != 0)
			input.items[i].remove(1);
	}
}

// Removes 1 from each item stack with replacement support
// Example: if replacements contains the pair ("bucket:bucket_water", "bucket:bucket_empty"),
//   a water bucket will not be removed but replaced by an empty bucket.
static void craftDecrementOrReplaceInput(CraftInput &input,
		std::vector<ItemStack> &output_replacements,
		const CraftReplacements &replacements,
		IGameDef *gamedef)
{
	if (replacements.pairs.empty()) {
		craftDecrementInput(input, gamedef);
		return;
	}

	// Make a copy of the replacements pair list
	std::vector<std::pair<std::string, std::string> > pairs = replacements.pairs;

	for (std::vector<ItemStack>::size_type i = 0;
			i < input.items.size(); i++) {
		ItemStack &item = input.items[i];
		// Find an appropriate replacement
		bool found_replacement = false;
		for (std::vector<std::pair<std::string, std::string> >::iterator
				j = pairs.begin();
				j != pairs.end(); ++j) {
			if (inputItemMatchesRecipe(item.name, j->first, gamedef->idef())) {
				if (item.count == 1) {
					item.deSerialize(j->second, gamedef->idef());
					found_replacement = true;
					pairs.erase(j);
					break;
				} else {
					ItemStack rep;
					rep.deSerialize(j->second, gamedef->idef());
					item.remove(1);
					found_replacement = true;
					output_replacements.push_back(rep);
					break;
				}
			}
		}
		// No replacement was found, simply decrement count by one
		if (!found_replacement && item.count > 0)
			item.remove(1);
	}
}

// Dump an itemstring matrix
static std::string craftDumpMatrix(const std::vector<std::string> &items,
		unsigned int width)
{
	std::ostringstream os(std::ios::binary);
	os << "{ ";
	unsigned int x = 0;
	for(std::vector<std::string>::size_type i = 0;
			i < items.size(); i++, x++) {
		if (x == width) {
			os << "; ";
			x = 0;
		} else if (x != 0) {
			os << ",";
		}
		os << '"' << items[i] << '"';
	}
	os << " }";
	return os.str();
}

// Dump an item matrix
std::string craftDumpMatrix(const std::vector<ItemStack> &items,
		unsigned int width)
{
	std::ostringstream os(std::ios::binary);
	os << "{ ";
	unsigned int x = 0;
	for (std::vector<ItemStack>::size_type i = 0;
			i < items.size(); i++, x++) {
		if (x == width) {
			os << "; ";
			x = 0;
		} else if (x != 0) {
			os << ",";
		}
		os << '"' << (items[i].getItemString()) << '"';
	}
	os << " }";
	return os.str();
}

/*
	CraftInput
*/

std::string CraftInput::dump() const
{
	std::ostringstream os(std::ios::binary);
	os << "(method=" << ((int)method) << ", items="
		<< craftDumpMatrix(items, width) << ")";
	return os.str();
}

/*
	CraftOutput
*/

std::string CraftOutput::dump() const
{
	std::ostringstream os(std::ios::binary);
	os << "(item=\"" << item << "\", time=" << time << ")";
	return os.str();
}

/*
	CraftReplacements
*/

std::string CraftReplacements::dump() const
{
	std::ostringstream os(std::ios::binary);
	os<<"{";
	const char *sep = "";
	for (std::vector<std::pair<std::string, std::string> >::size_type i = 0;
			i < pairs.size(); i++) {
		const std::pair<std::string, std::string> &repl_p = pairs[i];
		os << sep
			<< '"' << (repl_p.first)
			<< "\"=>\"" << (repl_p.second) << '"';
		sep = ",";
	}
	os << "}";
	return os.str();
}

/*
	CraftDefinitionShaped
*/

std::string CraftDefinitionShaped::getName() const
{
	return "shaped";
}

bool CraftDefinitionShaped::check(const CraftInput &input, IGameDef *gamedef) const
{
	if (input.method != CRAFT_METHOD_NORMAL)
		return false;

	// Get input item matrix
	std::vector<std::string> inp_names = craftGetItemNames(input.items, gamedef);
	unsigned int inp_width = input.width;
	if (inp_width == 0)
		return false;
	while (inp_names.size() % inp_width != 0)
		inp_names.push_back("");

	// Get input bounds
	unsigned int inp_min_x = 0, inp_max_x = 0, inp_min_y = 0, inp_max_y = 0;
	if (!craftGetBounds(inp_names, inp_width, inp_min_x, inp_max_x,
			inp_min_y, inp_max_y))
		return false;  // it was empty

	std::vector<std::string> rec_names;
	if (hash_inited)
		rec_names = recipe_names;
	else
		rec_names = craftGetItemNames(recipe, gamedef);

	// Get recipe item matrix
	unsigned int rec_width = width;
	if (rec_width == 0)
		return false;
	while (rec_names.size() % rec_width != 0)
		rec_names.push_back("");

	// Get recipe bounds
	unsigned int rec_min_x=0, rec_max_x=0, rec_min_y=0, rec_max_y=0;
	if (!craftGetBounds(rec_names, rec_width, rec_min_x, rec_max_x,
			rec_min_y, rec_max_y))
		return false;  // it was empty

	// Different sizes?
	if (inp_max_x - inp_min_x != rec_max_x - rec_min_x ||
			inp_max_y - inp_min_y != rec_max_y - rec_min_y)
		return false;

	// Verify that all item names in the bounding box are equal
	unsigned int w = inp_max_x - inp_min_x + 1;
	unsigned int h = inp_max_y - inp_min_y + 1;

	for (unsigned int y=0; y < h; y++) {
		unsigned int inp_y = (inp_min_y + y) * inp_width;
		unsigned int rec_y = (rec_min_y + y) * rec_width;

		for (unsigned int x=0; x < w; x++) {
			unsigned int inp_x = inp_min_x + x;
			unsigned int rec_x = rec_min_x + x;

			if (!inputItemMatchesRecipe(
					inp_names[inp_y + inp_x],
					rec_names[rec_y + rec_x], gamedef->idef())) {
				return false;
			}
		}
	}

	return true;
}

CraftOutput CraftDefinitionShaped::getOutput(const CraftInput &input, IGameDef *gamedef) const
{
	return CraftOutput(output, 0);
}

CraftInput CraftDefinitionShaped::getInput(const CraftOutput &output, IGameDef *gamedef) const
{
	return CraftInput(CRAFT_METHOD_NORMAL,width,craftGetItems(recipe,gamedef));
}

void CraftDefinitionShaped::decrementInput(CraftInput &input, std::vector<ItemStack> &output_replacements,
	 IGameDef *gamedef) const
{
	craftDecrementOrReplaceInput(input, output_replacements, replacements, gamedef);
}

CraftHashType CraftDefinitionShaped::getHashType() const
{
	assert(hash_inited); // Pre-condition
	bool has_group = false;
	for (size_t i = 0; i < recipe_names.size(); i++) {
		if (isGroupRecipeStr(recipe_names[i])) {
			has_group = true;
			break;
		}
	}
	if (has_group)
		return CRAFT_HASH_TYPE_COUNT;
	else
		return CRAFT_HASH_TYPE_ITEM_NAMES;
}

u64 CraftDefinitionShaped::getHash(CraftHashType type) const
{
	assert(hash_inited); // Pre-condition
	assert((type == CRAFT_HASH_TYPE_ITEM_NAMES)
		|| (type == CRAFT_HASH_TYPE_COUNT)); // Pre-condition

	std::vector<std::string> rec_names = recipe_names;
	std::sort(rec_names.begin(), rec_names.end());
	return getHashForGrid(type, rec_names);
}

void CraftDefinitionShaped::initHash(IGameDef *gamedef)
{
	if (hash_inited)
		return;
	hash_inited = true;
	recipe_names = craftGetItemNames(recipe, gamedef);
}

std::string CraftDefinitionShaped::dump() const
{
	std::ostringstream os(std::ios::binary);
	os << "(shaped, output=\"" << output
		<< "\", recipe=" << craftDumpMatrix(recipe, width)
		<< ", replacements=" << replacements.dump() << ")";
	return os.str();
}

/*
	CraftDefinitionShapeless
*/

std::string CraftDefinitionShapeless::getName() const
{
	return "shapeless";
}

bool CraftDefinitionShapeless::check(const CraftInput &input, IGameDef *gamedef) const
{
	if (input.method != CRAFT_METHOD_NORMAL)
		return false;

	// Filter empty items out of input
	std::vector<std::string> input_filtered;
	for (std::vector<ItemStack>::size_type i = 0;
			i < input.items.size(); i++) {
		const ItemStack &item = input.items[i];
		if (item.name != "")
			input_filtered.push_back(item.name);
	}

	// If there is a wrong number of items in input, no match
	if (input_filtered.size() != recipe.size()) {
		/*dstream<<"Number of input items ("<<input_filtered.size()
				<<") does not match recipe size ("<<recipe.size()<<") "
				<<"of recipe with output="<<output<<std::endl;*/
		return false;
	}

	std::vector<std::string> recipe_copy;
	if (hash_inited)
		recipe_copy = recipe_names;
	else {
		recipe_copy = craftGetItemNames(recipe, gamedef);
		std::sort(recipe_copy.begin(), recipe_copy.end());
	}

	// Try with all permutations of the recipe,
	// start from the lexicographically first permutation (=sorted),
	// recipe_names is pre-sorted
	do {
		// If all items match, the recipe matches
		bool all_match = true;
		//dstream<<"Testing recipe (output="<<output<<"):";
		for (size_t i=0; i<recipe.size(); i++) {
			//dstream<<" ("<<input_filtered[i]<<" == "<<recipe_copy[i]<<")";
			if (!inputItemMatchesRecipe(input_filtered[i], recipe_copy[i],
					gamedef->idef())) {
				all_match = false;
				break;
			}
		}
		//dstream<<" -> match="<<all_match<<std::endl;
		if (all_match)
			return true;
	} while (std::next_permutation(recipe_copy.begin(), recipe_copy.end()));

	return false;
}

CraftOutput CraftDefinitionShapeless::getOutput(const CraftInput &input, IGameDef *gamedef) const
{
	return CraftOutput(output, 0);
}

CraftInput CraftDefinitionShapeless::getInput(const CraftOutput &output, IGameDef *gamedef) const
{
	return CraftInput(CRAFT_METHOD_NORMAL, 0, craftGetItems(recipe, gamedef));
}

void CraftDefinitionShapeless::decrementInput(CraftInput &input, std::vector<ItemStack> &output_replacements,
	IGameDef *gamedef) const
{
	craftDecrementOrReplaceInput(input, output_replacements, replacements, gamedef);
}

CraftHashType CraftDefinitionShapeless::getHashType() const
{
	assert(hash_inited); // Pre-condition
	bool has_group = false;
	for (size_t i = 0; i < recipe_names.size(); i++) {
		if (isGroupRecipeStr(recipe_names[i])) {
			has_group = true;
			break;
		}
	}
	if (has_group)
		return CRAFT_HASH_TYPE_COUNT;
	else
		return CRAFT_HASH_TYPE_ITEM_NAMES;
}

u64 CraftDefinitionShapeless::getHash(CraftHashType type) const
{
	assert(hash_inited); // Pre-condition
	assert(type == CRAFT_HASH_TYPE_ITEM_NAMES
		|| type == CRAFT_HASH_TYPE_COUNT); // Pre-condition
	return getHashForGrid(type, recipe_names);
}

void CraftDefinitionShapeless::initHash(IGameDef *gamedef)
{
	if (hash_inited)
		return;
	hash_inited = true;
	recipe_names = craftGetItemNames(recipe, gamedef);
	std::sort(recipe_names.begin(), recipe_names.end());
}

std::string CraftDefinitionShapeless::dump() const
{
	std::ostringstream os(std::ios::binary);
	os << "(shapeless, output=\"" << output
		<< "\", recipe=" << craftDumpMatrix(recipe, recipe.size())
		<< ", replacements=" << replacements.dump() << ")";
	return os.str();
}

/*
	CraftDefinitionToolRepair
*/

static ItemStack craftToolRepair(
		const ItemStack &item1,
		const ItemStack &item2,
		float additional_wear,
		IGameDef *gamedef)
{
	IItemDefManager *idef = gamedef->idef();
	if (item1.count != 1 || item2.count != 1 || item1.name != item2.name
			|| idef->get(item1.name).type != ITEM_TOOL
			|| idef->get(item2.name).type != ITEM_TOOL) {
		// Failure
		return ItemStack();
	}

	s32 item1_uses = 65536 - (u32) item1.wear;
	s32 item2_uses = 65536 - (u32) item2.wear;
	s32 new_uses = item1_uses + item2_uses;
	s32 new_wear = 65536 - new_uses + floor(additional_wear * 65536 + 0.5);
	if (new_wear >= 65536)
		return ItemStack();
	if (new_wear < 0)
		new_wear = 0;

	ItemStack repaired = item1;
	repaired.wear = new_wear;
	return repaired;
}

std::string CraftDefinitionToolRepair::getName() const
{
	return "toolrepair";
}

bool CraftDefinitionToolRepair::check(const CraftInput &input, IGameDef *gamedef) const
{
	if (input.method != CRAFT_METHOD_NORMAL)
		return false;

	ItemStack item1;
	ItemStack item2;
	for (std::vector<ItemStack>::size_type i = 0;
			i < input.items.size(); i++) {
		const ItemStack &item = input.items[i];
		if (!item.empty()) {
			if (item1.empty())
				item1 = item;
			else if (item2.empty())
				item2 = item;
			else
				return false;
		}
	}
	ItemStack repaired = craftToolRepair(item1, item2, additional_wear, gamedef);
	return !repaired.empty();
}

CraftOutput CraftDefinitionToolRepair::getOutput(const CraftInput &input, IGameDef *gamedef) const
{
	ItemStack item1;
	ItemStack item2;
	for (std::vector<ItemStack>::size_type i = 0;
			i < input.items.size(); i++) {
		const ItemStack &item = input.items[i];
		if (!item.empty()) {
			if (item1.empty())
				item1 = item;
			else if (item2.empty())
				item2 = item;
		}
	}
	ItemStack repaired = craftToolRepair(item1, item2, additional_wear, gamedef);
	return CraftOutput(repaired.getItemString(), 0);
}

CraftInput CraftDefinitionToolRepair::getInput(const CraftOutput &output, IGameDef *gamedef) const
{
	std::vector<ItemStack> stack;
	stack.push_back(ItemStack());
	return CraftInput(CRAFT_METHOD_COOKING, additional_wear, stack);
}

void CraftDefinitionToolRepair::decrementInput(CraftInput &input, std::vector<ItemStack> &output_replacements,
	IGameDef *gamedef) const
{
	craftDecrementInput(input, gamedef);
}

std::string CraftDefinitionToolRepair::dump() const
{
	std::ostringstream os(std::ios::binary);
	os << "(toolrepair, additional_wear=" << additional_wear << ")";
	return os.str();
}

/*
	CraftDefinitionCooking
*/

std::string CraftDefinitionCooking::getName() const
{
	return "cooking";
}

bool CraftDefinitionCooking::check(const CraftInput &input, IGameDef *gamedef) const
{
	if (input.method != CRAFT_METHOD_COOKING)
		return false;

	// Filter empty items out of input
	std::vector<std::string> input_filtered;
	for (std::vector<ItemStack>::size_type i = 0;
			i < input.items.size(); i++) {
		const std::string &name = input.items[i].name;
		if (name != "")
			input_filtered.push_back(name);
	}

	// If there is a wrong number of items in input, no match
	if (input_filtered.size() != 1) {
		/*dstream<<"Number of input items ("<<input_filtered.size()
				<<") does not match recipe size (1) "
				<<"of cooking recipe with output="<<output<<std::endl;*/
		return false;
	}

	// Check the single input item
	return inputItemMatchesRecipe(input_filtered[0], recipe, gamedef->idef());
}

CraftOutput CraftDefinitionCooking::getOutput(const CraftInput &input, IGameDef *gamedef) const
{
	return CraftOutput(output, cooktime);
}

CraftInput CraftDefinitionCooking::getInput(const CraftOutput &output, IGameDef *gamedef) const
{
	std::vector<std::string> rec;
	rec.push_back(recipe);
	return CraftInput(CRAFT_METHOD_COOKING,cooktime,craftGetItems(rec,gamedef));
}

void CraftDefinitionCooking::decrementInput(CraftInput &input, std::vector<ItemStack> &output_replacements,
	IGameDef *gamedef) const
{
	craftDecrementOrReplaceInput(input, output_replacements, replacements, gamedef);
}

CraftHashType CraftDefinitionCooking::getHashType() const
{
	if (isGroupRecipeStr(recipe_name))
		return CRAFT_HASH_TYPE_COUNT;
	else
		return CRAFT_HASH_TYPE_ITEM_NAMES;
}

u64 CraftDefinitionCooking::getHash(CraftHashType type) const
{
	if (type == CRAFT_HASH_TYPE_ITEM_NAMES) {
		return getHashForString(recipe_name);
	} else if (type == CRAFT_HASH_TYPE_COUNT) {
		return 1;
	} else {
		//illegal hash type for this CraftDefinition (pre-condition)
		assert(false);
		return 0;
	}
}

void CraftDefinitionCooking::initHash(IGameDef *gamedef)
{
	if (hash_inited)
		return;
	hash_inited = true;
	recipe_name = craftGetItemName(recipe, gamedef);
}

std::string CraftDefinitionCooking::dump() const
{
	std::ostringstream os(std::ios::binary);
	os << "(cooking, output=\"" << output
		<< "\", recipe=\"" << recipe
		<< "\", cooktime=" << cooktime << ")"
		<< ", replacements=" << replacements.dump() << ")";
	return os.str();
}

/*
	CraftDefinitionFuel
*/

std::string CraftDefinitionFuel::getName() const
{
	return "fuel";
}

bool CraftDefinitionFuel::check(const CraftInput &input, IGameDef *gamedef) const
{
	if (input.method != CRAFT_METHOD_FUEL)
		return false;

	// Filter empty items out of input
	std::vector<std::string> input_filtered;
	for (std::vector<ItemStack>::size_type i = 0;
			i < input.items.size(); i++) {
		const std::string &name = input.items[i].name;
		if (name != "")
			input_filtered.push_back(name);
	}

	// If there is a wrong number of items in input, no match
	if (input_filtered.size() != 1) {
		/*dstream<<"Number of input items ("<<input_filtered.size()
				<<") does not match recipe size (1) "
				<<"of fuel recipe with burntime="<<burntime<<std::endl;*/
		return false;
	}

	// Check the single input item
	return inputItemMatchesRecipe(input_filtered[0], recipe, gamedef->idef());
}

CraftOutput CraftDefinitionFuel::getOutput(const CraftInput &input, IGameDef *gamedef) const
{
	return CraftOutput("", burntime);
}

CraftInput CraftDefinitionFuel::getInput(const CraftOutput &output, IGameDef *gamedef) const
{
	std::vector<std::string> rec;
	rec.push_back(recipe);
	return CraftInput(CRAFT_METHOD_COOKING,(int)burntime,craftGetItems(rec,gamedef));
}

void CraftDefinitionFuel::decrementInput(CraftInput &input, std::vector<ItemStack> &output_replacements,
	IGameDef *gamedef) const
{
	craftDecrementOrReplaceInput(input, output_replacements, replacements, gamedef);
}

CraftHashType CraftDefinitionFuel::getHashType() const
{
	if (isGroupRecipeStr(recipe_name))
		return CRAFT_HASH_TYPE_COUNT;
	else
		return CRAFT_HASH_TYPE_ITEM_NAMES;
}

u64 CraftDefinitionFuel::getHash(CraftHashType type) const
{
	if (type == CRAFT_HASH_TYPE_ITEM_NAMES) {
		return getHashForString(recipe_name);
	} else if (type == CRAFT_HASH_TYPE_COUNT) {
		return 1;
	} else {
		//illegal hash type for this CraftDefinition (pre-condition)
		assert(false);
		return 0;
	}
}

void CraftDefinitionFuel::initHash(IGameDef *gamedef)
{
	if (hash_inited)
		return;
	hash_inited = true;
	recipe_name = craftGetItemName(recipe, gamedef);
}
std::string CraftDefinitionFuel::dump() const
{
	std::ostringstream os(std::ios::binary);
	os << "(fuel, recipe=\"" << recipe
		<< "\", burntime=" << burntime << ")"
		<< ", replacements=" << replacements.dump() << ")";
	return os.str();
}

/*
	Craft definition manager
*/

class CCraftDefManager: public IWritableCraftDefManager
{
public:
	CCraftDefManager()
	{
		m_craft_defs.resize(craft_hash_type_max + 1);
	}

	virtual ~CCraftDefManager()
	{
		clear();
	}

	virtual bool getCraftResult(CraftInput &input, CraftOutput &output,
			std::vector<ItemStack> &output_replacement, bool decrementInput,
			IGameDef *gamedef) const
	{
		output.item = "";
		output.time = 0;

		// If all input items are empty, abort.
		bool all_empty = true;
		for (std::vector<ItemStack>::size_type i = 0;
			i < input.items.size(); i++) {
			if (!input.items[i].empty()) {
				all_empty = false;
				break;
			}
		}
		if (all_empty)
			return false;

		std::vector<std::string> input_names;
		input_names = craftGetItemNames(input.items, gamedef);
		std::sort(input_names.begin(), input_names.end());

		// Try hash types with increasing collision rate, and return if found.
		for (int type = 0; type <= craft_hash_type_max; type++) {
			u64 hash = getHashForGrid((CraftHashType) type, input_names);

			/*errorstream << "Checking type " << type << " with hash " << hash << std::endl;*/

			// We'd like to do "const [...] hash_collisions = m_craft_defs[type][hash];"
			// but that doesn't compile for some reason. This does.
			std::map<u64, std::vector<CraftDefinition*> >::const_iterator
				col_iter = (m_craft_defs[type]).find(hash);

			if (col_iter == (m_craft_defs[type]).end())
				continue;

			const std::vector<CraftDefinition*> &hash_collisions = col_iter->second;
			// Walk crafting definitions from back to front, so that later
			// definitions can override earlier ones.
			for (std::vector<CraftDefinition*>::size_type
					i = hash_collisions.size(); i > 0; i--) {
				CraftDefinition *def = hash_collisions[i - 1];

				/*errorstream << "Checking " << input.dump() << std::endl
					<< " against " << def->dump() << std::endl;*/

				if (def->check(input, gamedef)) {
					// Check if the crafted node/item exists
					CraftOutput out = def->getOutput(input, gamedef);
					ItemStack is;
					is.deSerialize(out.item, gamedef->idef());
					if (!is.isKnown(gamedef->idef())) {
						infostream << "trying to craft non-existent "
							<< out.item << ", ignoring recipe" << std::endl;
						continue;
					}

					// Get output, then decrement input (if requested)
					output = out;

					if (decrementInput)
						def->decrementInput(input, output_replacement, gamedef);
					/*errorstream << "Check RETURNS TRUE" << std::endl;*/
					return true;
				}
			}
		}
		return false;
	}

	virtual std::vector<CraftDefinition*> getCraftRecipes(CraftOutput &output,
			IGameDef *gamedef, unsigned limit=0) const
	{
		std::vector<CraftDefinition*> recipes;

		std::map<std::string, std::vector<CraftDefinition*> >::const_iterator
			vec_iter = m_output_craft_definitions.find(output.item);

		if (vec_iter == m_output_craft_definitions.end())
			return recipes;

		const std::vector<CraftDefinition*> &vec = vec_iter->second;

		recipes.reserve(limit ? MYMIN(limit, vec.size()) : vec.size());

		for (std::vector<CraftDefinition*>::size_type i = vec.size();
				i > 0; i--) {
			CraftDefinition *def = vec[i - 1];
			if (limit && recipes.size() >= limit)
				break;
			recipes.push_back(def);
		}

		return recipes;
	}

	virtual bool clearCraftRecipesByOutput(const CraftOutput &output, IGameDef *gamedef)
	{
		std::map<std::string, std::vector<CraftDefinition*> >::iterator vec_iter =
			m_output_craft_definitions.find(output.item);

		if (vec_iter == m_output_craft_definitions.end())
			return false;

		std::vector<CraftDefinition*> &vec = vec_iter->second;
		for (std::vector<CraftDefinition*>::iterator i = vec.begin();
				i != vec.end(); ++i) {
			CraftDefinition *def = *i;
			// Recipes are not yet hashed at this point
			std::vector<CraftDefinition*> &unhashed_inputs_vec = m_craft_defs[(int) CRAFT_HASH_TYPE_UNHASHED][0];
			std::vector<CraftDefinition*> new_vec_by_input;
			/* We will preallocate necessary memory addresses, so we don't need to reallocate them later.
				This would save us some performance. */
			new_vec_by_input.reserve(unhashed_inputs_vec.size());
			for (std::vector<CraftDefinition*>::iterator i2 = unhashed_inputs_vec.begin();
					i2 != unhashed_inputs_vec.end(); ++i2) {
				if (def != *i2) {
					new_vec_by_input.push_back(*i2);
				}
			}
			m_craft_defs[(int) CRAFT_HASH_TYPE_UNHASHED][0].swap(new_vec_by_input);
		}
		m_output_craft_definitions.erase(output.item);
		return true;
	}

	virtual bool clearCraftRecipesByInput(CraftMethod craft_method, unsigned int craft_grid_width,
		const std::vector<std::string> &recipe, IGameDef *gamedef)
	{
		bool all_empty = true;
		for (std::vector<std::string>::size_type i = 0;
				i < recipe.size(); i++) {
			if (!recipe[i].empty()) {
				all_empty = false;
				break;
			}
		}
		if (all_empty)
			return false;

		CraftInput input(craft_method, craft_grid_width, craftGetItems(recipe, gamedef));
		// Recipes are not yet hashed at this point
		std::vector<CraftDefinition*> &unhashed_inputs_vec = m_craft_defs[(int) CRAFT_HASH_TYPE_UNHASHED][0];
		std::vector<CraftDefinition*> new_vec_by_input;
		bool got_hit = false;
		for (std::vector<CraftDefinition*>::size_type
				i = unhashed_inputs_vec.size(); i > 0; i--) {
			CraftDefinition *def = unhashed_inputs_vec[i - 1];
			/* If the input doesn't match the recipe definition, this recipe definition later
				will be added back in source map. */
			if (!def->check(input, gamedef)) {
				new_vec_by_input.push_back(def);
				continue;
			}
			CraftOutput output = def->getOutput(input, gamedef);
			got_hit = true;
			std::map<std::string, std::vector<CraftDefinition*> >::iterator
				vec_iter = m_output_craft_definitions.find(output.item);
			if (vec_iter == m_output_craft_definitions.end())
				continue;
			std::vector<CraftDefinition*> &vec = vec_iter->second;
			std::vector<CraftDefinition*> new_vec_by_output;
			/* We will preallocate necessary memory addresses, so we don't need
				to reallocate them later. This would save us some performance. */
			new_vec_by_output.reserve(vec.size());
			for (std::vector<CraftDefinition*>::iterator i = vec.begin();
					i != vec.end(); ++i) {
				/* If pointers from map by input and output are not same,
					we will add 'CraftDefinition*' to a new vector. */
				if (def != *i) {
					/* Adding dereferenced iterator value (which are
						'CraftDefinition' reference) to a new vector. */
					new_vec_by_output.push_back(*i);
				}
			}
			// Swaps assigned to current key value with new vector for output map.
			m_output_craft_definitions[output.item].swap(new_vec_by_output);
		}
		if (got_hit)
			// Swaps value with new vector for input map.
			m_craft_defs[(int) CRAFT_HASH_TYPE_UNHASHED][0].swap(new_vec_by_input);

		return got_hit;
	}

	virtual std::string dump() const
	{
		std::ostringstream os(std::ios::binary);
		os << "Crafting definitions:\n";
		for (int type = 0; type <= craft_hash_type_max; ++type) {
			for (std::map<u64, std::vector<CraftDefinition*> >::const_iterator
					it = (m_craft_defs[type]).begin();
					it != (m_craft_defs[type]).end(); ++it) {
				for (std::vector<CraftDefinition*>::size_type i = 0;
						i < it->second.size(); i++) {
					os << "type " << type
						<< " hash " << it->first
						<< " def " << it->second[i]->dump()
						<< "\n";
				}
			}
		}
		return os.str();
	}
	virtual void registerCraft(CraftDefinition *def, IGameDef *gamedef)
	{
		verbosestream << "registerCraft: registering craft definition: "
				<< def->dump() << std::endl;
		m_craft_defs[(int) CRAFT_HASH_TYPE_UNHASHED][0].push_back(def);

		CraftInput input;
		std::string output_name = craftGetItemName(
				def->getOutput(input, gamedef).item, gamedef);
		m_output_craft_definitions[output_name].push_back(def);
	}
	virtual void clear()
	{
		for (int type = 0; type <= craft_hash_type_max; ++type) {
			for (std::map<u64, std::vector<CraftDefinition*> >::iterator
					it = m_craft_defs[type].begin();
					it != m_craft_defs[type].end(); ++it) {
				for (std::vector<CraftDefinition*>::iterator
						iit = it->second.begin();
						iit != it->second.end(); ++iit) {
					delete *iit;
				}
				it->second.clear();
			}
			m_craft_defs[type].clear();
		}
		m_output_craft_definitions.clear();
	}
	virtual void initHashes(IGameDef *gamedef)
	{
		// Move the CraftDefs from the unhashed layer into layers higher up.
		std::vector<CraftDefinition *> &unhashed =
			m_craft_defs[(int) CRAFT_HASH_TYPE_UNHASHED][0];
		for (std::vector<CraftDefinition*>::size_type i = 0;
			i < unhashed.size(); i++) {
			CraftDefinition *def = unhashed[i];

			// Initialize and get the definition's hash
			def->initHash(gamedef);
			CraftHashType type = def->getHashType();
			u64 hash = def->getHash(type);

			// Enter the definition
			m_craft_defs[type][hash].push_back(def);
		}
		unhashed.clear();
	}
private:
	//TODO: change both maps to unordered_map when c++11 can be used
	std::vector<std::map<u64, std::vector<CraftDefinition*> > > m_craft_defs;
	std::map<std::string, std::vector<CraftDefinition*> > m_output_craft_definitions;
};

IWritableCraftDefManager* createCraftDefManager()
{
	return new CCraftDefManager();
}


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 CRAFTDEF_HEADER
#define CRAFTDEF_HEADER

#include <string>
#include <iostream>
#include <vector>
#include <utility>
#include "gamedef.h"
#include "inventory.h"

/*
	Crafting methods.

	The crafting method depends on the inventory list
	that the crafting input comes from.
*/
enum CraftMethod
{
	// Crafting grid
	CRAFT_METHOD_NORMAL,
	// Cooking something in a furnace
	CRAFT_METHOD_COOKING,
	// Using something as fuel for a furnace
	CRAFT_METHOD_FUEL,
};

/*
	The type a hash can be. The earlier a type is mentioned in this enum,
	the earlier it is tried at crafting, and the less likely is a collision.
	Changing order causes changes in behaviour, so know what you do.
 */
enum CraftHashType
{
	// Hashes the normalized names of the recipe's elements.
	// Only recipes without group usage can be found here,
	// because groups can't be guessed efficiently.
	CRAFT_HASH_TYPE_ITEM_NAMES,

	// Counts the non-empty slots.
	CRAFT_HASH_TYPE_COUNT,

	// This layer both spares an extra variable, and helps to retain (albeit rarely used) functionality. Maps to 0.
	// Before hashes are "initialized", all hashes reside here, after initialisation, none are.
	CRAFT_HASH_TYPE_UNHASHED

};
const int craft_hash_type_max = (int) CRAFT_HASH_TYPE_UNHASHED;

/*
	Input: The contents of the crafting slots, arranged in matrix form
*/
struct CraftInput
{
	CraftMethod method;
	unsigned int width;
	std::vector<ItemStack> items;

	CraftInput():
		method(CRAFT_METHOD_NORMAL), width(0), items()
	{}
	CraftInput(CraftMethod method_, unsigned int width_,
			const std::vector<ItemStack> &items_):
		method(method_), width(width_), items(items_)
	{}
	std::string dump() const;
};

/*
	Output: Result of crafting operation
*/
struct CraftOutput
{
	// Used for normal crafting and cooking, itemstring
	std::string item;
	// Used for cooking (cook time) and fuel (burn time), seconds
	float time;

	CraftOutput():
		item(""), time(0)
	{}
	CraftOutput(const std::string &item_, float time_):
		item(item_), time(time_)
	{}
	std::string dump() const;
};

/*
	A list of replacements. A replacement indicates that a specific
	input item should not be deleted (when crafting) but replaced with
	a different item. Each replacements is a pair (itemstring to remove,
	itemstring to replace with)

	Example: If ("bucket:bucket_water", "bucket:bucket_empty") is a
	replacement pair, the crafting input slot that contained a water
	bucket will contain an empty bucket after crafting.

	Note: replacements only work correctly when stack_max of the item
	to be replaced is 1. It is up to the mod writer to ensure this.
*/
struct CraftReplacements
{
	// List of replacements
	std::vector<std::pair<std::string, std::string> > pairs;

	CraftReplacements():
		pairs()
	{}
	CraftReplacements(const std::vector<std::pair<std::string, std::string> > &pairs_):
		pairs(pairs_)
	{}
	std::string dump() const;
};

/*
	Crafting definition base class
*/
class CraftDefinition
{
public:
	CraftDefinition(){}
	virtual ~CraftDefinition(){}

	// Returns type of crafting definition
	virtual std::string getName() const=0;

	// Checks whether the recipe is applicable
	virtual bool check(const CraftInput &input, IGameDef *gamedef) const=0;
	// Returns the output structure, meaning depends on crafting method
	// The implementation can assume that check(input) returns true
	virtual CraftOutput getOutput(const CraftInput &input, IGameDef *gamedef) const=0;
	// the inverse of the above
	virtual CraftInput getInput(const CraftOutput &output, IGameDef *gamedef) const=0;
	// Decreases count of every input item
	virtual void decrementInput(CraftInput &input,
		std::vector<ItemStack> &output_replacements, IGameDef *gamedef) const=0;

	virtual CraftHashType getHashType() const = 0;
	virtual u64 getHash(CraftHashType type) const = 0;

	// to be called after all mods are loaded, so that we catch all aliases
	virtual void initHash(IGameDef *gamedef) = 0;

	virtual std::string dump() const=0;
};

/*
	A plain-jane (shaped) crafting definition

	Supported crafting method: CRAFT_METHOD_NORMAL.
	Requires the input items to be arranged exactly like in the recipe.
*/
class CraftDefinitionShaped: public CraftDefinition
{
public:
	CraftDefinitionShaped():
		output(""), width(1), recipe(), hash_inited(false), replacements()
	{}
	CraftDefinitionShaped(
			const std::string &output_,
			unsigned int width_,
			const std::vector<std::string> &recipe_,
			const CraftReplacements &replacements_):
		output(output_), width(width_), recipe(recipe_),
		hash_inited(false), replacements(replacements_)
	{}
	virtual ~CraftDefinitionShaped(){}

	virtual std::string getName() const;
	virtual bool check(const CraftInput &input, IGameDef *gamedef) const;
	virtual CraftOutput getOutput(const CraftInput &input, IGameDef *gamedef) const;
	virtual CraftInput getInput(const CraftOutput &output, IGameDef *gamedef) const;
	virtual void decrementInput(CraftInput &input,
		std::vector<ItemStack> &output_replacements, IGameDef *gamedef) const;

	virtual CraftHashType getHashType() const;
	virtual u64 getHash(CraftHashType type) const;

	virtual void initHash(IGameDef *gamedef);

	virtual std::string dump() const;

private:
	// Output itemstring
	std::string output;
	// Width of recipe
	unsigned int width;
	// Recipe matrix (itemstrings)
	std::vector<std::string> recipe;
	// Recipe matrix (item names)
	std::vector<std::string> recipe_names;
	// bool indicating if initHash has been called already
	bool hash_inited;
	// Replacement items for decrementInput()
	CraftReplacements replacements;
};

/*
	A shapeless crafting definition
	Supported crafting method: CRAFT_METHOD_NORMAL.
	Input items can arranged in any way.
*/
class CraftDefinitionShapeless: public CraftDefinition
{
public:
	CraftDefinitionShapeless():
		output(""), recipe(), hash_inited(false), replacements()
	{}
	CraftDefinitionShapeless(
			const std::string &output_,
			const std::vector<std::string> &recipe_,
			const CraftReplacements &replacements_):
		output(output_), recipe(recipe_),
		hash_inited(false), replacements(replacements_)
	{}
	virtual ~CraftDefinitionShapeless(){}

	virtual std::string getName() const;
	virtual bool check(const CraftInput &input, IGameDef *gamedef) const;
	virtual CraftOutput getOutput(const CraftInput &input, IGameDef *gamedef) const;
	virtual CraftInput getInput(const CraftOutput &output, IGameDef *gamedef) const;
	virtual void decrementInput(CraftInput &input,
		std::vector<ItemStack> &output_replacements, IGameDef *gamedef) const;

	virtual CraftHashType getHashType() const;
	virtual u64 getHash(CraftHashType type) const;

	virtual void initHash(IGameDef *gamedef);

	virtual std::string dump() const;

private:
	// Output itemstring
	std::string output;
	// Recipe list (itemstrings)
	std::vector<std::string> recipe;
	// Recipe list (item names)
	std::vector<std::string> recipe_names;
	// bool indicating if initHash has been called already
	bool hash_inited;
	// Replacement items for decrementInput()
	CraftReplacements replacements;
};

/*
	Tool repair crafting definition
	Supported crafting method: CRAFT_METHOD_NORMAL.
	Put two damaged tools into the crafting grid, get one tool back.
	There should only be one crafting definition of this type.
*/
class CraftDefinitionToolRepair: public CraftDefinition
{
public:
	CraftDefinitionToolRepair():
		additional_wear(0)
	{}
	CraftDefinitionToolRepair(float additional_wear_):
		additional_wear(additional_wear_)
	{}
	virtual ~CraftDefinitionToolRepair(){}

	virtual std::string getName() const;
	virtual bool check(const CraftInput &input, IGameDef *gamedef) const;
	virtual CraftOutput getOutput(const CraftInput &input, IGameDef *gamedef) const;
	virtual CraftInput getInput(const CraftOutput &output, IGameDef *gamedef) const;
	virtual void decrementInput(CraftInput &input,
		std::vector<ItemStack> &output_replacements, IGameDef *gamedef) const;

	virtual CraftHashType getHashType() const { return CRAFT_HASH_TYPE_COUNT; }
	virtual u64 getHash(CraftHashType type) const { return 2; }

	virtual void initHash(IGameDef *gamedef) {}

	virtual std::string dump() const;

private:
	// This is a constant that is added to the wear of the result.
	// May be positive or negative, allowed range [-1,1].
	// 1 = new tool is completely broken
	// 0 = simply add remaining uses of both input tools
	// -1 = new tool is completely pristine
	float additional_wear;
};

/*
	A cooking (in furnace) definition
	Supported crafting method: CRAFT_METHOD_COOKING.
*/
class CraftDefinitionCooking: public CraftDefinition
{
public:
	CraftDefinitionCooking():
		output(""), recipe(""), hash_inited(false), cooktime()
	{}
	CraftDefinitionCooking(
			const std::string &output_,
			const std::string &recipe_,
			float cooktime_,
			const CraftReplacements &replacements_):
		output(output_), recipe(recipe_), hash_inited(false),
		cooktime(cooktime_), replacements(replacements_)
	{}
	virtual ~CraftDefinitionCooking(){}

	virtual std::string getName() const;
	virtual bool check(const CraftInput &input, IGameDef *gamedef) const;
	virtual CraftOutput getOutput(const CraftInput &input, IGameDef *gamedef) const;
	virtual CraftInput getInput(const CraftOutput &output, IGameDef *gamedef) const;
	virtual void decrementInput(CraftInput &input,
		std::vector<ItemStack> &output_replacements, IGameDef *gamedef) const;

	virtual CraftHashType getHashType() const;
	virtual u64 getHash(CraftHashType type) const;

	virtual void initHash(IGameDef *gamedef);

	virtual std::string dump() const;

private:
	// Output itemstring
	std::string output;
	// Recipe itemstring
	std::string recipe;
	// Recipe item name
	std::string recipe_name;
	// bool indicating if initHash has been called already
	bool hash_inited;
	// Time in seconds
	float cooktime;
	// Replacement items for decrementInput()
	CraftReplacements replacements;
};

/*
	A fuel (for furnace) definition
	Supported crafting method: CRAFT_METHOD_FUEL.
*/
class CraftDefinitionFuel: public CraftDefinition
{
public:
	CraftDefinitionFuel():
		recipe(""), hash_inited(false), burntime()
	{}
	CraftDefinitionFuel(const std::string &recipe_,
			float burntime_,
			const CraftReplacements &replacements_):
		recipe(recipe_),
		hash_inited(false),
		burntime(burntime_),
		replacements(replacements_)
	{}
	virtual ~CraftDefinitionFuel(){}

	virtual std::string getName() const;
	virtual bool check(const CraftInput &input, IGameDef *gamedef) const;
	virtual CraftOutput getOutput(const CraftInput &input, IGameDef *gamedef) const;
	virtual CraftInput getInput(const CraftOutput &output, IGameDef *gamedef) const;
	virtual void decrementInput(CraftInput &input,
		std::vector<ItemStack> &output_replacements, IGameDef *gamedef) const;

	virtual CraftHashType getHashType() const;
	virtual u64 getHash(CraftHashType type) const;

	virtual void initHash(IGameDef *gamedef);

	virtual std::string dump() const;

private:
	// Recipe itemstring
	std::string recipe;
	// Recipe item name
	std::string recipe_name;
	// bool indicating if initHash has been called already
	bool hash_inited;
	// Time in seconds
	float burntime;
	// Replacement items for decrementInput()
	CraftReplacements replacements;
};

/*
	Crafting definition manager
*/
class ICraftDefManager
{
public:
	ICraftDefManager(){}
	virtual ~ICraftDefManager(){}

	// The main crafting function
	virtual bool getCraftResult(CraftInput &input, CraftOutput &output,
			std::vector<ItemStack> &output_replacements,
			bool decrementInput, IGameDef *gamedef) const=0;
	virtual std::vector<CraftDefinition*> getCraftRecipes(CraftOutput &output,
			IGameDef *gamedef, unsigned limit=0) const=0;

	// Print crafting recipes for debugging
	virtual std::string dump() const=0;
};

class IWritableCraftDefManager : public ICraftDefManager
{
public:
	IWritableCraftDefManager(){}
	virtual ~IWritableCraftDefManager(){}

	// The main crafting function
	virtual bool getCraftResult(CraftInput &input, CraftOutput &output,
			std::vector<ItemStack> &output_replacements,
			bool decrementInput, IGameDef *gamedef) const=0;
	virtual std::vector<CraftDefinition*> getCraftRecipes(CraftOutput &output,
			IGameDef *gamedef, unsigned limit=0) const=0;

	virtual bool clearCraftRecipesByOutput(const CraftOutput &output, IGameDef *gamedef) = 0;
	virtual bool clearCraftRecipesByInput(CraftMethod craft_method,
			unsigned int craft_grid_width, const std::vector<std::string> &recipe, IGameDef *gamedef) = 0;

	// Print crafting recipes for debugging
	virtual std::string dump() const=0;

	// Add a crafting definition.
	// After calling this, the pointer belongs to the manager.
	virtual void registerCraft(CraftDefinition *def, IGameDef *gamedef) = 0;

	// Delete all crafting definitions
	virtual void clear()=0;

	// To be called after all mods are loaded, so that we catch all aliases
	virtual void initHashes(IGameDef *gamedef) = 0;
};

IWritableCraftDefManager* createCraftDefManager();

#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.
*/

/*
Dummy database class
*/

#include "database-dummy.h"

bool Database_Dummy::saveBlock(const v3s16 &pos, const std::string &data)
{
	m_database[getBlockAsInteger(pos)] = data;
	return true;
}

void Database_Dummy::loadBlock(const v3s16 &pos, std::string *block)
{
	s64 i = getBlockAsInteger(pos);
	std::map<s64, std::string>::iterator it = m_database.find(i);
	if (it == m_database.end()) {
		*block = "";
		return;
	}

	*block = it->second;
}

bool Database_Dummy::deleteBlock(const v3s16 &pos)
{
	m_database.erase(getBlockAsInteger(pos));
	return true;
}

void Database_Dummy::listAllLoadableBlocks(std::vector<v3s16> &dst)
{
	dst.reserve(m_database.size());
	for (std::map<s64, std::string>::const_iterator x = m_database.begin();
			x != m_database.end(); ++x) {
		dst.push_back(getIntegerAsBlock(x->first));
	}
}


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 DATABASE_DUMMY_HEADER
#define DATABASE_DUMMY_HEADER

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

class Database_Dummy : public MapDatabase, public PlayerDatabase
{
public:
	bool saveBlock(const v3s16 &pos, const std::string &data);
	void loadBlock(const v3s16 &pos, std::string *block);
	bool deleteBlock(const v3s16 &pos);
	void listAllLoadableBlocks(std::vector<v3s16> &dst);

	void savePlayer(RemotePlayer *player) {}
	bool loadPlayer(RemotePlayer *player, PlayerSAO *sao) { return true; }
	bool removePlayer(const std::string &name) { return true; }
	void listPlayers(std::vector<std::string> &) {}

	void beginSave() {}
	void endSave() {}
private:
	std::map<s64, std::string> m_database;
};

#endif


code:
/*
Minetest
Copyright (C) 2017 nerzhul, Loic Blot <loic.blot@unix-experience.fr>

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 <cassert>
#include <json/json.h>
#include "database-files.h"
#include "content_sao.h"
#include "remoteplayer.h"
#include "settings.h"
#include "porting.h"
#include "filesys.h"

// !!! WARNING !!!
// This backend is intended to be used on Minetest 0.4.16 only for the transition backend
// for player files

void PlayerDatabaseFiles::serialize(std::ostringstream &os, RemotePlayer *player)
{
	// Utilize a Settings object for storing values
	Settings args;
	args.setS32("version", 1);
	args.set("name", player->getName());

	sanity_check(player->getPlayerSAO());
	args.setS32("hp", player->getPlayerSAO()->getHP());
	args.setV3F("position", player->getPlayerSAO()->getBasePosition());
	args.setFloat("pitch", player->getPlayerSAO()->getPitch());
	args.setFloat("yaw", player->getPlayerSAO()->getYaw());
	args.setS32("breath", player->getPlayerSAO()->getBreath());

	std::string extended_attrs = "";
	player->serializeExtraAttributes(extended_attrs);
	args.set("extended_attributes", extended_attrs);

	args.writeLines(os);

	os << "PlayerArgsEnd\n";

	player->inventory.serialize(os);
}

void PlayerDatabaseFiles::savePlayer(RemotePlayer *player)
{
	std::string savedir = m_savedir + DIR_DELIM;
	std::string path = savedir + player->getName();
	bool path_found = false;
	RemotePlayer testplayer("", NULL);

	for (u32 i = 0; i < PLAYER_FILE_ALTERNATE_TRIES && !path_found; i++) {
		if (!fs::PathExists(path)) {
			path_found = true;
			continue;
		}

		// Open and deserialize file to check player name
		std::ifstream is(path.c_str(), std::ios_base::binary);
		if (!is.good()) {
			errorstream << "Failed to open " << path << std::endl;
			return;
		}

		testplayer.deSerialize(is, path, NULL);
		is.close();
		if (strcmp(testplayer.getName(), player->getName()) == 0) {
			path_found = true;
			continue;
		}

		path = savedir + player->getName() + itos(i);
	}

	if (!path_found) {
		errorstream << "Didn't find free file for player " << player->getName()
				<< std::endl;
		return;
	}

	// Open and serialize file
	std::ostringstream ss(std::ios_base::binary);
	serialize(ss, player);
	if (!fs::safeWriteToFile(path, ss.str())) {
		infostream << "Failed to write " << path << std::endl;
	}
	player->setModified(false);
}

bool PlayerDatabaseFiles::removePlayer(const std::string &name)
{
	std::string players_path = m_savedir + DIR_DELIM;
	std::string path = players_path + name;

	RemotePlayer temp_player("", NULL);
	for (u32 i = 0; i < PLAYER_FILE_ALTERNATE_TRIES; i++) {
		// Open file and deserialize
		std::ifstream is(path.c_str(), std::ios_base::binary);
		if (!is.good())
			continue;

		temp_player.deSerialize(is, path, NULL);
		is.close();

		if (temp_player.getName() == name) {
			fs::DeleteSingleFileOrEmptyDirectory(path);
			return true;
		}

		path = players_path + name + itos(i);
	}

	return false;
}

bool PlayerDatabaseFiles::loadPlayer(RemotePlayer *player, PlayerSAO *sao)
{
	std::string players_path = m_savedir + DIR_DELIM;
	std::string path = players_path + player->getName();

	const std::string player_to_load = player->getName();
	for (u32 i = 0; i < PLAYER_FILE_ALTERNATE_TRIES; i++) {
		// Open file and deserialize
		std::ifstream is(path.c_str(), std::ios_base::binary);
		if (!is.good())
			continue;

		player->deSerialize(is, path, sao);
		is.close();

		if (player->getName() == player_to_load)
			return true;

		path = players_path + player_to_load + itos(i);
	}

	infostream << "Player file for player " << player_to_load << " not found" << std::endl;
	return false;
}

void PlayerDatabaseFiles::listPlayers(std::vector<std::string> &res)
{
	std::vector<fs::DirListNode> files = fs::GetDirListing(m_savedir);
	// list files into players directory
	for (std::vector<fs::DirListNode>::const_iterator it = files.begin(); it !=
		files.end(); ++it) {
		// Ignore directories
		if (it->dir)
			continue;

		const std::string &filename = it->name;
		std::string full_path = m_savedir + DIR_DELIM + filename;
		std::ifstream is(full_path.c_str(), std::ios_base::binary);
		if (!is.good())
			continue;

		RemotePlayer player(filename.c_str(), NULL);
		// Null env & dummy peer_id
		PlayerSAO playerSAO(NULL, &player, 15789, false);

		player.deSerialize(is, "", &playerSAO);
		is.close();

		res.push_back(player.getName());
	}
}


code:
/*
Minetest
Copyright (C) 2017 nerzhul, Loic Blot <loic.blot@unix-experience.fr>

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 DATABASE_FILES_HEADER
#define DATABASE_FILES_HEADER

// !!! WARNING !!!
// This backend is intended to be used on Minetest 0.4.16 only for the transition backend
// for player files

#include "database.h"

class PlayerDatabaseFiles : public PlayerDatabase
{
public:
	PlayerDatabaseFiles(const std::string &savedir) : m_savedir(savedir) {}
	virtual ~PlayerDatabaseFiles() {}

	void savePlayer(RemotePlayer *player);
	bool loadPlayer(RemotePlayer *player, PlayerSAO *sao);
	bool removePlayer(const std::string &name);
	void listPlayers(std::vector<std::string> &res);

private:
	void serialize(std::ostringstream &os, RemotePlayer *player);

	std::string m_savedir;
};

#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 "config.h"

#if USE_LEVELDB

#include "database-leveldb.h"

#include "log.h"
#include "filesys.h"
#include "exceptions.h"
#include "util/string.h"

#include "leveldb/db.h"

#define ENSURE_STATUS_OK(s) \
	if (!(s).ok()) { \
		throw DatabaseException(std::string("LevelDB error: ") + \
				(s).ToString()); \
	}

Database_LevelDB::Database_LevelDB(const std::string &savedir)
{
	leveldb::Options options;
	options.create_if_missing = true;
	leveldb::Status status = leveldb::DB::Open(options,
		savedir + DIR_DELIM + "map.db", &m_database);
	ENSURE_STATUS_OK(status);
}

Database_LevelDB::~Database_LevelDB()
{
	delete m_database;
}

bool Database_LevelDB::saveBlock(const v3s16 &pos, const std::string &data)
{
	leveldb::Status status = m_database->Put(leveldb::WriteOptions(),
			i64tos(getBlockAsInteger(pos)), data);
	if (!status.ok()) {
		warningstream << "saveBlock: LevelDB error saving block "
			<< PP(pos) << ": " << status.ToString() << std::endl;
		return false;
	}

	return true;
}

void Database_LevelDB::loadBlock(const v3s16 &pos, std::string *block)
{
	std::string datastr;
	leveldb::Status status = m_database->Get(leveldb::ReadOptions(),
		i64tos(getBlockAsInteger(pos)), &datastr);

	*block = (status.ok()) ? datastr : "";
}

bool Database_LevelDB::deleteBlock(const v3s16 &pos)
{
	leveldb::Status status = m_database->Delete(leveldb::WriteOptions(),
			i64tos(getBlockAsInteger(pos)));
	if (!status.ok()) {
		warningstream << "deleteBlock: LevelDB error deleting block "
			<< PP(pos) << ": " << status.ToString() << std::endl;
		return false;
	}

	return true;
}

void Database_LevelDB::listAllLoadableBlocks(std::vector<v3s16> &dst)
{
	leveldb::Iterator* it = m_database->NewIterator(leveldb::ReadOptions());
	for (it->SeekToFirst(); it->Valid(); it->Next()) {
		dst.push_back(getIntegerAsBlock(stoi64(it->key().ToString())));
	}
	ENSURE_STATUS_OK(it->status());  // Check for any errors found during the scan
	delete it;
}

#endif // USE_LEVELDB


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 DATABASE_LEVELDB_HEADER
#define DATABASE_LEVELDB_HEADER

#include "config.h"

#if USE_LEVELDB

#include <string>
#include "database.h"
#include "leveldb/db.h"

class Database_LevelDB : public MapDatabase
{
public:
	Database_LevelDB(const std::string &savedir);
	~Database_LevelDB();

	bool saveBlock(const v3s16 &pos, const std::string &data);
	void loadBlock(const v3s16 &pos, std::string *block);
	bool deleteBlock(const v3s16 &pos);
	void listAllLoadableBlocks(std::vector<v3s16> &dst);

	void beginSave() {}
	void endSave() {}
private:
	leveldb::DB *m_database;
};

#endif // USE_LEVELDB

#endif


code:
/*
Copyright (C) 2016 Loic Blot <loic.blot@unix-experience.fr>

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 "config.h"

#if USE_POSTGRESQL

#include "database-postgresql.h"

#ifdef _WIN32
        #ifndef WIN32_LEAN_AND_MEAN
                #define WIN32_LEAN_AND_MEAN
        #endif
        // Without this some of the network functions are not found on mingw
        #ifndef _WIN32_WINNT
                #define _WIN32_WINNT 0x0501
        #endif
        #include <windows.h>
        #include <winsock2.h>
#else
#include <netinet/in.h>
#endif

#include "log.h"
#include "exceptions.h"
#include "settings.h"
#include "content_sao.h"
#include "remoteplayer.h"

Database_PostgreSQL::Database_PostgreSQL(const std::string &connect_string) :
	m_connect_string(connect_string),
	m_conn(NULL),
	m_pgversion(0)
{
	if (m_connect_string.empty()) {
		throw SettingNotFoundException(
			"Set pgsql_connection string in world.mt to "
			"use the postgresql backend\n"
			"Notes:\n"
			"pgsql_connection has the following form: \n"
			"\tpgsql_connection = host=127.0.0.1 port=5432 user=mt_user "
			"password=mt_password dbname=minetest_world\n"
			"mt_user should have CREATE TABLE, INSERT, SELECT, UPDATE and "
			"DELETE rights on the database.\n"
			"Don't create mt_user as a SUPERUSER!");
	}
}

Database_PostgreSQL::~Database_PostgreSQL()
{
	PQfinish(m_conn);
}

void Database_PostgreSQL::connectToDatabase()
{
	m_conn = PQconnectdb(m_connect_string.c_str());

	if (PQstatus(m_conn) != CONNECTION_OK) {
		throw DatabaseException(std::string(
			"PostgreSQL database error: ") +
			PQerrorMessage(m_conn));
	}

	m_pgversion = PQserverVersion(m_conn);

	/*
	* We are using UPSERT feature from PostgreSQL 9.5
	* to have the better performance where possible.
	*/
	if (m_pgversion < 90500) {
		warningstream << "Your PostgreSQL server lacks UPSERT "
			<< "support. Use version 9.5 or better if possible."
			<< std::endl;
	}

	infostream << "PostgreSQL Database: Version " << m_pgversion
			<< " Connection made." << std::endl;

	createDatabase();
	initStatements();
}

void Database_PostgreSQL::verifyDatabase()
{
	if (PQstatus(m_conn) == CONNECTION_OK)
		return;

	PQreset(m_conn);
	ping();
}

void Database_PostgreSQL::ping()
{
	if (PQping(m_connect_string.c_str()) != PQPING_OK) {
		throw DatabaseException(std::string(
			"PostgreSQL database error: ") +
			PQerrorMessage(m_conn));
	}
}

bool Database_PostgreSQL::initialized() const
{
	return (PQstatus(m_conn) == CONNECTION_OK);
}

PGresult *Database_PostgreSQL::checkResults(PGresult *result, bool clear)
{
	ExecStatusType statusType = PQresultStatus(result);

	switch (statusType) {
	case PGRES_COMMAND_OK:
	case PGRES_TUPLES_OK:
		break;
	case PGRES_FATAL_ERROR:
	default:
		throw DatabaseException(
			std::string("PostgreSQL database error: ") +
			PQresultErrorMessage(result));
	}

	if (clear)
		PQclear(result);

	return result;
}

void Database_PostgreSQL::createTableIfNotExists(const std::string &table_name,
		const std::string &definition)
{
	std::string sql_check_table = "SELECT relname FROM pg_class WHERE relname='" +
		table_name + "';";
	PGresult *result = checkResults(PQexec(m_conn, sql_check_table.c_str()), false);

	// If table doesn't exist, create it
	if (!PQntuples(result)) {
		checkResults(PQexec(m_conn, definition.c_str()));
	}

	PQclear(result);
}

void Database_PostgreSQL::beginSave()
{
	verifyDatabase();
	checkResults(PQexec(m_conn, "BEGIN;"));
}

void Database_PostgreSQL::endSave()
{
	checkResults(PQexec(m_conn, "COMMIT;"));
}

MapDatabasePostgreSQL::MapDatabasePostgreSQL(const std::string &connect_string):
	Database_PostgreSQL(connect_string),
	MapDatabase()
{
	connectToDatabase();
}

void MapDatabasePostgreSQL::createDatabase()
{
	createTableIfNotExists("blocks",
		"CREATE TABLE blocks ("
			"posX INT NOT NULL,"
			"posY INT NOT NULL,"
			"posZ INT NOT NULL,"
			"data BYTEA,"
			"PRIMARY KEY (posX,posY,posZ)"
			");"
	);

	infostream << "PostgreSQL: Map Database was initialized." << std::endl;
}

void MapDatabasePostgreSQL::initStatements()
{
	prepareStatement("read_block",
		"SELECT data FROM blocks "
			"WHERE posX = $1::int4 AND posY = $2::int4 AND "
			"posZ = $3::int4");

	if (getPGVersion() < 90500) {
		prepareStatement("write_block_insert",
			"INSERT INTO blocks (posX, posY, posZ, data) SELECT "
				"$1::int4, $2::int4, $3::int4, $4::bytea "
				"WHERE NOT EXISTS (SELECT true FROM blocks "
				"WHERE posX = $1::int4 AND posY = $2::int4 AND "
				"posZ = $3::int4)");

		prepareStatement("write_block_update",
			"UPDATE blocks SET data = $4::bytea "
				"WHERE posX = $1::int4 AND posY = $2::int4 AND "
				"posZ = $3::int4");
	} else {
		prepareStatement("write_block",
			"INSERT INTO blocks (posX, posY, posZ, data) VALUES "
				"($1::int4, $2::int4, $3::int4, $4::bytea) "
				"ON CONFLICT ON CONSTRAINT blocks_pkey DO "
				"UPDATE SET data = $4::bytea");
	}

	prepareStatement("delete_block", "DELETE FROM blocks WHERE "
		"posX = $1::int4 AND posY = $2::int4 AND posZ = $3::int4");

	prepareStatement("list_all_loadable_blocks",
		"SELECT posX, posY, posZ FROM blocks");
}

bool MapDatabasePostgreSQL::saveBlock(const v3s16 &pos, const std::string &data)
{
	// Verify if we don't overflow the platform integer with the mapblock size
	if (data.size() > INT_MAX) {
		errorstream << "Database_PostgreSQL::saveBlock: Data truncation! "
			<< "data.size() over 0xFFFFFFFF (== " << data.size()
			<< ")" << std::endl;
		return false;
	}

	verifyDatabase();

	s32 x, y, z;
	x = htonl(pos.X);
	y = htonl(pos.Y);
	z = htonl(pos.Z);

	const void *args[] = { &x, &y, &z, data.c_str() };
	const int argLen[] = {
		sizeof(x), sizeof(y), sizeof(z), (int)data.size()
	};
	const int argFmt[] = { 1, 1, 1, 1 };

	if (getPGVersion() < 90500) {
		execPrepared("write_block_update", ARRLEN(args), args, argLen, argFmt);
		execPrepared("write_block_insert", ARRLEN(args), args, argLen, argFmt);
	} else {
		execPrepared("write_block", ARRLEN(args), args, argLen, argFmt);
	}
	return true;
}

void MapDatabasePostgreSQL::loadBlock(const v3s16 &pos, std::string *block)
{
	verifyDatabase();

	s32 x, y, z;
	x = htonl(pos.X);
	y = htonl(pos.Y);
	z = htonl(pos.Z);

	const void *args[] = { &x, &y, &z };
	const int argLen[] = { sizeof(x), sizeof(y), sizeof(z) };
	const int argFmt[] = { 1, 1, 1 };

	PGresult *results = execPrepared("read_block", ARRLEN(args), args,
		argLen, argFmt, false);

	*block = "";

	if (PQntuples(results))
		*block = std::string(PQgetvalue(results, 0, 0), PQgetlength(results, 0, 0));

	PQclear(results);
}

bool MapDatabasePostgreSQL::deleteBlock(const v3s16 &pos)
{
	verifyDatabase();

	s32 x, y, z;
	x = htonl(pos.X);
	y = htonl(pos.Y);
	z = htonl(pos.Z);

	const void *args[] = { &x, &y, &z };
	const int argLen[] = { sizeof(x), sizeof(y), sizeof(z) };
	const int argFmt[] = { 1, 1, 1 };

	execPrepared("delete_block", ARRLEN(args), args, argLen, argFmt);

	return true;
}

void MapDatabasePostgreSQL::listAllLoadableBlocks(std::vector<v3s16> &dst)
{
	verifyDatabase();

	PGresult *results = execPrepared("list_all_loadable_blocks", 0,
		NULL, NULL, NULL, false, false);

	int numrows = PQntuples(results);

	for (int row = 0; row < numrows; ++row)
		dst.push_back(pg_to_v3s16(results, 0, 0));

	PQclear(results);
}

/*
 * Player Database
 */
PlayerDatabasePostgreSQL::PlayerDatabasePostgreSQL(const std::string &connect_string):
	Database_PostgreSQL(connect_string),
	PlayerDatabase()
{
	connectToDatabase();
}

void PlayerDatabasePostgreSQL::createDatabase()
{
	createTableIfNotExists("player",
		"CREATE TABLE player ("
			"name VARCHAR(60) NOT NULL,"
			"pitch NUMERIC(15, 7) NOT NULL,"
			"yaw NUMERIC(15, 7) NOT NULL,"
			"posX NUMERIC(15, 7) NOT NULL,"
			"posY NUMERIC(15, 7) NOT NULL,"
			"posZ NUMERIC(15, 7) NOT NULL,"
			"hp INT NOT NULL,"
			"breath INT NOT NULL,"
			"creation_date TIMESTAMP WITHOUT TIME ZONE NOT NULL DEFAULT NOW(),"
			"modification_date TIMESTAMP WITHOUT TIME ZONE NOT NULL DEFAULT NOW(),"
			"PRIMARY KEY (name)"
			");"
	);

	createTableIfNotExists("player_inventories",
		"CREATE TABLE player_inventories ("
			"player VARCHAR(60) NOT NULL,"
			"inv_id INT NOT NULL,"
			"inv_width INT NOT NULL,"
			"inv_name TEXT NOT NULL DEFAULT '',"
			"inv_size INT NOT NULL,"
			"PRIMARY KEY(player, inv_id),"
			"CONSTRAINT player_inventories_fkey FOREIGN KEY (player) REFERENCES "
			"player (name) ON DELETE CASCADE"
			");"
	);

	createTableIfNotExists("player_inventory_items",
		"CREATE TABLE player_inventory_items ("
			"player VARCHAR(60) NOT NULL,"
			"inv_id INT NOT NULL,"
			"slot_id INT NOT NULL,"
			"item TEXT NOT NULL DEFAULT '',"
			"PRIMARY KEY(player, inv_id, slot_id),"
			"CONSTRAINT player_inventory_items_fkey FOREIGN KEY (player) REFERENCES "
			"player (name) ON DELETE CASCADE"
			");"
	);

	createTableIfNotExists("player_metadata",
		"CREATE TABLE player_metadata ("
			"player VARCHAR(60) NOT NULL,"
			"attr VARCHAR(256) NOT NULL,"
			"value TEXT,"
			"PRIMARY KEY(player, attr),"
			"CONSTRAINT player_metadata_fkey FOREIGN KEY (player) REFERENCES "
			"player (name) ON DELETE CASCADE"
			");"
	);

	infostream << "PostgreSQL: Player Database was inited." << std::endl;
}

void PlayerDatabasePostgreSQL::initStatements()
{
	if (getPGVersion() < 90500) {
		prepareStatement("create_player",
			"INSERT INTO player(name, pitch, yaw, posX, posY, posZ, hp, breath) VALUES "
				"($1, $2, $3, $4, $5, $6, $7::int, $8::int)");

		prepareStatement("update_player",
			"UPDATE SET pitch = $2, yaw = $3, posX = $4, posY = $5, posZ = $6, hp = $7::int, "
				"breath = $8::int, modification_date = NOW() WHERE name = $1");
	} else {
		prepareStatement("save_player",
			"INSERT INTO player(name, pitch, yaw, posX, posY, posZ, hp, breath) VALUES "
				"($1, $2, $3, $4, $5, $6, $7::int, $8::int)"
				"ON CONFLICT ON CONSTRAINT player_pkey DO UPDATE SET pitch = $2, yaw = $3, "
				"posX = $4, posY = $5, posZ = $6, hp = $7::int, breath = $8::int, "
				"modification_date = NOW()");
	}

	prepareStatement("remove_player", "DELETE FROM player WHERE name = $1");

	prepareStatement("load_player_list", "SELECT name FROM player");

	prepareStatement("remove_player_inventories",
		"DELETE FROM player_inventories WHERE player = $1");

	prepareStatement("remove_player_inventory_items",
		"DELETE FROM player_inventory_items WHERE player = $1");

	prepareStatement("add_player_inventory",
		"INSERT INTO player_inventories (player, inv_id, inv_width, inv_name, inv_size) VALUES "
			"($1, $2::int, $3::int, $4, $5::int)");

	prepareStatement("add_player_inventory_item",
		"INSERT INTO player_inventory_items (player, inv_id, slot_id, item) VALUES "
			"($1, $2::int, $3::int, $4)");

	prepareStatement("load_player_inventories",
		"SELECT inv_id, inv_width, inv_name, inv_size FROM player_inventories "
			"WHERE player = $1 ORDER BY inv_id");

	prepareStatement("load_player_inventory_items",
		"SELECT slot_id, item FROM player_inventory_items WHERE "
			"player = $1 AND inv_id = $2::int");

	prepareStatement("load_player",
		"SELECT pitch, yaw, posX, posY, posZ, hp, breath FROM player WHERE name = $1");

	prepareStatement("remove_player_metadata",
		"DELETE FROM player_metadata WHERE player = $1");

	prepareStatement("save_player_metadata",
		"INSERT INTO player_metadata (player, attr, value) VALUES ($1, $2, $3)");

	prepareStatement("load_player_metadata",
		"SELECT attr, value FROM player_metadata WHERE player = $1");

}

bool PlayerDatabasePostgreSQL::playerDataExists(const std::string &playername)
{
	verifyDatabase();

	const char *values[] = { playername.c_str() };
	PGresult *results = execPrepared("load_player", 1, values, false);

	bool res = (PQntuples(results) > 0);
	PQclear(results);
	return res;
}

void PlayerDatabasePostgreSQL::savePlayer(RemotePlayer *player)
{
	PlayerSAO* sao = player->getPlayerSAO();
	if (!sao)
		return;

	verifyDatabase();

	v3f pos = sao->getBasePosition();
	std::string pitch = ftos(sao->getPitch());
	std::string yaw = ftos(sao->getYaw());
	std::string posx = ftos(pos.X);
	std::string posy = ftos(pos.Y);
	std::string posz = ftos(pos.Z);
	std::string hp = itos(sao->getHP());
	std::string breath = itos(sao->getBreath());
	const char *values[] = {
		player->getName(),
		pitch.c_str(),
		yaw.c_str(),
		posx.c_str(), posy.c_str(), posz.c_str(),
		hp.c_str(),
		breath.c_str()
	};

	const char* rmvalues[] = { player->getName() };
	beginSave();

	if (getPGVersion() < 90500) {
		if (!playerDataExists(player->getName()))
			execPrepared("create_player", 8, values, true, false);
		else
			execPrepared("update_player", 8, values, true, false);
	}
	else
		execPrepared("save_player", 8, values, true, false);

	// Write player inventories
	execPrepared("remove_player_inventories", 1, rmvalues);
	execPrepared("remove_player_inventory_items", 1, rmvalues);

	std::vector<const InventoryList*> inventory_lists = sao->getInventory()->getLists();
	for (u16 i = 0; i < inventory_lists.size(); i++) {
		const InventoryList* list = inventory_lists[i];
		std::string name = list->getName(), width = itos(list->getWidth()),
			inv_id = itos(i), lsize = itos(list->getSize());

		const char* inv_values[] = {
			player->getName(),
			inv_id.c_str(),
			width.c_str(),
			name.c_str(),
			lsize.c_str()
		};
		execPrepared("add_player_inventory", 5, inv_values);

		for (u32 j = 0; j < list->getSize(); j++) {
			std::ostringstream os;
			list->getItem(j).serialize(os);
			std::string itemStr = os.str(), slotId = itos(j);

			const char* invitem_values[] = {
				player->getName(),
				inv_id.c_str(),
				slotId.c_str(),
				itemStr.c_str()
			};
			execPrepared("add_player_inventory_item", 4, invitem_values);
		}
	}

	execPrepared("remove_player_metadata", 1, rmvalues);
	const PlayerAttributes &attrs = sao->getExtendedAttributes();
	for (PlayerAttributes::const_iterator it = attrs.begin(); it != attrs.end(); ++it) {
		const char *meta_values[] = {
			player->getName(),
			it->first.c_str(),
			it->second.c_str()
		};
		execPrepared("save_player_metadata", 3, meta_values);
	}
	endSave();
}

bool PlayerDatabasePostgreSQL::loadPlayer(RemotePlayer *player, PlayerSAO *sao)
{
	sanity_check(sao);
	verifyDatabase();

	const char *values[] = { player->getName() };
	PGresult *results = execPrepared("load_player", 1, values, false, false);

	// Player not found, return not found
	if (!PQntuples(results)) {
		PQclear(results);
		return false;
	}

	sao->setPitch(pg_to_float(results, 0, 0));
	sao->setYaw(pg_to_float(results, 0, 1));
	sao->setBasePosition(v3f(
		pg_to_float(results, 0, 2),
		pg_to_float(results, 0, 3),
		pg_to_float(results, 0, 4))
	);
	sao->setHPRaw((s16) pg_to_int(results, 0, 5));
	sao->setBreath((u16) pg_to_int(results, 0, 6), false);

	PQclear(results);

	// Load inventory
	results = execPrepared("load_player_inventories", 1, values, false, false);

	int resultCount = PQntuples(results);

	for (int row = 0; row < resultCount; ++row) {
		InventoryList* invList = player->inventory.
			addList(PQgetvalue(results, row, 2), pg_to_uint(results, row, 3));
		invList->setWidth(pg_to_uint(results, row, 1));

		u32 invId = pg_to_uint(results, row, 0);
		std::string invIdStr = itos(invId);

		const char* values2[] = {
			player->getName(),
			invIdStr.c_str()
		};
		PGresult *results2 = execPrepared("load_player_inventory_items", 2,
			values2, false, false);

		int resultCount2 = PQntuples(results2);
		for (int row2 = 0; row2 < resultCount2; row2++) {
			const std::string itemStr = PQgetvalue(results2, row2, 1);
			if (itemStr.length() > 0) {
				ItemStack stack;
				stack.deSerialize(itemStr);
				invList->addItem(pg_to_uint(results2, row2, 0), stack);
			}
		}
		PQclear(results2);
	}

	PQclear(results);

	results = execPrepared("load_player_metadata", 1, values, false);

	int numrows = PQntuples(results);
	for (int row = 0; row < numrows; row++) {
		sao->setExtendedAttribute(PQgetvalue(results, row, 0),PQgetvalue(results, row, 1));
	}

	PQclear(results);

	return true;
}

bool PlayerDatabasePostgreSQL::removePlayer(const std::string &name)
{
	if (!playerDataExists(name))
		return false;

	verifyDatabase();

	const char *values[] = { name.c_str() };
	execPrepared("remove_player", 1, values);

	return true;
}

void PlayerDatabasePostgreSQL::listPlayers(std::vector<std::string> &res)
{
	verifyDatabase();

	PGresult *results = execPrepared("load_player_list", 0, NULL, false);

	int numrows = PQntuples(results);
	for (int row = 0; row < numrows; row++)
		res.push_back(PQgetvalue(results, row, 0));

	PQclear(results);
}

#endif // USE_POSTGRESQL