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

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU Lesser General Public License for more details.

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

#ifndef DRAWSCENE_H_
#define DRAWSCENE_H_

#include "camera.h"
#include "hud.h"
#include "minimap.h"
#include "irrlichttypes_extrabloated.h"

void draw_load_screen(const std::wstring &text, IrrlichtDevice *device,
		gui::IGUIEnvironment *guienv, ITextureSource *tsrc, float dtime = 0,
		int percent = 0, bool clouds = true);

void draw_scene(video::IVideoDriver *driver, scene::ISceneManager *smgr,
		Camera &camera, Client &client, LocalPlayer *player,
		Hud &hud, Minimap *mapper, gui::IGUIEnvironment *guienv,
		const v2u32 &screensize, const video::SColor &skycolor,
		bool show_hud, bool show_minimap);

#endif /* DRAWSCENE_H_ */


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

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU Lesser General Public License for more details.

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

#include "dungeongen.h"
#include "mapgen.h"
#include "voxel.h"
#include "noise.h"
#include "mapblock.h"
#include "mapnode.h"
#include "map.h"
#include "nodedef.h"
#include "profiler.h"
#include "settings.h"

//#define DGEN_USE_TORCHES

NoiseParams nparams_dungeon_density(0.9, 0.5, v3f(500.0, 500.0, 500.0), 0, 2, 0.8, 2.0);
NoiseParams nparams_dungeon_alt_wall(-0.4, 1.0, v3f(40.0, 40.0, 40.0), 32474, 6, 1.1, 2.0);

///////////////////////////////////////////////////////////////////////////////

DungeonGen::DungeonGen(INodeDefManager *ndef,
	GenerateNotifier *gennotify, DungeonParams *dparams)
{
	assert(ndef);

	this->ndef      = ndef;
	this->gennotify = gennotify;

#ifdef DGEN_USE_TORCHES
	c_torch  = ndef->getId("default:torch");
#endif

	if (dparams) {
		memcpy(&dp, dparams, sizeof(dp));
	} else {
		// Default dungeon parameters
		dp.seed = 0;

		dp.c_water       = ndef->getId("mapgen_water_source");
		dp.c_river_water = ndef->getId("mapgen_river_water_source");
		dp.c_wall        = ndef->getId("mapgen_cobble");
		dp.c_alt_wall    = ndef->getId("mapgen_mossycobble");
		dp.c_stair       = ndef->getId("mapgen_stair_cobble");

		if (dp.c_river_water == CONTENT_IGNORE)
			dp.c_river_water = ndef->getId("mapgen_water_source");

		dp.diagonal_dirs       = false;
		dp.only_in_ground      = true;
		dp.holesize            = v3s16(1, 2, 1);
		dp.corridor_len_min    = 1;
		dp.corridor_len_max    = 13;
		dp.room_size_min       = v3s16(4, 4, 4);
		dp.room_size_max       = v3s16(8, 6, 8);
		dp.room_size_large_min = v3s16(8, 8, 8);
		dp.room_size_large_max = v3s16(16, 16, 16);
		dp.rooms_min           = 2;
		dp.rooms_max           = 16;
		dp.y_min               = -MAX_MAP_GENERATION_LIMIT;
		dp.y_max               = MAX_MAP_GENERATION_LIMIT;
		dp.notifytype          = GENNOTIFY_DUNGEON;

		dp.np_density  = nparams_dungeon_density;
		dp.np_alt_wall = nparams_dungeon_alt_wall;
	}
}

void DungeonGen::generate(MMVManip *vm, u32 bseed, v3s16 nmin, v3s16 nmax)
{
	assert(vm);

	//TimeTaker t("gen dungeons");
	if (nmin.Y < dp.y_min || nmax.Y > dp.y_max)
		return;

	float nval_density = NoisePerlin3D(&dp.np_density, nmin.X, nmin.Y, nmin.Z, dp.seed);
	if (nval_density < 1.0f)
		return;

	this->vm = vm;
	this->blockseed = bseed;
	random.seed(bseed + 2);

	// Dungeon generator doesn't modify places which have this set
	vm->clearFlag(VMANIP_FLAG_DUNGEON_INSIDE | VMANIP_FLAG_DUNGEON_PRESERVE);

	if (dp.only_in_ground) {
		// Set all air and water to be untouchable
		// to make dungeons open to caves and open air
		for (s16 z = nmin.Z; z <= nmax.Z; z++) {
			for (s16 y = nmin.Y; y <= nmax.Y; y++) {
				u32 i = vm->m_area.index(nmin.X, y, z);
				for (s16 x = nmin.X; x <= nmax.X; x++) {
					content_t c = vm->m_data[i].getContent();
					if (c == CONTENT_AIR || c == dp.c_water ||
							c == dp.c_river_water)
						vm->m_flags[i] |= VMANIP_FLAG_DUNGEON_PRESERVE;
					i++;
				}
			}
		}
	}

	// Add them
	for (u32 i = 0; i < floor(nval_density); i++)
		makeDungeon(v3s16(1, 1, 1) * MAP_BLOCKSIZE);

	// Optionally convert some structure to alternative structure
	if (dp.c_alt_wall == CONTENT_IGNORE)
		return;

	for (s16 z = nmin.Z; z <= nmax.Z; z++)
	for (s16 y = nmin.Y; y <= nmax.Y; y++) {
		u32 i = vm->m_area.index(nmin.X, y, z);
		for (s16 x = nmin.X; x <= nmax.X; x++) {
			if (vm->m_data[i].getContent() == dp.c_wall) {
				if (NoisePerlin3D(&dp.np_alt_wall, x, y, z, blockseed) > 0.0f)
					vm->m_data[i].setContent(dp.c_alt_wall);
			}
			i++;
		}
	}

	//printf("== gen dungeons: %dms\n", t.stop());
}

void DungeonGen::makeDungeon(v3s16 start_padding)
{
	v3s16 areasize = vm->m_area.getExtent();
	v3s16 roomsize;
	v3s16 roomplace;

	/*
		Find place for first room.
		There is a 1 in 4 chance of the first room being 'large',
		all other rooms are not 'large'.
	*/
	bool fits = false;
	for (u32 i = 0; i < 100 && !fits; i++) {
		bool is_large_room = ((random.next() & 3) == 1);
		if (is_large_room) {
			roomsize.Z = random.range(
				dp.room_size_large_min.Z, dp.room_size_large_max.Z);
			roomsize.Y = random.range(
				dp.room_size_large_min.Y, dp.room_size_large_max.Y);
			roomsize.X = random.range(
				dp.room_size_large_min.X, dp.room_size_large_max.X);
		} else {
			roomsize.Z = random.range(dp.room_size_min.Z, dp.room_size_max.Z);
			roomsize.Y = random.range(dp.room_size_min.Y, dp.room_size_max.Y);
			roomsize.X = random.range(dp.room_size_min.X, dp.room_size_max.X);
		}

		// start_padding is used to disallow starting the generation of
		// a dungeon in a neighboring generation chunk
		roomplace = vm->m_area.MinEdge + start_padding;
		roomplace.Z += random.range(0, areasize.Z - roomsize.Z - start_padding.Z);
		roomplace.Y += random.range(0, areasize.Y - roomsize.Y - start_padding.Y);
		roomplace.X += random.range(0, areasize.X - roomsize.X - start_padding.X);

		/*
			Check that we're not putting the room to an unknown place,
			otherwise it might end up floating in the air
		*/
		fits = true;
		for (s16 z = 0; z < roomsize.Z; z++)
		for (s16 y = 0; y < roomsize.Y; y++)
		for (s16 x = 0; x < roomsize.X; x++) {
			v3s16 p = roomplace + v3s16(x, y, z);
			u32 vi = vm->m_area.index(p);
			if ((vm->m_flags[vi] & VMANIP_FLAG_DUNGEON_UNTOUCHABLE) ||
					vm->m_data[vi].getContent() == CONTENT_IGNORE) {
				fits = false;
				break;
			}
		}
	}
	// No place found
	if (fits == false)
		return;

	/*
		Stores the center position of the last room made, so that
		a new corridor can be started from the last room instead of
		the new room, if chosen so.
	*/
	v3s16 last_room_center = roomplace + v3s16(roomsize.X / 2, 1, roomsize.Z / 2);

	u32 room_count = random.range(dp.rooms_min, dp.rooms_max);
	for (u32 i = 0; i < room_count; i++) {
		// Make a room to the determined place
		makeRoom(roomsize, roomplace);

		v3s16 room_center = roomplace + v3s16(roomsize.X / 2, 1, roomsize.Z / 2);
		if (gennotify)
			gennotify->addEvent(dp.notifytype, room_center);

#ifdef DGEN_USE_TORCHES
		// Place torch at room center (for testing)
		vm->m_data[vm->m_area.index(room_center)] = MapNode(c_torch);
#endif

		// Quit if last room
		if (i == room_count - 1)
			break;

		// Determine walker start position

		bool start_in_last_room = (random.range(0, 2) != 0);

		v3s16 walker_start_place;

		if (start_in_last_room) {
			walker_start_place = last_room_center;
		} else {
			walker_start_place = room_center;
			// Store center of current room as the last one
			last_room_center = room_center;
		}

		// Create walker and find a place for a door
		v3s16 doorplace;
		v3s16 doordir;

		m_pos = walker_start_place;
		if (!findPlaceForDoor(doorplace, doordir))
			return;

		if (random.range(0, 1) == 0)
			// Make the door
			makeDoor(doorplace, doordir);
		else
			// Don't actually make a door
			doorplace -= doordir;

		// Make a random corridor starting from the door
		v3s16 corridor_end;
		v3s16 corridor_end_dir;
		makeCorridor(doorplace, doordir, corridor_end, corridor_end_dir);

		// Find a place for a random sized room
		roomsize.Z = random.range(dp.room_size_min.Z, dp.room_size_max.Z);
		roomsize.Y = random.range(dp.room_size_min.Y, dp.room_size_max.Y);
		roomsize.X = random.range(dp.room_size_min.X, dp.room_size_max.X);

		m_pos = corridor_end;
		m_dir = corridor_end_dir;
		if (!findPlaceForRoomDoor(roomsize, doorplace, doordir, roomplace))
			return;

		if (random.range(0, 1) == 0)
			// Make the door
			makeDoor(doorplace, doordir);
		else
			// Don't actually make a door
			roomplace -= doordir;

	}
}

void DungeonGen::makeRoom(v3s16 roomsize, v3s16 roomplace)
{
	MapNode n_wall(dp.c_wall);
	MapNode n_air(CONTENT_AIR);

	// Make +-X walls
	for (s16 z = 0; z < roomsize.Z; z++)
	for (s16 y = 0; y < roomsize.Y; y++) {
		{
			v3s16 p = roomplace + v3s16(0, y, z);
			if (!vm->m_area.contains(p))
				continue;
			u32 vi = vm->m_area.index(p);
			if (vm->m_flags[vi] & VMANIP_FLAG_DUNGEON_UNTOUCHABLE)
				continue;
			vm->m_data[vi] = n_wall;
		}
		{
			v3s16 p = roomplace + v3s16(roomsize.X - 1, y, z);
			if (!vm->m_area.contains(p))
				continue;
			u32 vi = vm->m_area.index(p);
			if (vm->m_flags[vi] & VMANIP_FLAG_DUNGEON_UNTOUCHABLE)
				continue;
			vm->m_data[vi] = n_wall;
		}
	}

	// Make +-Z walls
	for (s16 x = 0; x < roomsize.X; x++)
	for (s16 y = 0; y < roomsize.Y; y++) {
		{
			v3s16 p = roomplace + v3s16(x, y, 0);
			if (!vm->m_area.contains(p))
				continue;
			u32 vi = vm->m_area.index(p);
			if (vm->m_flags[vi] & VMANIP_FLAG_DUNGEON_UNTOUCHABLE)
				continue;
			vm->m_data[vi] = n_wall;
		}
		{
			v3s16 p = roomplace + v3s16(x, y, roomsize.Z - 1);
			if (!vm->m_area.contains(p))
				continue;
			u32 vi = vm->m_area.index(p);
			if (vm->m_flags[vi] & VMANIP_FLAG_DUNGEON_UNTOUCHABLE)
				continue;
			vm->m_data[vi] = n_wall;
		}
	}

	// Make +-Y walls (floor and ceiling)
	for (s16 z = 0; z < roomsize.Z; z++)
	for (s16 x = 0; x < roomsize.X; x++) {
		{
			v3s16 p = roomplace + v3s16(x, 0, z);
			if (!vm->m_area.contains(p))
				continue;
			u32 vi = vm->m_area.index(p);
			if (vm->m_flags[vi] & VMANIP_FLAG_DUNGEON_UNTOUCHABLE)
				continue;
			vm->m_data[vi] = n_wall;
		}
		{
			v3s16 p = roomplace + v3s16(x,roomsize. Y - 1, z);
			if (!vm->m_area.contains(p))
				continue;
			u32 vi = vm->m_area.index(p);
			if (vm->m_flags[vi] & VMANIP_FLAG_DUNGEON_UNTOUCHABLE)
				continue;
			vm->m_data[vi] = n_wall;
		}
	}

	// Fill with air
	for (s16 z = 1; z < roomsize.Z - 1; z++)
	for (s16 y = 1; y < roomsize.Y - 1; y++)
	for (s16 x = 1; x < roomsize.X - 1; x++) {
		v3s16 p = roomplace + v3s16(x, y, z);
		if (!vm->m_area.contains(p))
			continue;
		u32 vi = vm->m_area.index(p);
		vm->m_flags[vi] |= VMANIP_FLAG_DUNGEON_UNTOUCHABLE;
		vm->m_data[vi] = n_air;
	}
}

void DungeonGen::makeFill(v3s16 place, v3s16 size,
	u8 avoid_flags, MapNode n, u8 or_flags)
{
	for (s16 z = 0; z < size.Z; z++)
	for (s16 y = 0; y < size.Y; y++)
	for (s16 x = 0; x < size.X; x++) {
		v3s16 p = place + v3s16(x, y, z);
		if (!vm->m_area.contains(p))
			continue;
		u32 vi = vm->m_area.index(p);
		if (vm->m_flags[vi] & avoid_flags)
			continue;
		vm->m_flags[vi] |= or_flags;
		vm->m_data[vi] = n;
	}
}

void DungeonGen::makeHole(v3s16 place)
{
	makeFill(place, dp.holesize, 0, MapNode(CONTENT_AIR),
		VMANIP_FLAG_DUNGEON_INSIDE);
}

void DungeonGen::makeDoor(v3s16 doorplace, v3s16 doordir)
{
	makeHole(doorplace);

#ifdef DGEN_USE_TORCHES
	// Place torch (for testing)
	vm->m_data[vm->m_area.index(doorplace)] = MapNode(c_torch);
#endif
}

void DungeonGen::makeCorridor(v3s16 doorplace, v3s16 doordir,
	v3s16 &result_place, v3s16 &result_dir)
{
	makeHole(doorplace);
	v3s16 p0 = doorplace;
	v3s16 dir = doordir;
	u32 length = random.range(dp.corridor_len_min, dp.corridor_len_max);
	u32 partlength = random.range(dp.corridor_len_min, dp.corridor_len_max);
	u32 partcount = 0;
	s16 make_stairs = 0;

	if (random.next() % 2 == 0 && partlength >= 3)
		make_stairs = random.next() % 2 ? 1 : -1;

	for (u32 i = 0; i < length; i++) {
		v3s16 p = p0 + dir;
		if (partcount != 0)
			p.Y += make_stairs;

		// Check segment of minimum size corridor is in voxelmanip
		if (vm->m_area.contains(p) && vm->m_area.contains(p + v3s16(0, 1, 0))) {
			if (make_stairs) {
				makeFill(p + v3s16(-1, -1, -1),
					dp.holesize + v3s16(2, 3, 2),
					VMANIP_FLAG_DUNGEON_UNTOUCHABLE,
					MapNode(dp.c_wall),
					0);
				makeHole(p);
				makeHole(p - dir);

				// TODO: fix stairs code so it works 100%
				// (quite difficult)

				// exclude stairs from the bottom step
				// exclude stairs from diagonal steps
				if (((dir.X ^ dir.Z) & 1) &&
						(((make_stairs ==  1) && i != 0) ||
						((make_stairs == -1) && i != length - 1))) {
					// rotate face 180 deg if
					// making stairs backwards
					int facedir = dir_to_facedir(dir * make_stairs);
					v3s16 ps = p;
					u16 stair_width = (dir.Z != 0) ? dp.holesize.X : dp.holesize.Z;
					// Stair width direction vector
					v3s16 swv = (dir.Z != 0) ? v3s16(1, 0, 0) : v3s16(0, 0, 1);

					for (u16 st = 0; st < stair_width; st++) {
						u32 vi = vm->m_area.index(ps.X - dir.X, ps.Y - 1, ps.Z - dir.Z);
						if (vm->m_area.contains(ps + v3s16(-dir.X, -1, -dir.Z)) &&
								vm->m_data[vi].getContent() == dp.c_wall)
							vm->m_data[vi] = MapNode(dp.c_stair, 0, facedir);

						vi = vm->m_area.index(ps.X, ps.Y, ps.Z);
						if (vm->m_area.contains(ps) &&
								vm->m_data[vi].getContent() == dp.c_wall)
							vm->m_data[vi] = MapNode(dp.c_stair, 0, facedir);

						ps += swv;
					}
				}
			} else {
				makeFill(p + v3s16(-1, -1, -1),
					dp.holesize + v3s16(2, 2, 2),
					VMANIP_FLAG_DUNGEON_UNTOUCHABLE,
					MapNode(dp.c_wall),
					0);
				makeHole(p);
			}

			p0 = p;
		} else {
			// Can't go here, turn away
			dir = turn_xz(dir, random.range(0, 1));
			make_stairs = -make_stairs;
			partcount = 0;
			partlength = random.range(1, length);
			continue;
		}

		partcount++;
		if (partcount >= partlength) {
			partcount = 0;

			dir = random_turn(random, dir);

			partlength = random.range(1, length);

			make_stairs = 0;
			if (random.next() % 2 == 0 && partlength >= 3)
				make_stairs = random.next() % 2 ? 1 : -1;
		}
	}
	result_place = p0;
	result_dir = dir;
}

bool DungeonGen::findPlaceForDoor(v3s16 &result_place, v3s16 &result_dir)
{
	for (u32 i = 0; i < 100; i++) {
		v3s16 p = m_pos + m_dir;
		v3s16 p1 = p + v3s16(0, 1, 0);
		if (!vm->m_area.contains(p) || !vm->m_area.contains(p1) || i % 4 == 0) {
			randomizeDir();
			continue;
		}
		if (vm->getNodeNoExNoEmerge(p).getContent() == dp.c_wall &&
				vm->getNodeNoExNoEmerge(p1).getContent() == dp.c_wall) {
			// Found wall, this is a good place!
			result_place = p;
			result_dir = m_dir;
			// Randomize next direction
			randomizeDir();
			return true;
		}
		/*
			Determine where to move next
		*/
		// Jump one up if the actual space is there
		if (vm->getNodeNoExNoEmerge(p +
				v3s16(0, 0, 0)).getContent() == dp.c_wall &&
				vm->getNodeNoExNoEmerge(p +
				v3s16(0, 1, 0)).getContent() == CONTENT_AIR &&
				vm->getNodeNoExNoEmerge(p +
				v3s16(0, 2, 0)).getContent() == CONTENT_AIR)
			p += v3s16(0,1,0);
		// Jump one down if the actual space is there
		if (vm->getNodeNoExNoEmerge(p +
				v3s16(0, 1, 0)).getContent() == dp.c_wall &&
				vm->getNodeNoExNoEmerge(p +
				v3s16(0, 0, 0)).getContent() == CONTENT_AIR &&
				vm->getNodeNoExNoEmerge(p +
				v3s16(0, -1, 0)).getContent() == CONTENT_AIR)
			p += v3s16(0, -1, 0);
		// Check if walking is now possible
		if (vm->getNodeNoExNoEmerge(p).getContent() != CONTENT_AIR ||
				vm->getNodeNoExNoEmerge(p +
				v3s16(0, 1, 0)).getContent() != CONTENT_AIR) {
			// Cannot continue walking here
			randomizeDir();
			continue;
		}
		// Move there
		m_pos = p;
	}
	return false;
}

bool DungeonGen::findPlaceForRoomDoor(v3s16 roomsize, v3s16 &result_doorplace,
	v3s16 &result_doordir, v3s16 &result_roomplace)
{
	for (s16 trycount = 0; trycount < 30; trycount++) {
		v3s16 doorplace;
		v3s16 doordir;
		bool r = findPlaceForDoor(doorplace, doordir);
		if (r == false)
			continue;
		v3s16 roomplace;
		// X east, Z north, Y up
		if (doordir == v3s16(1, 0, 0)) // X+
			roomplace = doorplace +
				v3s16(0, -1, random.range(-roomsize.Z + 2, -2));
		if (doordir == v3s16(-1, 0, 0)) // X-
			roomplace = doorplace +
				v3s16(-roomsize.X + 1, -1, random.range(-roomsize.Z + 2, -2));
		if (doordir == v3s16(0, 0, 1)) // Z+
			roomplace = doorplace +
				v3s16(random.range(-roomsize.X + 2, -2), -1, 0);
		if (doordir == v3s16(0, 0, -1)) // Z-
			roomplace = doorplace +
				v3s16(random.range(-roomsize.X + 2, -2), -1, -roomsize.Z + 1);

		// Check fit
		bool fits = true;
		for (s16 z = 1; z < roomsize.Z - 1; z++)
		for (s16 y = 1; y < roomsize.Y - 1; y++)
		for (s16 x = 1; x < roomsize.X - 1; x++) {
			v3s16 p = roomplace + v3s16(x, y, z);
			if (!vm->m_area.contains(p)) {
				fits = false;
				break;
			}
			if (vm->m_flags[vm->m_area.index(p)] & VMANIP_FLAG_DUNGEON_INSIDE) {
				fits = false;
				break;
			}
		}
		if (fits == false) {
			// Find new place
			continue;
		}
		result_doorplace = doorplace;
		result_doordir   = doordir;
		result_roomplace = roomplace;
		return true;
	}
	return false;
}

v3s16 rand_ortho_dir(PseudoRandom &random, bool diagonal_dirs)
{
	// Make diagonal directions somewhat rare
	if (diagonal_dirs && (random.next() % 4 == 0)) {
		v3s16 dir;
		int trycount = 0;

		do {
			trycount++;

			dir.Z = random.next() % 3 - 1;
			dir.Y = 0;
			dir.X = random.next() % 3 - 1;
		} while ((dir.X == 0 || dir.Z == 0) && trycount < 10);

		return dir;
	} else {
		if (random.next() % 2 == 0)
			return random.next() % 2 ? v3s16(-1, 0, 0) : v3s16(1, 0, 0);
		else
			return random.next() % 2 ? v3s16(0, 0, -1) : v3s16(0, 0, 1);
	}
}

v3s16 turn_xz(v3s16 olddir, int t)
{
	v3s16 dir;
	if (t == 0) {
		// Turn right
		dir.X = olddir.Z;
		dir.Z = -olddir.X;
		dir.Y = olddir.Y;
	} else {
		// Turn left
		dir.X = -olddir.Z;
		dir.Z = olddir.X;
		dir.Y = olddir.Y;
	}
	return dir;
}

v3s16 random_turn(PseudoRandom &random, v3s16 olddir)
{
	int turn = random.range(0, 2);
	v3s16 dir;
	if (turn == 0)
		// Go straight
		dir = olddir;
	else if (turn == 1)
		// Turn right
		dir = turn_xz(olddir, 0);
	else
		// Turn left
		dir = turn_xz(olddir, 1);
	return dir;
}

int dir_to_facedir(v3s16 d)
{
	if (abs(d.X) > abs(d.Z))
		return d.X < 0 ? 3 : 1;
	else
		return d.Z < 0 ? 2 : 0;
}

repo_name: pgimeno/minetest

license: unknown


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

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU Lesser General Public License for more details.

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

#ifndef DUNGEONGEN_HEADER
#define DUNGEONGEN_HEADER

#include "voxel.h"
#include "noise.h"
#include "mapgen.h"

#define VMANIP_FLAG_DUNGEON_INSIDE VOXELFLAG_CHECKED1
#define VMANIP_FLAG_DUNGEON_PRESERVE VOXELFLAG_CHECKED2
#define VMANIP_FLAG_DUNGEON_UNTOUCHABLE (\
		VMANIP_FLAG_DUNGEON_INSIDE|VMANIP_FLAG_DUNGEON_PRESERVE)

class MMVManip;
class INodeDefManager;

v3s16 rand_ortho_dir(PseudoRandom &random, bool diagonal_dirs);
v3s16 turn_xz(v3s16 olddir, int t);
v3s16 random_turn(PseudoRandom &random, v3s16 olddir);
int dir_to_facedir(v3s16 d);

struct DungeonParams {
	s32 seed;

	content_t c_water;
	content_t c_river_water;
	content_t c_wall;
	content_t c_alt_wall;
	content_t c_stair;

	bool diagonal_dirs;
	bool only_in_ground;
	v3s16 holesize;
	u16 corridor_len_min;
	u16 corridor_len_max;
	v3s16 room_size_min;
	v3s16 room_size_max;
	v3s16 room_size_large_min;
	v3s16 room_size_large_max;
	u16 rooms_min;
	u16 rooms_max;
	s16 y_min;
	s16 y_max;
	GenNotifyType notifytype;

	NoiseParams np_density;
	NoiseParams np_alt_wall;
};

class DungeonGen {
public:
	MMVManip *vm;
	INodeDefManager *ndef;
	GenerateNotifier *gennotify;

	u32 blockseed;
	PseudoRandom random;
	v3s16 csize;

	content_t c_torch;
	DungeonParams dp;

	// RoomWalker
	v3s16 m_pos;
	v3s16 m_dir;

	DungeonGen(INodeDefManager *ndef,
		GenerateNotifier *gennotify, DungeonParams *dparams);

	void generate(MMVManip *vm, u32 bseed,
		v3s16 full_node_min, v3s16 full_node_max);

	void makeDungeon(v3s16 start_padding);
	void makeRoom(v3s16 roomsize, v3s16 roomplace);
	void makeCorridor(v3s16 doorplace, v3s16 doordir,
		v3s16 &result_place, v3s16 &result_dir);
	void makeDoor(v3s16 doorplace, v3s16 doordir);
	void makeFill(v3s16 place, v3s16 size, u8 avoid_flags, MapNode n, u8 or_flags);
	void makeHole(v3s16 place);

	bool findPlaceForDoor(v3s16 &result_place, v3s16 &result_dir);
	bool findPlaceForRoomDoor(v3s16 roomsize, v3s16 &result_doorplace,
			v3s16 &result_doordir, v3s16 &result_roomplace);

	inline void randomizeDir()
	{
		m_dir = rand_ortho_dir(random, dp.diagonal_dirs);
	}
};

extern NoiseParams nparams_dungeon_density;
extern NoiseParams nparams_dungeon_alt_wall;

#endif


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

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU Lesser General Public License for more details.

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

#include "emerge.h"

#include <iostream>
#include <queue>

#include "util/container.h"
#include "util/thread.h"
#include "threading/event.h"

#include "config.h"
#include "constants.h"
#include "environment.h"
#include "log.h"
#include "map.h"
#include "mapblock.h"
#include "mg_biome.h"
#include "mg_ore.h"
#include "mg_decoration.h"
#include "mg_schematic.h"
#include "nodedef.h"
#include "profiler.h"
#include "scripting_server.h"
#include "server.h"
#include "serverobject.h"
#include "settings.h"
#include "voxel.h"

class EmergeThread : public Thread {
public:
	bool enable_mapgen_debug_info;
	int id;

	EmergeThread(Server *server, int ethreadid);
	~EmergeThread();

	void *run();
	void signal();

	// Requires queue mutex held
	bool pushBlock(v3s16 pos);

	void cancelPendingItems();

	static void runCompletionCallbacks(
		v3s16 pos, EmergeAction action,
		const EmergeCallbackList &callbacks);

private:
	Server *m_server;
	ServerMap *m_map;
	EmergeManager *m_emerge;
	Mapgen *m_mapgen;

	Event m_queue_event;
	std::queue<v3s16> m_block_queue;

	bool popBlockEmerge(v3s16 *pos, BlockEmergeData *bedata);

	EmergeAction getBlockOrStartGen(
		v3s16 pos, bool allow_gen, MapBlock **block, BlockMakeData *data);
	MapBlock *finishGen(v3s16 pos, BlockMakeData *bmdata,
		std::map<v3s16, MapBlock *> *modified_blocks);

	friend class EmergeManager;
};

////
//// EmergeManager
////

EmergeManager::EmergeManager(Server *server)
{
	this->ndef      = server->getNodeDefManager();
	this->biomemgr  = new BiomeManager(server);
	this->oremgr    = new OreManager(server);
	this->decomgr   = new DecorationManager(server);
	this->schemmgr  = new SchematicManager(server);
	this->gen_notify_on = 0;

	// Note that accesses to this variable are not synchronized.
	// This is because the *only* thread ever starting or stopping
	// EmergeThreads should be the ServerThread.
	this->m_threads_active = false;

	enable_mapgen_debug_info = g_settings->getBool("enable_mapgen_debug_info");

	// If unspecified, leave a proc for the main thread and one for
	// some other misc thread
	s16 nthreads = 0;
	if (!g_settings->getS16NoEx("num_emerge_threads", nthreads))
		nthreads = Thread::getNumberOfProcessors() - 2;
	if (nthreads < 1)
		nthreads = 1;

	m_qlimit_total = g_settings->getU16("emergequeue_limit_total");
	if (!g_settings->getU16NoEx("emergequeue_limit_diskonly", m_qlimit_diskonly))
		m_qlimit_diskonly = nthreads * 5 + 1;
	if (!g_settings->getU16NoEx("emergequeue_limit_generate", m_qlimit_generate))
		m_qlimit_generate = nthreads + 1;

	// don't trust user input for something very important like this
	if (m_qlimit_total < 1)
		m_qlimit_total = 1;
	if (m_qlimit_diskonly < 1)
		m_qlimit_diskonly = 1;
	if (m_qlimit_generate < 1)
		m_qlimit_generate = 1;

	for (s16 i = 0; i < nthreads; i++)
		m_threads.push_back(new EmergeThread(server, i));

	infostream << "EmergeManager: using " << nthreads << " threads" << std::endl;
}

EmergeManager::~EmergeManager()
{
	for (u32 i = 0; i != m_threads.size(); i++) {
		EmergeThread *thread = m_threads[i];

		if (m_threads_active) {
			thread->stop();
			thread->signal();
			thread->wait();
		}

		delete thread;
		delete m_mapgens[i];
	}

	delete biomemgr;
	delete oremgr;
	delete decomgr;
	delete schemmgr;
}

bool EmergeManager::initMapgens(MapgenParams *params)
{
	if (m_mapgens.size())
		return false;

	this->mgparams = params;

	for (u32 i = 0; i != m_threads.size(); i++) {
		Mapgen *mg = Mapgen::createMapgen(params->mgtype, i, params, this);
		m_mapgens.push_back(mg);
	}

	return true;
}

Mapgen *EmergeManager::getCurrentMapgen()
{
	if (!m_threads_active)
		return NULL;

	for (u32 i = 0; i != m_threads.size(); i++) {
		if (m_threads[i]->isCurrentThread())
			return m_threads[i]->m_mapgen;
	}

	return NULL;
}

void EmergeManager::startThreads()
{
	if (m_threads_active)
		return;

	for (u32 i = 0; i != m_threads.size(); i++)
		m_threads[i]->start();

	m_threads_active = true;
}

void EmergeManager::stopThreads()
{
	if (!m_threads_active)
		return;

	// Request thread stop in parallel
	for (u32 i = 0; i != m_threads.size(); i++) {
		m_threads[i]->stop();
		m_threads[i]->signal();
	}

	// Then do the waiting for each
	for (u32 i = 0; i != m_threads.size(); i++)
		m_threads[i]->wait();

	m_threads_active = false;
}

bool EmergeManager::isRunning()
{
	return m_threads_active;
}

bool EmergeManager::enqueueBlockEmerge(
	u16 peer_id,
	v3s16 blockpos,
	bool allow_generate,
	bool ignore_queue_limits)
{
	u16 flags = 0;
	if (allow_generate)
		flags |= BLOCK_EMERGE_ALLOW_GEN;
	if (ignore_queue_limits)
		flags |= BLOCK_EMERGE_FORCE_QUEUE;

	return enqueueBlockEmergeEx(blockpos, peer_id, flags, NULL, NULL);
}

bool EmergeManager::enqueueBlockEmergeEx(
	v3s16 blockpos,
	u16 peer_id,
	u16 flags,
	EmergeCompletionCallback callback,
	void *callback_param)
{
	EmergeThread *thread = NULL;
	bool entry_already_exists = false;

	{
		MutexAutoLock queuelock(m_queue_mutex);

		if (!pushBlockEmergeData(blockpos, peer_id, flags,
				callback, callback_param, &entry_already_exists))
			return false;

		if (entry_already_exists)
			return true;

		thread = getOptimalThread();
		thread->pushBlock(blockpos);
	}

	thread->signal();

	return true;
}

//
// Mapgen-related helper functions
//

// TODO(hmmmm): Move this to ServerMap
v3s16 EmergeManager::getContainingChunk(v3s16 blockpos)
{
	return getContainingChunk(blockpos, mgparams->chunksize);
}

// TODO(hmmmm): Move this to ServerMap
v3s16 EmergeManager::getContainingChunk(v3s16 blockpos, s16 chunksize)
{
	s16 coff = -chunksize / 2;
	v3s16 chunk_offset(coff, coff, coff);

	return getContainerPos(blockpos - chunk_offset, chunksize)
		* chunksize + chunk_offset;
}

int EmergeManager::getSpawnLevelAtPoint(v2s16 p)
{
	if (m_mapgens.size() == 0 || !m_mapgens[0]) {
		errorstream << "EmergeManager: getSpawnLevelAtPoint() called"
			" before mapgen init" << std::endl;
		return 0;
	}

	return m_mapgens[0]->getSpawnLevelAtPoint(p);
}

int EmergeManager::getGroundLevelAtPoint(v2s16 p)
{
	if (m_mapgens.size() == 0 || !m_mapgens[0]) {
		errorstream << "EmergeManager: getGroundLevelAtPoint() called"
			" before mapgen init" << std::endl;
		return 0;
	}

	return m_mapgens[0]->getGroundLevelAtPoint(p);
}

// TODO(hmmmm): Move this to ServerMap
bool EmergeManager::isBlockUnderground(v3s16 blockpos)
{
#if 0
	v2s16 p = v2s16((blockpos.X * MAP_BLOCKSIZE) + MAP_BLOCKSIZE / 2,
					(blockpos.Y * MAP_BLOCKSIZE) + MAP_BLOCKSIZE / 2);
	int ground_level = getGroundLevelAtPoint(p);
	return blockpos.Y * (MAP_BLOCKSIZE + 1) <= min(water_level, ground_level);
#endif

	// Use a simple heuristic; the above method is wildly inaccurate anyway.
	return blockpos.Y * (MAP_BLOCKSIZE + 1) <= mgparams->water_level;
}

bool EmergeManager::pushBlockEmergeData(
	v3s16 pos,
	u16 peer_requested,
	u16 flags,
	EmergeCompletionCallback callback,
	void *callback_param,
	bool *entry_already_exists)
{
	u16 &count_peer = m_peer_queue_count[peer_requested];

	if ((flags & BLOCK_EMERGE_FORCE_QUEUE) == 0) {
		if (m_blocks_enqueued.size() >= m_qlimit_total)
			return false;

		if (peer_requested != PEER_ID_INEXISTENT) {
			u16 qlimit_peer = (flags & BLOCK_EMERGE_ALLOW_GEN) ?
				m_qlimit_generate : m_qlimit_diskonly;
			if (count_peer >= qlimit_peer)
				return false;
		}
	}

	std::pair<std::map<v3s16, BlockEmergeData>::iterator, bool> findres;
	findres = m_blocks_enqueued.insert(std::make_pair(pos, BlockEmergeData()));

	BlockEmergeData &bedata = findres.first->second;
	*entry_already_exists   = !findres.second;

	if (callback)
		bedata.callbacks.push_back(std::make_pair(callback, callback_param));

	if (*entry_already_exists) {
		bedata.flags |= flags;
	} else {
		bedata.flags = flags;
		bedata.peer_requested = peer_requested;

		count_peer++;
	}

	return true;
}

bool EmergeManager::popBlockEmergeData(v3s16 pos, BlockEmergeData *bedata)
{
	std::map<v3s16, BlockEmergeData>::iterator it;
	UNORDERED_MAP<u16, u16>::iterator it2;

	it = m_blocks_enqueued.find(pos);
	if (it == m_blocks_enqueued.end())
		return false;

	*bedata = it->second;

	it2 = m_peer_queue_count.find(bedata->peer_requested);
	if (it2 == m_peer_queue_count.end())
		return false;

	u16 &count_peer = it2->second;
	assert(count_peer != 0);
	count_peer--;

	m_blocks_enqueued.erase(it);

	return true;
}

EmergeThread *EmergeManager::getOptimalThread()
{
	size_t nthreads = m_threads.size();

	FATAL_ERROR_IF(nthreads == 0, "No emerge threads!");

	size_t index = 0;
	size_t nitems_lowest = m_threads[0]->m_block_queue.size();

	for (size_t i = 1; i < nthreads; i++) {
		size_t nitems = m_threads[i]->m_block_queue.size();
		if (nitems < nitems_lowest) {
			index = i;
			nitems_lowest = nitems;
		}
	}

	return m_threads[index];
}

////
//// EmergeThread
////

EmergeThread::EmergeThread(Server *server, int ethreadid) :
	enable_mapgen_debug_info(false),
	id(ethreadid),
	m_server(server),
	m_map(NULL),
	m_emerge(NULL),
	m_mapgen(NULL)
{
	m_name = "Emerge-" + itos(ethreadid);
}

EmergeThread::~EmergeThread()
{
	//cancelPendingItems();
}

void EmergeThread::signal()
{
	m_queue_event.signal();
}

bool EmergeThread::pushBlock(v3s16 pos)
{
	m_block_queue.push(pos);
	return true;
}

void EmergeThread::cancelPendingItems()
{
	MutexAutoLock queuelock(m_emerge->m_queue_mutex);

	while (!m_block_queue.empty()) {
		BlockEmergeData bedata;
		v3s16 pos;

		pos = m_block_queue.front();
		m_block_queue.pop();

		m_emerge->popBlockEmergeData(pos, &bedata);

		runCompletionCallbacks(pos, EMERGE_CANCELLED, bedata.callbacks);
	}
}

void EmergeThread::runCompletionCallbacks(
	v3s16 pos,
	EmergeAction action,
	const EmergeCallbackList &callbacks)
{
	for (size_t i = 0; i != callbacks.size(); i++) {
		EmergeCompletionCallback callback;
		void *param;

		callback = callbacks[i].first;
		param    = callbacks[i].second;

		callback(pos, action, param);
	}
}

bool EmergeThread::popBlockEmerge(v3s16 *pos, BlockEmergeData *bedata)
{
	MutexAutoLock queuelock(m_emerge->m_queue_mutex);

	if (m_block_queue.empty())
		return false;

	*pos = m_block_queue.front();
	m_block_queue.pop();

	m_emerge->popBlockEmergeData(*pos, bedata);

	return true;
}

EmergeAction EmergeThread::getBlockOrStartGen(
	v3s16 pos, bool allow_gen, MapBlock **block, BlockMakeData *bmdata)
{
	MutexAutoLock envlock(m_server->m_env_mutex);

	// 1). Attempt to fetch block from memory
	*block = m_map->getBlockNoCreateNoEx(pos);
	if (*block && !(*block)->isDummy()) {
		if ((*block)->isGenerated())
			return EMERGE_FROM_MEMORY;
	} else {
		// 2). Attempt to load block from disk if it was not in the memory
		*block = m_map->loadBlock(pos);
		if (*block && (*block)->isGenerated())
			return EMERGE_FROM_DISK;
	}

	// 3). Attempt to start generation
	if (allow_gen && m_map->initBlockMake(pos, bmdata))
		return EMERGE_GENERATED;

	// All attempts failed; cancel this block emerge
	return EMERGE_CANCELLED;
}

MapBlock *EmergeThread::finishGen(v3s16 pos, BlockMakeData *bmdata,
	std::map<v3s16, MapBlock *> *modified_blocks)
{
	MutexAutoLock envlock(m_server->m_env_mutex);
	ScopeProfiler sp(g_profiler,
		"EmergeThread: after Mapgen::makeChunk", SPT_AVG);

	/*
		Perform post-processing on blocks (invalidate lighting, queue liquid
		transforms, etc.) to finish block make
	*/
	m_map->finishBlockMake(bmdata, modified_blocks);

	MapBlock *block = m_map->getBlockNoCreateNoEx(pos);
	if (!block) {
		errorstream << "EmergeThread::finishGen: Couldn't grab block we "
			"just generated: " << PP(pos) << std::endl;
		return NULL;
	}

	v3s16 minp = bmdata->blockpos_min * MAP_BLOCKSIZE;
	v3s16 maxp = bmdata->blockpos_max * MAP_BLOCKSIZE +
				 v3s16(1,1,1) * (MAP_BLOCKSIZE - 1);

	// Ignore map edit events, they will not need to be sent
	// to anybody because the block hasn't been sent to anybody
	MapEditEventAreaIgnorer ign(
		&m_server->m_ignore_map_edit_events_area,
		VoxelArea(minp, maxp));

	/*
		Run Lua on_generated callbacks
	*/
	try {
		m_server->getScriptIface()->environment_OnGenerated(
			minp, maxp, m_mapgen->blockseed);
	} catch (LuaError &e) {
		m_server->setAsyncFatalError("Lua: finishGen" + std::string(e.what()));
	}

	EMERGE_DBG_OUT("ended up with: " << analyze_block(block));

	/*
		Activate the block
	*/
	m_server->m_env->activateBlock(block, 0);

	return block;
}

void *EmergeThread::run()
{
	DSTACK(FUNCTION_NAME);
	BEGIN_DEBUG_EXCEPTION_HANDLER

	v3s16 pos;

	m_map    = (ServerMap *)&(m_server->m_env->getMap());
	m_emerge = m_server->m_emerge;
	m_mapgen = m_emerge->m_mapgens[id];
	enable_mapgen_debug_info = m_emerge->enable_mapgen_debug_info;

	try {
	while (!stopRequested()) {
		std::map<v3s16, MapBlock *> modified_blocks;
		BlockEmergeData bedata;
		BlockMakeData bmdata;
		EmergeAction action;
		MapBlock *block;

		if (!popBlockEmerge(&pos, &bedata)) {
			m_queue_event.wait();
			continue;
		}

		if (blockpos_over_max_limit(pos))
			continue;

		bool allow_gen = bedata.flags & BLOCK_EMERGE_ALLOW_GEN;
		EMERGE_DBG_OUT("pos=" PP(pos) " allow_gen=" << allow_gen);

		action = getBlockOrStartGen(pos, allow_gen, &block, &bmdata);
		if (action == EMERGE_GENERATED) {
			{
				ScopeProfiler sp(g_profiler,
					"EmergeThread: Mapgen::makeChunk", SPT_AVG);
				TimeTaker t("mapgen::make_block()");

				m_mapgen->makeChunk(&bmdata);

				if (enable_mapgen_debug_info == false)
					t.stop(true); // Hide output
			}

			block = finishGen(pos, &bmdata, &modified_blocks);
		}

		runCompletionCallbacks(pos, action, bedata.callbacks);

		if (block)
			modified_blocks[pos] = block;

		if (modified_blocks.size() > 0)
			m_server->SetBlocksNotSent(modified_blocks);
	}
	} catch (VersionMismatchException &e) {
		std::ostringstream err;
		err << "World data version mismatch in MapBlock " << PP(pos) << std::endl
			<< "----" << std::endl
			<< "\"" << e.what() << "\"" << std::endl
			<< "See debug.txt." << std::endl
			<< "World probably saved by a newer version of " PROJECT_NAME_C "."
			<< std::endl;
		m_server->setAsyncFatalError(err.str());
	} catch (SerializationError &e) {
		std::ostringstream err;
		err << "Invalid data in MapBlock " << PP(pos) << std::endl
			<< "----" << std::endl
			<< "\"" << e.what() << "\"" << std::endl
			<< "See debug.txt." << std::endl
			<< "You can ignore this using [ignore_world_load_errors = true]."
			<< std::endl;
		m_server->setAsyncFatalError(err.str());
	}

	END_DEBUG_EXCEPTION_HANDLER
	return NULL;
}


code:
/*
Minetest
Copyright (C) 2010-2013 kwolekr, Ryan Kwolek <kwolekr@minetest.net>

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU Lesser General Public License for more details.

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

#ifndef EMERGE_HEADER
#define EMERGE_HEADER

#include <map>
#include "irr_v3d.h"
#include "util/container.h"
#include "mapgen.h" // for MapgenParams
#include "map.h"

#define BLOCK_EMERGE_ALLOW_GEN   (1 << 0)
#define BLOCK_EMERGE_FORCE_QUEUE (1 << 1)

#define EMERGE_DBG_OUT(x) do {                         \
	if (enable_mapgen_debug_info)                      \
		infostream << "EmergeThread: " x << std::endl; \
} while (0)

class EmergeThread;
class INodeDefManager;
class Settings;

class BiomeManager;
class OreManager;
class DecorationManager;
class SchematicManager;
class Server;

// Structure containing inputs/outputs for chunk generation
struct BlockMakeData {
	MMVManip *vmanip;
	u64 seed;
	v3s16 blockpos_min;
	v3s16 blockpos_max;
	v3s16 blockpos_requested;
	UniqueQueue<v3s16> transforming_liquid;
	INodeDefManager *nodedef;

	BlockMakeData():
		vmanip(NULL),
		seed(0),
		nodedef(NULL)
	{}

	~BlockMakeData() { delete vmanip; }
};

// Result from processing an item on the emerge queue
enum EmergeAction {
	EMERGE_CANCELLED,
	EMERGE_ERRORED,
	EMERGE_FROM_MEMORY,
	EMERGE_FROM_DISK,
	EMERGE_GENERATED,
};

// Callback
typedef void (*EmergeCompletionCallback)(
	v3s16 blockpos, EmergeAction action, void *param);

typedef std::vector<
	std::pair<
		EmergeCompletionCallback,
		void *
	>
> EmergeCallbackList;

struct BlockEmergeData {
	u16 peer_requested;
	u16 flags;
	EmergeCallbackList callbacks;
};

class EmergeManager {
public:
	INodeDefManager *ndef;
	bool enable_mapgen_debug_info;

	// Generation Notify
	u32 gen_notify_on;
	std::set<u32> gen_notify_on_deco_ids;

	// Parameters passed to mapgens owned by ServerMap
	// TODO(hmmmm): Remove this after mapgen helper methods using them
	// are moved to ServerMap
	MapgenParams *mgparams;

	// Hackish workaround:
	// For now, EmergeManager must hold onto a ptr to the Map's setting manager
	// since the Map can only be accessed through the Environment, and the
	// Environment is not created until after script initialization.
	MapSettingsManager *map_settings_mgr;

	// Managers of various map generation-related components
	BiomeManager *biomemgr;
	OreManager *oremgr;
	DecorationManager *decomgr;
	SchematicManager *schemmgr;

	// Methods
	EmergeManager(Server *server);
	~EmergeManager();

	bool initMapgens(MapgenParams *mgparams);

	void startThreads();
	void stopThreads();
	bool isRunning();

	bool enqueueBlockEmerge(
		u16 peer_id,
		v3s16 blockpos,
		bool allow_generate,
		bool ignore_queue_limits=false);

	bool enqueueBlockEmergeEx(
		v3s16 blockpos,
		u16 peer_id,
		u16 flags,
		EmergeCompletionCallback callback,
		void *callback_param);

	v3s16 getContainingChunk(v3s16 blockpos);

	Mapgen *getCurrentMapgen();

	// Mapgen helpers methods
	Biome *getBiomeAtPoint(v3s16 p);
	int getSpawnLevelAtPoint(v2s16 p);
	int getGroundLevelAtPoint(v2s16 p);
	bool isBlockUnderground(v3s16 blockpos);

	static v3s16 getContainingChunk(v3s16 blockpos, s16 chunksize);

private:
	std::vector<Mapgen *> m_mapgens;
	std::vector<EmergeThread *> m_threads;
	bool m_threads_active;

	Mutex m_queue_mutex;
	std::map<v3s16, BlockEmergeData> m_blocks_enqueued;
	UNORDERED_MAP<u16, u16> m_peer_queue_count;

	u16 m_qlimit_total;
	u16 m_qlimit_diskonly;
	u16 m_qlimit_generate;

	// Requires m_queue_mutex held
	EmergeThread *getOptimalThread();

	bool pushBlockEmergeData(
		v3s16 pos,
		u16 peer_requested,
		u16 flags,
		EmergeCompletionCallback callback,
		void *callback_param,
		bool *entry_already_exists);

	bool popBlockEmergeData(v3s16 pos, BlockEmergeData *bedata);

	friend class EmergeThread;

	DISABLE_CLASS_COPY(EmergeManager);
};

#endif


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

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU Lesser General Public License for more details.

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

#include <fstream>
#include "environment.h"
#include "collision.h"
#include "serverobject.h"
#include "scripting_server.h"
#include "server.h"
#include "daynightratio.h"
#include "emerge.h"

Environment::Environment(IGameDef *gamedef):
	m_time_of_day_speed(0),
	m_time_of_day(9000),
	m_time_of_day_f(9000./24000),
	m_time_conversion_skew(0.0f),
	m_enable_day_night_ratio_override(false),
	m_day_night_ratio_override(0.0f),
	m_gamedef(gamedef)
{
	m_cache_enable_shaders = g_settings->getBool("enable_shaders");
	m_cache_active_block_mgmt_interval = g_settings->getFloat("active_block_mgmt_interval");
	m_cache_abm_interval = g_settings->getFloat("abm_interval");
	m_cache_nodetimer_interval = g_settings->getFloat("nodetimer_interval");
}

Environment::~Environment()
{
}

u32 Environment::getDayNightRatio()
{
	MutexAutoLock lock(this->m_time_lock);
	if (m_enable_day_night_ratio_override)
		return m_day_night_ratio_override;
	return time_to_daynight_ratio(m_time_of_day_f * 24000, m_cache_enable_shaders);
}

void Environment::setTimeOfDaySpeed(float speed)
{
	m_time_of_day_speed = speed;
}

void Environment::setDayNightRatioOverride(bool enable, u32 value)
{
	MutexAutoLock lock(this->m_time_lock);
	m_enable_day_night_ratio_override = enable;
	m_day_night_ratio_override = value;
}

void Environment::setTimeOfDay(u32 time)
{
	MutexAutoLock lock(this->m_time_lock);
	if (m_time_of_day > time)
		++m_day_count;
	m_time_of_day = time;
	m_time_of_day_f = (float)time / 24000.0;
}

u32 Environment::getTimeOfDay()
{
	MutexAutoLock lock(this->m_time_lock);
	return m_time_of_day;
}

float Environment::getTimeOfDayF()
{
	MutexAutoLock lock(this->m_time_lock);
	return m_time_of_day_f;
}

void Environment::stepTimeOfDay(float dtime)
{
	MutexAutoLock lock(this->m_time_lock);

	// Cached in order to prevent the two reads we do to give
	// different results (can be written by code not under the lock)
	f32 cached_time_of_day_speed = m_time_of_day_speed;

	f32 speed = cached_time_of_day_speed * 24000. / (24. * 3600);
	m_time_conversion_skew += dtime;
	u32 units = (u32)(m_time_conversion_skew * speed);
	bool sync_f = false;
	if (units > 0) {
		// Sync at overflow
		if (m_time_of_day + units >= 24000) {
			sync_f = true;
			++m_day_count;
		}
		m_time_of_day = (m_time_of_day + units) % 24000;
		if (sync_f)
			m_time_of_day_f = (float)m_time_of_day / 24000.0;
	}
	if (speed > 0) {
		m_time_conversion_skew -= (f32)units / speed;
	}
	if (!sync_f) {
		m_time_of_day_f += cached_time_of_day_speed / 24 / 3600 * dtime;
		if (m_time_of_day_f > 1.0)
			m_time_of_day_f -= 1.0;
		if (m_time_of_day_f < 0.0)
			m_time_of_day_f += 1.0;
	}
}

u32 Environment::getDayCount()
{
	// Atomic<u32> counter
	return m_day_count;
}


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

/*
	This class is the game's environment.
	It contains:
	- The map
	- Players
	- Other objects
	- The current time in the game
	- etc.
*/

#include <list>
#include <queue>
#include <map>
#include "irr_v3d.h"
#include "activeobject.h"
#include "util/numeric.h"
#include "threading/mutex.h"
#include "threading/atomic.h"
#include "network/networkprotocol.h" // for AccessDeniedCode

class IGameDef;
class Map;

class Environment
{
public:
	// Environment will delete the map passed to the constructor
	Environment(IGameDef *gamedef);
	virtual ~Environment();

	/*
		Step everything in environment.
		- Move players
		- Step mobs
		- Run timers of map
	*/
	virtual void step(f32 dtime) = 0;

	virtual Map &getMap() = 0;

	u32 getDayNightRatio();

	// 0-23999
	virtual void setTimeOfDay(u32 time);
	u32 getTimeOfDay();
	float getTimeOfDayF();

	void stepTimeOfDay(float dtime);

	void setTimeOfDaySpeed(float speed);

	void setDayNightRatioOverride(bool enable, u32 value);

	u32 getDayCount();

	// counter used internally when triggering ABMs
	u32 m_added_objects;

	IGameDef *getGameDef() { return m_gamedef; }
protected:
	GenericAtomic<float> m_time_of_day_speed;

	/*
	 * Below: values managed by m_time_lock
	*/
	// Time of day in milli-hours (0-23999); determines day and night
	u32 m_time_of_day;
	// Time of day in 0...1
	float m_time_of_day_f;
	// Stores the skew created by the float -> u32 conversion
	// to be applied at next conversion, so that there is no real skew.
	float m_time_conversion_skew;
	// Overriding the day-night ratio is useful for custom sky visuals
	bool m_enable_day_night_ratio_override;
	u32 m_day_night_ratio_override;
	// Days from the server start, accounts for time shift
	// in game (e.g. /time or bed usage)
	Atomic<u32> m_day_count;
	/*
	 * Above: values managed by m_time_lock
	*/

	/* TODO: Add a callback function so these can be updated when a setting
	 *       changes.  At this point in time it doesn't matter (e.g. /set
	 *       is documented to change server settings only)
	 *
	 * TODO: Local caching of settings is not optimal and should at some stage
	 *       be updated to use a global settings object for getting thse values
	 *       (as opposed to the this local caching). This can be addressed in
	 *       a later release.
	 */
	bool m_cache_enable_shaders;
	float m_cache_active_block_mgmt_interval;
	float m_cache_abm_interval;
	float m_cache_nodetimer_interval;

	IGameDef *m_gamedef;

private:
	Mutex m_time_lock;

	DISABLE_CLASS_COPY(Environment);
};

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

#ifndef EVENT_HEADER
#define EVENT_HEADER

class MtEvent
{
public:
	virtual ~MtEvent(){};
	//virtual MtEvent* clone(){ return new IEvent; }
	virtual const char* getType() const = 0;

	MtEvent* checkIs(const std::string &type)
	{
		if(type == getType())
			return this;
		return NULL;
	}
};

// An event with no parameters and customizable name
class SimpleTriggerEvent: public MtEvent
{
	const char *type;
public:
	SimpleTriggerEvent(const char *type):
		type(type)
	{}
	const char* getType() const
	{return type;}
};

class MtEventReceiver
{
public:
	virtual ~MtEventReceiver(){};
	virtual void onEvent(MtEvent *e) = 0;
};

typedef void (*event_receive_func)(MtEvent *e, void *data);

class MtEventManager
{
public:
	virtual ~MtEventManager(){};
	virtual void put(MtEvent *e) = 0;
	virtual void reg(const char *type, event_receive_func f, void *data) = 0;
	// If data==NULL, every occurence of f is deregistered.
	virtual void dereg(const char *type, event_receive_func f, void *data) = 0;
	virtual void reg(MtEventReceiver *r, const char *type) = 0;
	virtual void dereg(MtEventReceiver *r, const char *type) = 0;
};

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

#ifndef EVENT_MANAGER_HEADER
#define EVENT_MANAGER_HEADER

#include "event.h"
#include <list>
#include <map>

class EventManager: public MtEventManager
{
	static void receiverReceive(MtEvent *e, void *data)
	{
		MtEventReceiver *r = (MtEventReceiver*)data;
		r->onEvent(e);
	}
	struct FuncSpec{
		event_receive_func f;
		void *d;
		FuncSpec(event_receive_func f, void *d):
			f(f), d(d)
		{}
	};
	struct Dest{
		std::list<FuncSpec> funcs;
	};
	std::map<std::string, Dest> m_dest;

public:
	~EventManager()
	{
	}
	void put(MtEvent *e)
	{
		std::map<std::string, Dest>::iterator i = m_dest.find(e->getType());
		if(i != m_dest.end()){
			std::list<FuncSpec> &funcs = i->second.funcs;
			for(std::list<FuncSpec>::iterator i = funcs.begin();
					i != funcs.end(); ++i){
				(*(i->f))(e, i->d);
			}
		}
		delete e;
	}
	void reg(const char *type, event_receive_func f, void *data)
	{
		std::map<std::string, Dest>::iterator i = m_dest.find(type);
		if(i != m_dest.end()){
			i->second.funcs.push_back(FuncSpec(f, data));
		} else{
			std::list<FuncSpec> funcs;
			Dest dest;
			dest.funcs.push_back(FuncSpec(f, data));
			m_dest[type] = dest;
		}
	}
	void dereg(const char *type, event_receive_func f, void *data)
	{
		if(type != NULL){
			std::map<std::string, Dest>::iterator i = m_dest.find(type);
			if(i != m_dest.end()){
				std::list<FuncSpec> &funcs = i->second.funcs;
				std::list<FuncSpec>::iterator j = funcs.begin();
				while(j != funcs.end()){
					bool remove = (j->f == f && (!data || j->d == data));
					if(remove)
						funcs.erase(j++);
					else
						++j;
				}
			}
		} else{
			for(std::map<std::string, Dest>::iterator
					i = m_dest.begin(); i != m_dest.end(); ++i){
				std::list<FuncSpec> &funcs = i->second.funcs;
				std::list<FuncSpec>::iterator j = funcs.begin();
				while(j != funcs.end()){
					bool remove = (j->f == f && (!data || j->d == data));
					if(remove)
						funcs.erase(j++);
					else
						++j;
				}
			}
		}
	}
	void reg(MtEventReceiver *r, const char *type)
	{
		reg(type, EventManager::receiverReceive, r);
	}
	void dereg(MtEventReceiver *r, const char *type)
	{
		dereg(type, EventManager::receiverReceive, r);
	}
};

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

#ifndef EXCEPTIONS_HEADER
#define EXCEPTIONS_HEADER

#include <exception>
#include <string>

class BaseException : public std::exception
{
public:
	BaseException(const std::string &s) throw(): m_s(s) {}
	~BaseException() throw() {}
	virtual const char * what() const throw()
	{
		return m_s.c_str();
	}
protected:
	std::string m_s;
};

class AsyncQueuedException : public BaseException {
public:
	AsyncQueuedException(const std::string &s): BaseException(s) {}
};

class NotImplementedException : public BaseException {
public:
	NotImplementedException(const std::string &s): BaseException(s) {}
};

class AlreadyExistsException : public BaseException {
public:
	AlreadyExistsException(const std::string &s): BaseException(s) {}
};

class VersionMismatchException : public BaseException {
public:
	VersionMismatchException(const std::string &s): BaseException(s) {}
};

class FileNotGoodException : public BaseException {
public:
	FileNotGoodException(const std::string &s): BaseException(s) {}
};

class DatabaseException : public BaseException {
public:
	DatabaseException(const std::string &s): BaseException(s) {}
};

class SerializationError : public BaseException {
public:
	SerializationError(const std::string &s): BaseException(s) {}
};

class PacketError : public BaseException {
public:
	PacketError(const std::string &s): BaseException(s) {}
};

class LoadError : public BaseException {
public:
	LoadError(const std::string &s): BaseException(s) {}
};

class ContainerFullException : public BaseException {
public:
	ContainerFullException(const std::string &s): BaseException(s) {}
};

class SettingNotFoundException : public BaseException {
public:
	SettingNotFoundException(const std::string &s): BaseException(s) {}
};

class InvalidFilenameException : public BaseException {
public:
	InvalidFilenameException(const std::string &s): BaseException(s) {}
};

class ProcessingLimitException : public BaseException {
public:
	ProcessingLimitException(const std::string &s): BaseException(s) {}
};

class CommandLineError : public BaseException {
public:
	CommandLineError(const std::string &s): BaseException(s) {}
};

class ItemNotFoundException : public BaseException {
public:
	ItemNotFoundException(const std::string &s): BaseException(s) {}
};

class ServerError : public BaseException {
public:
	ServerError(const std::string &s): BaseException(s) {}
};

class ClientStateError : public BaseException {
public:
	ClientStateError(const std::string &s): BaseException(s) {}
};

class PrngException : public BaseException {
public:
	PrngException(const std::string &s): BaseException(s) {}
};

class ModError : public BaseException {
public:
	ModError(const std::string &s): BaseException(s) {}
};

/*
	Some "old-style" interrupts:
*/

class InvalidNoiseParamsException : public BaseException {
public:
	InvalidNoiseParamsException():
		BaseException("One or more noise parameters were invalid or require "
			"too much memory")
	{}

	InvalidNoiseParamsException(const std::string &s):
		BaseException(s)
	{}
};

class InvalidPositionException : public BaseException
{
public:
	InvalidPositionException():
		BaseException("Somebody tried to get/set something in a nonexistent position.")
	{}
	InvalidPositionException(const std::string &s):
		BaseException(s)
	{}
};

class TargetInexistentException : public std::exception
{
	virtual const char * what() const throw()
	{
		return "Somebody tried to refer to something that doesn't exist.";
	}
};

class NullPointerException : public std::exception
{
	virtual const char * what() const throw()
	{
		return "NullPointerException";
	}
};

#endif


code:
/*
Minetest
Copyright (C) 2015 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 "face_position_cache.h"
#include "threading/mutex_auto_lock.h"

UNORDERED_MAP<u16, std::vector<v3s16> > FacePositionCache::cache;
Mutex FacePositionCache::cache_mutex;

// Calculate the borders of a "d-radius" cube
const std::vector<v3s16> &FacePositionCache::getFacePositions(u16 d)
{
	MutexAutoLock lock(cache_mutex);
	UNORDERED_MAP<u16, std::vector<v3s16> >::iterator it = cache.find(d);
	if (it != cache.end())
		return it->second;

	return generateFacePosition(d);
}

const std::vector<v3s16> &FacePositionCache::generateFacePosition(u16 d)
{
	cache[d] = std::vector<v3s16>();
	std::vector<v3s16> &c = cache[d];
	if (d == 0) {
		c.push_back(v3s16(0,0,0));
		return c;
	}
	if (d == 1) {
		// This is an optimized sequence of coordinates.
		c.push_back(v3s16( 0, 1, 0)); // Top
		c.push_back(v3s16( 0, 0, 1)); // Back
		c.push_back(v3s16(-1, 0, 0)); // Left
		c.push_back(v3s16( 1, 0, 0)); // Right
		c.push_back(v3s16( 0, 0,-1)); // Front
		c.push_back(v3s16( 0,-1, 0)); // Bottom
		// 6
		c.push_back(v3s16(-1, 0, 1)); // Back left
		c.push_back(v3s16( 1, 0, 1)); // Back right
		c.push_back(v3s16(-1, 0,-1)); // Front left
		c.push_back(v3s16( 1, 0,-1)); // Front right
		c.push_back(v3s16(-1,-1, 0)); // Bottom left
		c.push_back(v3s16( 1,-1, 0)); // Bottom right
		c.push_back(v3s16( 0,-1, 1)); // Bottom back
		c.push_back(v3s16( 0,-1,-1)); // Bottom front
		c.push_back(v3s16(-1, 1, 0)); // Top left
		c.push_back(v3s16( 1, 1, 0)); // Top right
		c.push_back(v3s16( 0, 1, 1)); // Top back
		c.push_back(v3s16( 0, 1,-1)); // Top front
		// 18
		c.push_back(v3s16(-1, 1, 1)); // Top back-left
		c.push_back(v3s16( 1, 1, 1)); // Top back-right
		c.push_back(v3s16(-1, 1,-1)); // Top front-left
		c.push_back(v3s16( 1, 1,-1)); // Top front-right
		c.push_back(v3s16(-1,-1, 1)); // Bottom back-left
		c.push_back(v3s16( 1,-1, 1)); // Bottom back-right
		c.push_back(v3s16(-1,-1,-1)); // Bottom front-left
		c.push_back(v3s16( 1,-1,-1)); // Bottom front-right
		// 26
		return c;
	}

	// Take blocks in all sides, starting from y=0 and going +-y
	for (s16 y = 0; y <= d - 1; y++) {
		// Left and right side, including borders
		for (s16 z =- d; z <= d; z++) {
			c.push_back(v3s16( d, y, z));
			c.push_back(v3s16(-d, y, z));
			if (y != 0) {
				c.push_back(v3s16( d, -y, z));
				c.push_back(v3s16(-d, -y, z));
			}
		}
		// Back and front side, excluding borders
		for (s16 x = -d + 1; x <= d - 1; x++) {
			c.push_back(v3s16(x, y, d));
			c.push_back(v3s16(x, y, -d));
			if (y != 0) {
				c.push_back(v3s16(x, -y, d));
				c.push_back(v3s16(x, -y, -d));
			}
		}
	}

	// Take the bottom and top face with borders
	// -d < x < d, y = +-d, -d < z < d
	for (s16 x = -d; x <= d; x++)
	for (s16 z = -d; z <= d; z++) {
		c.push_back(v3s16(x, -d, z));
		c.push_back(v3s16(x, d, z));
	}
	return c;
}


code:
/*
Minetest
Copyright (C) 2015 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 FACE_POSITION_CACHE_HEADER
#define FACE_POSITION_CACHE_HEADER

#include "irr_v3d.h"
#include "threading/mutex.h"
#include "util/cpp11_container.h"

#include <map>
#include <vector>

/*
 * This class permits caching getFacePosition call results.
 * This reduces CPU usage and vector calls.
 */
class FacePositionCache {
public:
	static const std::vector<v3s16> &getFacePositions(u16 d);

private:
	static const std::vector<v3s16> &generateFacePosition(u16 d);
	static UNORDERED_MAP<u16, std::vector<v3s16> > cache;
	static Mutex cache_mutex;
};

#endif


code:
/*
Minetest
Copyright (C) 2013 celeron55, Perttu Ahola <celeron55@gmail.com>
Copyright (C) 2013 Jonathan Neuschäfer <j.neuschaefer@gmx.net>

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU Lesser General Public License for more details.

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

#include "filecache.h"

#include "network/networkprotocol.h"
#include "log.h"
#include "filesys.h"
#include <string>
#include <iostream>
#include <fstream>
#include <stdlib.h>

bool FileCache::loadByPath(const std::string &path, std::ostream &os)
{
	std::ifstream fis(path.c_str(), std::ios_base::binary);

	if(!fis.good()){
		verbosestream<<"FileCache: File not found in cache: "
				<<path<<std::endl;
		return false;
	}

	bool bad = false;
	for(;;){
		char buf[1024];
		fis.read(buf, 1024);
		std::streamsize len = fis.gcount();
		os.write(buf, len);
		if(fis.eof())
			break;
		if(!fis.good()){
			bad = true;
			break;
		}
	}
	if(bad){
		errorstream<<"FileCache: Failed to read file from cache: \""
				<<path<<"\""<<std::endl;
	}

	return !bad;
}

bool FileCache::updateByPath(const std::string &path, const std::string &data)
{
	std::ofstream file(path.c_str(), std::ios_base::binary |
			std::ios_base::trunc);

	if(!file.good())
	{
		errorstream<<"FileCache: Can't write to file at "
				<<path<<std::endl;
		return false;
	}

	file.write(data.c_str(), data.length());
	file.close();

	return !file.fail();
}

bool FileCache::update(const std::string &name, const std::string &data)
{
	std::string path = m_dir + DIR_DELIM + name;
	return updateByPath(path, data);
}
bool FileCache::load(const std::string &name, std::ostream &os)
{
	std::string path = m_dir + DIR_DELIM + name;
	return loadByPath(path, os);
}


code:
/*
Minetest
Copyright (C) 2013 celeron55, Perttu Ahola <celeron55@gmail.com>
Copyright (C) 2013 Jonathan Neuschäfer <j.neuschaefer@gmx.net>

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU Lesser General Public License for more details.

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

#ifndef FILECACHE_HEADER
#define FILECACHE_HEADER

#include <iostream>
#include <string>

class FileCache
{
public:
	/*
		'dir' is the file cache directory to use.
	*/
	FileCache(const std::string &dir) : m_dir(dir) {}

	bool update(const std::string &name, const std::string &data);
	bool load(const std::string &name, std::ostream &os);

private:
	std::string m_dir;

	bool loadByPath(const std::string &path, std::ostream &os);
	bool updateByPath(const std::string &path, const std::string &data);
};

#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 "filesys.h"
#include "util/string.h"
#include <iostream>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <fstream>
#include "log.h"
#include "config.h"
#include "porting.h"

namespace fs
{

#ifdef _WIN32 // WINDOWS

#define _WIN32_WINNT 0x0501
#include <windows.h>
#include <shlwapi.h>

std::vector<DirListNode> GetDirListing(const std::string &pathstring)
{
	std::vector<DirListNode> listing;

	WIN32_FIND_DATA FindFileData;
	HANDLE hFind = INVALID_HANDLE_VALUE;
	DWORD dwError;

	std::string dirSpec = pathstring + "\\*";

	// Find the first file in the directory.
	hFind = FindFirstFile(dirSpec.c_str(), &FindFileData);

	if (hFind == INVALID_HANDLE_VALUE) {
		dwError = GetLastError();
		if (dwError != ERROR_FILE_NOT_FOUND && dwError != ERROR_PATH_NOT_FOUND) {
			errorstream << "GetDirListing: FindFirstFile error."
					<< " Error is " << dwError << std::endl;
		}
	} else {
		// NOTE:
		// Be very sure to not include '..' in the results, it will
		// result in an epic failure when deleting stuff.

		DirListNode node;
		node.name = FindFileData.cFileName;
		node.dir = FindFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY;
		if (node.name != "." && node.name != "..")
			listing.push_back(node);

		// List all the other files in the directory.
		while (FindNextFile(hFind, &FindFileData) != 0) {
			DirListNode node;
			node.name = FindFileData.cFileName;
			node.dir = FindFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY;
			if(node.name != "." && node.name != "..")
				listing.push_back(node);
		}

		dwError = GetLastError();
		FindClose(hFind);
		if (dwError != ERROR_NO_MORE_FILES) {
			errorstream << "GetDirListing: FindNextFile error."
					<< " Error is " << dwError << std::endl;
			listing.clear();
			return listing;
 		}
	}
	return listing;
}

bool CreateDir(const std::string &path)
{
	bool r = CreateDirectory(path.c_str(), NULL);
	if(r == true)
		return true;
	if(GetLastError() == ERROR_ALREADY_EXISTS)
		return true;
	return false;
}

bool PathExists(const std::string &path)
{
	return (GetFileAttributes(path.c_str()) != INVALID_FILE_ATTRIBUTES);
}

bool IsPathAbsolute(const std::string &path)
{
	return !PathIsRelative(path.c_str());
}

bool IsDir(const std::string &path)
{
	DWORD attr = GetFileAttributes(path.c_str());
	return (attr != INVALID_FILE_ATTRIBUTES &&
			(attr & FILE_ATTRIBUTE_DIRECTORY));
}

bool IsDirDelimiter(char c)
{
	return c == '/' || c == '\\';
}

bool RecursiveDelete(const std::string &path)
{
	infostream<<"Recursively deleting \""<<path<<"\""<<std::endl;

	DWORD attr = GetFileAttributes(path.c_str());
	bool is_directory = (attr != INVALID_FILE_ATTRIBUTES &&
			(attr & FILE_ATTRIBUTE_DIRECTORY));
	if(!is_directory)
	{
		infostream<<"RecursiveDelete: Deleting file "<<path<<std::endl;
		//bool did = DeleteFile(path.c_str());
		bool did = true;
		if(!did){
			errorstream<<"RecursiveDelete: Failed to delete file "
					<<path<<std::endl;
			return false;
		}
	}
	else
	{
		infostream<<"RecursiveDelete: Deleting content of directory "
				<<path<<std::endl;
		std::vector<DirListNode> content = GetDirListing(path);
		for(size_t i=0; i<content.size(); i++){
			const DirListNode &n = content[i];
			std::string fullpath = path + DIR_DELIM + n.name;
			bool did = RecursiveDelete(fullpath);
			if(!did){
				errorstream<<"RecursiveDelete: Failed to recurse to "
						<<fullpath<<std::endl;
				return false;
			}
		}
		infostream<<"RecursiveDelete: Deleting directory "<<path<<std::endl;
		//bool did = RemoveDirectory(path.c_str();
		bool did = true;
		if(!did){
			errorstream<<"Failed to recursively delete directory "
					<<path<<std::endl;
			return false;
		}
	}
	return true;
}

bool DeleteSingleFileOrEmptyDirectory(const std::string &path)
{
	DWORD attr = GetFileAttributes(path.c_str());
	bool is_directory = (attr != INVALID_FILE_ATTRIBUTES &&
			(attr & FILE_ATTRIBUTE_DIRECTORY));
	if(!is_directory)
	{
		bool did = DeleteFile(path.c_str());
		return did;
	}
	else
	{
		bool did = RemoveDirectory(path.c_str());
		return did;
	}
}

std::string TempPath()
{
	DWORD bufsize = GetTempPath(0, NULL);
	if(bufsize == 0){
		errorstream<<"GetTempPath failed, error = "<<GetLastError()<<std::endl;
		return "";
	}
	std::vector<char> buf(bufsize);
	DWORD len = GetTempPath(bufsize, &buf[0]);
	if(len == 0 || len > bufsize){
		errorstream<<"GetTempPath failed, error = "<<GetLastError()<<std::endl;
		return "";
	}
	return std::string(buf.begin(), buf.begin() + len);
}

#else // POSIX

#include <sys/types.h>
#include <dirent.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <unistd.h>

std::vector<DirListNode> GetDirListing(const std::string &pathstring)
{
	std::vector<DirListNode> listing;

	DIR *dp;
	struct dirent *dirp;
	if((dp = opendir(pathstring.c_str())) == NULL) {
		//infostream<<"Error("<<errno<<") opening "<<pathstring<<std::endl;
		return listing;
	}

	while ((dirp = readdir(dp)) != NULL) {
		// NOTE:
		// Be very sure to not include '..' in the results, it will
		// result in an epic failure when deleting stuff.
		if(strcmp(dirp->d_name, ".") == 0 || strcmp(dirp->d_name, "..") == 0)
			continue;

		DirListNode node;
		node.name = dirp->d_name;

		int isdir = -1; // -1 means unknown

		/*
			POSIX doesn't define d_type member of struct dirent and
			certain filesystems on glibc/Linux will only return
			DT_UNKNOWN for the d_type member.

			Also we don't know whether symlinks are directories or not.
		*/
#ifdef _DIRENT_HAVE_D_TYPE
		if(dirp->d_type != DT_UNKNOWN && dirp->d_type != DT_LNK)
			isdir = (dirp->d_type == DT_DIR);
#endif /* _DIRENT_HAVE_D_TYPE */

		/*
			Was d_type DT_UNKNOWN, DT_LNK or nonexistent?
			If so, try stat().
		*/
		if(isdir == -1) {
			struct stat statbuf;
			if (stat((pathstring + "/" + node.name).c_str(), &statbuf))
				continue;
			isdir = ((statbuf.st_mode & S_IFDIR) == S_IFDIR);
		}
		node.dir = isdir;
		listing.push_back(node);
	}
	closedir(dp);

	return listing;
}

bool CreateDir(const std::string &path)
{
	int r = mkdir(path.c_str(), S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH);
	if(r == 0)
	{
		return true;
	}
	else
	{
		// If already exists, return true
		if(errno == EEXIST)
			return true;
		return false;
	}
}

bool PathExists(const std::string &path)
{
	struct stat st;
	return (stat(path.c_str(),&st) == 0);
}

bool IsPathAbsolute(const std::string &path)
{
	return path[0] == '/';
}

bool IsDir(const std::string &path)
{
	struct stat statbuf;
	if(stat(path.c_str(), &statbuf))
		return false; // Actually error; but certainly not a directory
	return ((statbuf.st_mode & S_IFDIR) == S_IFDIR);
}

bool IsDirDelimiter(char c)
{
	return c == '/';
}

bool RecursiveDelete(const std::string &path)
{
	/*
		Execute the 'rm' command directly, by fork() and execve()
	*/

	infostream<<"Removing \""<<path<<"\""<<std::endl;

	//return false;

	pid_t child_pid = fork();

	if(child_pid == 0)
	{
		// Child
		char argv_data[3][10000];
		strcpy(argv_data[0], "/bin/rm");
		strcpy(argv_data[1], "-rf");
		strncpy(argv_data[2], path.c_str(), 10000);
		char *argv[4];
		argv[0] = argv_data[0];
		argv[1] = argv_data[1];
		argv[2] = argv_data[2];
		argv[3] = NULL;

		verbosestream<<"Executing '"<<argv[0]<<"' '"<<argv[1]<<"' '"
				<<argv[2]<<"'"<<std::endl;

		execv(argv[0], argv);

		// Execv shouldn't return. Failed.
		_exit(1);
	}
	else
	{
		// Parent
		int child_status;
		pid_t tpid;
		do{
			tpid = wait(&child_status);
			//if(tpid != child_pid) process_terminated(tpid);
		}while(tpid != child_pid);
		return (child_status == 0);
	}
}

bool DeleteSingleFileOrEmptyDirectory(const std::string &path)
{
	if(IsDir(path)){
		bool did = (rmdir(path.c_str()) == 0);
		if(!did)
			errorstream<<"rmdir errno: "<<errno<<": "<<strerror(errno)
					<<std::endl;
		return did;
	} else {
		bool did = (unlink(path.c_str()) == 0);
		if(!did)
			errorstream<<"unlink errno: "<<errno<<": "<<strerror(errno)
					<<std::endl;
		return did;
	}
}

std::string TempPath()
{
	/*
		Should the environment variables TMPDIR, TMP and TEMP
		and the macro P_tmpdir (if defined by stdio.h) be checked
		before falling back on /tmp?

		Probably not, because this function is intended to be
		compatible with lua's os.tmpname which under the default
		configuration hardcodes mkstemp("/tmp/lua_XXXXXX").
	*/
#ifdef __ANDROID__
	return DIR_DELIM "sdcard" DIR_DELIM PROJECT_NAME DIR_DELIM "tmp";
#else
	return DIR_DELIM "tmp";
#endif
}

#endif

void GetRecursiveSubPaths(const std::string &path, std::vector<std::string> &dst)
{
	std::vector<DirListNode> content = GetDirListing(path);
	for(unsigned int  i=0; i<content.size(); i++){
		const DirListNode &n = content[i];
		std::string fullpath = path + DIR_DELIM + n.name;
		dst.push_back(fullpath);
		if (n.dir) {
			GetRecursiveSubPaths(fullpath, dst);
		}
	}
}

bool DeletePaths(const std::vector<std::string> &paths)
{
	bool success = true;
	// Go backwards to succesfully delete the output of GetRecursiveSubPaths
	for(int i=paths.size()-1; i>=0; i--){
		const std::string &path = paths[i];
		bool did = DeleteSingleFileOrEmptyDirectory(path);
		if(!did){
			errorstream<<"Failed to delete "<<path<<std::endl;
			success = false;
		}
	}
	return success;
}

bool RecursiveDeleteContent(const std::string &path)
{
	infostream<<"Removing content of \""<<path<<"\""<<std::endl;
	std::vector<DirListNode> list = GetDirListing(path);
	for(unsigned int i=0; i<list.size(); i++)
	{
		if(trim(list[i].name) == "." || trim(list[i].name) == "..")
			continue;
		std::string childpath = path + DIR_DELIM + list[i].name;
		bool r = RecursiveDelete(childpath);
		if(r == false)
		{
			errorstream<<"Removing \""<<childpath<<"\" failed"<<std::endl;
			return false;
		}
	}
	return true;
}

bool CreateAllDirs(const std::string &path)
{

	std::vector<std::string> tocreate;
	std::string basepath = path;
	while(!PathExists(basepath))
	{
		tocreate.push_back(basepath);
		basepath = RemoveLastPathComponent(basepath);
		if(basepath.empty())
			break;
	}
	for(int i=tocreate.size()-1;i>=0;i--)
		if(!CreateDir(tocreate[i]))
			return false;
	return true;
}

bool CopyFileContents(const std::string &source, const std::string &target)
{
	FILE *sourcefile = fopen(source.c_str(), "rb");
	if(sourcefile == NULL){
		errorstream<<source<<": can't open for reading: "
			<<strerror(errno)<<std::endl;
		return false;
	}

	FILE *targetfile = fopen(target.c_str(), "wb");
	if(targetfile == NULL){
		errorstream<<target<<": can't open for writing: "
			<<strerror(errno)<<std::endl;
		fclose(sourcefile);
		return false;
	}

	size_t total = 0;
	bool retval = true;
	bool done = false;
	char readbuffer[BUFSIZ];
	while(!done){
		size_t readbytes = fread(readbuffer, 1,
				sizeof(readbuffer), sourcefile);
		total += readbytes;
		if(ferror(sourcefile)){
			errorstream<<source<<": IO error: "
				<<strerror(errno)<<std::endl;
			retval = false;
			done = true;
		}
		if(readbytes > 0){
			fwrite(readbuffer, 1, readbytes, targetfile);
		}
		if(feof(sourcefile) || ferror(sourcefile)){
			// flush destination file to catch write errors
			// (e.g. disk full)
			fflush(targetfile);
			done = true;
		}
		if(ferror(targetfile)){
			errorstream<<target<<": IO error: "
					<<strerror(errno)<<std::endl;
			retval = false;
			done = true;
		}
	}
	infostream<<"copied "<<total<<" bytes from "
		<<source<<" to "<<target<<std::endl;
	fclose(sourcefile);
	fclose(targetfile);
	return retval;
}

bool CopyDir(const std::string &source, const std::string &target)
{
	if(PathExists(source)){
		if(!PathExists(target)){
			fs::CreateAllDirs(target);
		}
		bool retval = true;
		std::vector<DirListNode> content = fs::GetDirListing(source);

		for(unsigned int i=0; i < content.size(); i++){
			std::string sourcechild = source + DIR_DELIM + content[i].name;
			std::string targetchild = target + DIR_DELIM + content[i].name;
			if(content[i].dir){
				if(!fs::CopyDir(sourcechild, targetchild)){
					retval = false;
				}
			}
			else {
				if(!fs::CopyFileContents(sourcechild, targetchild)){
					retval = false;
				}
			}
		}
		return retval;
	}
	else {
		return false;
	}
}

bool PathStartsWith(const std::string &path, const std::string &prefix)
{
	size_t pathsize = path.size();
	size_t pathpos = 0;
	size_t prefixsize = prefix.size();
	size_t prefixpos = 0;
	for(;;){
		bool delim1 = pathpos == pathsize
			|| IsDirDelimiter(path[pathpos]);
		bool delim2 = prefixpos == prefixsize
			|| IsDirDelimiter(prefix[prefixpos]);

		if(delim1 != delim2)
			return false;

		if(delim1){
			while(pathpos < pathsize &&
					IsDirDelimiter(path[pathpos]))
				++pathpos;
			while(prefixpos < prefixsize &&
					IsDirDelimiter(prefix[prefixpos]))
				++prefixpos;
			if(prefixpos == prefixsize)
				return true;
			if(pathpos == pathsize)
				return false;
		}
		else{
			size_t len = 0;
			do{
				char pathchar = path[pathpos+len];
				char prefixchar = prefix[prefixpos+len];
				if(FILESYS_CASE_INSENSITIVE){
					pathchar = tolower(pathchar);
					prefixchar = tolower(prefixchar);
				}
				if(pathchar != prefixchar)
					return false;
				++len;
			} while(pathpos+len < pathsize
					&& !IsDirDelimiter(path[pathpos+len])
					&& prefixpos+len < prefixsize
					&& !IsDirDelimiter(
						prefix[prefixpos+len]));
			pathpos += len;
			prefixpos += len;
		}
	}
}

std::string RemoveLastPathComponent(const std::string &path,
		std::string *removed, int count)
{
	if(removed)
		*removed = "";

	size_t remaining = path.size();

	for(int i = 0; i < count; ++i){
		// strip a dir delimiter
		while(remaining != 0 && IsDirDelimiter(path[remaining-1]))
			remaining--;
		// strip a path component
		size_t component_end = remaining;
		while(remaining != 0 && !IsDirDelimiter(path[remaining-1]))
			remaining--;
		size_t component_start = remaining;
		// strip a dir delimiter
		while(remaining != 0 && IsDirDelimiter(path[remaining-1]))
			remaining--;
		if(removed){
			std::string component = path.substr(component_start,
					component_end - component_start);
			if(i)
				*removed = component + DIR_DELIM + *removed;
			else
				*removed = component;
		}
	}
	return path.substr(0, remaining);
}

std::string RemoveRelativePathComponents(std::string path)
{
	size_t pos = path.size();
	size_t dotdot_count = 0;
	while (pos != 0) {
		size_t component_with_delim_end = pos;
		// skip a dir delimiter
		while (pos != 0 && IsDirDelimiter(path[pos-1]))
			pos--;
		// strip a path component
		size_t component_end = pos;
		while (pos != 0 && !IsDirDelimiter(path[pos-1]))
			pos--;
		size_t component_start = pos;

		std::string component = path.substr(component_start,
				component_end - component_start);
		bool remove_this_component = false;
		if (component == ".") {
			remove_this_component = true;
		} else if (component == "..") {
			remove_this_component = true;
			dotdot_count += 1;
		} else if (dotdot_count != 0) {
			remove_this_component = true;
			dotdot_count -= 1;
		}

		if (remove_this_component) {
			while (pos != 0 && IsDirDelimiter(path[pos-1]))
				pos--;
			if (component_start == 0) {
				// We need to remove the delemiter too
				path = path.substr(component_with_delim_end, std::string::npos);
			} else {
				path = path.substr(0, pos) + DIR_DELIM +
					path.substr(component_with_delim_end, std::string::npos);
			}
			if (pos > 0)
				pos++;
		}
	}

	if (dotdot_count > 0)
		return "";

	// remove trailing dir delimiters
	pos = path.size();
	while (pos != 0 && IsDirDelimiter(path[pos-1]))
		pos--;
	return path.substr(0, pos);
}

std::string AbsolutePath(const std::string &path)
{
#ifdef _WIN32
	char *abs_path = _fullpath(NULL, path.c_str(), MAX_PATH);
#else
	char *abs_path = realpath(path.c_str(), NULL);
#endif
	if (!abs_path) return "";
	std::string abs_path_str(abs_path);
	free(abs_path);
	return abs_path_str;
}

const char *GetFilenameFromPath(const char *path)
{
	const char *filename = strrchr(path, DIR_DELIM_CHAR);
	return filename ? filename + 1 : path;
}

bool safeWriteToFile(const std::string &path, const std::string &content)
{
	std::string tmp_file = path + ".~mt";

	// Write to a tmp file
	std::ofstream os(tmp_file.c_str(), std::ios::binary);
	if (!os.good())
		return false;
	os << content;
	os.flush();
	os.close();
	if (os.fail()) {
		// Remove the temporary file because writing it failed and it's useless.
		remove(tmp_file.c_str());
		return false;
	}

	bool rename_success = false;

	// Move the finished temporary file over the real file
#ifdef _WIN32
	// When creating the file, it can cause Windows Search indexer, virus scanners and other apps
	// to query the file. This can make the move file call below fail.
	// We retry up to 5 times, with a 1ms sleep between, before we consider the whole operation failed
	int number_attempts = 0;
	while (number_attempts < 5) {
		rename_success = MoveFileEx(tmp_file.c_str(), path.c_str(),
				MOVEFILE_REPLACE_EXISTING | MOVEFILE_WRITE_THROUGH);
		if (rename_success)
			break;
		sleep_ms(1);
		++number_attempts;
	}
#else
	// On POSIX compliant systems rename() is specified to be able to swap the
	// file in place of the destination file, making this a truly error-proof
	// transaction.
	rename_success = rename(tmp_file.c_str(), path.c_str()) == 0;
#endif
	if (!rename_success) {
		warningstream << "Failed to write to file: " << path.c_str() << std::endl;
		// Remove the temporary file because moving it over the target file
		// failed.
		remove(tmp_file.c_str());
		return false;
	}

	return true;
}

bool Rename(const std::string &from, const std::string &to)
{
	return rename(from.c_str(), to.c_str()) == 0;
}

} // namespace fs


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

#include <string>
#include <vector>
#include "exceptions.h"

#ifdef _WIN32 // WINDOWS
#define DIR_DELIM "\\"
#define DIR_DELIM_CHAR '\\'
#define FILESYS_CASE_INSENSITIVE 1
#define PATH_DELIM ";"
#else // POSIX
#define DIR_DELIM "/"
#define DIR_DELIM_CHAR '/'
#define FILESYS_CASE_INSENSITIVE 0
#define PATH_DELIM ":"
#endif

namespace fs
{

struct DirListNode
{
	std::string name;
	bool dir;
};

std::vector<DirListNode> GetDirListing(const std::string &path);

// Returns true if already exists
bool CreateDir(const std::string &path);

bool PathExists(const std::string &path);

bool IsPathAbsolute(const std::string &path);

bool IsDir(const std::string &path);

bool IsDirDelimiter(char c);

// Only pass full paths to this one. True on success.
// NOTE: The WIN32 version returns always true.
bool RecursiveDelete(const std::string &path);

bool DeleteSingleFileOrEmptyDirectory(const std::string &path);

// Returns path to temp directory, can return "" on error
std::string TempPath();

/* Multiplatform */

// The path itself not included
void GetRecursiveSubPaths(const std::string &path, std::vector<std::string> &dst);

// Tries to delete all, returns false if any failed
bool DeletePaths(const std::vector<std::string> &paths);

// Only pass full paths to this one. True on success.
bool RecursiveDeleteContent(const std::string &path);

// Create all directories on the given path that don't already exist.
bool CreateAllDirs(const std::string &path);

// Copy a regular file
bool CopyFileContents(const std::string &source, const std::string &target);

// Copy directory and all subdirectories
// Omits files and subdirectories that start with a period
bool CopyDir(const std::string &source, const std::string &target);

// Check if one path is prefix of another
// For example, "/tmp" is a prefix of "/tmp" and "/tmp/file" but not "/tmp2"
// Ignores case differences and '/' vs. '\\' on Windows
bool PathStartsWith(const std::string &path, const std::string &prefix);

// Remove last path component and the dir delimiter before and/or after it,
// returns "" if there is only one path component.
// removed: If non-NULL, receives the removed component(s).
// count: Number of components to remove
std::string RemoveLastPathComponent(const std::string &path,
               std::string *removed = NULL, int count = 1);

// Remove "." and ".." path components and for every ".." removed, remove
// the last normal path component before it. Unlike AbsolutePath,
// this does not resolve symlinks and check for existence of directories.
std::string RemoveRelativePathComponents(std::string path);

// Returns the absolute path for the passed path, with "." and ".." path
// components and symlinks removed.  Returns "" on error.
std::string AbsolutePath(const std::string &path);

// Returns the filename from a path or the entire path if no directory
// delimiter is found.
const char *GetFilenameFromPath(const char *path);

bool safeWriteToFile(const std::string &path, const std::string &content);

bool Rename(const std::string &from, const std::string &to);

} // namespace fs

#endif