code:
#include "sky.h"
#include "IVideoDriver.h"
#include "ISceneManager.h"
#include "ICameraSceneNode.h"
#include "S3DVertex.h"
#include "client/tile.h"
#include "noise.h"  // easeCurve
#include "profiler.h"
#include "util/numeric.h"
#include <cmath>
#include "settings.h"
#include "camera.h"  // CameraModes

Sky::Sky(scene::ISceneNode* parent, scene::ISceneManager* mgr, s32 id,
		ITextureSource *tsrc):
		scene::ISceneNode(parent, mgr, id),
		m_visible(true),
		m_fallback_bg_color(255, 255, 255, 255),
		m_first_update(true),
		m_brightness(0.5),
		m_cloud_brightness(0.5),
		m_bgcolor_bright_f(1, 1, 1, 1),
		m_skycolor_bright_f(1, 1, 1, 1),
		m_cloudcolor_bright_f(1, 1, 1, 1)
{
	setAutomaticCulling(scene::EAC_OFF);
	m_box.MaxEdge.set(0, 0, 0);
	m_box.MinEdge.set(0, 0, 0);

	// Create material

	video::SMaterial mat;
	mat.Lighting = false;
	mat.ZBuffer = video::ECFN_NEVER;
	mat.ZWriteEnable = false;
	mat.AntiAliasing = 0;
	mat.TextureLayer[0].TextureWrapU = video::ETC_CLAMP_TO_EDGE;
	mat.TextureLayer[0].TextureWrapV = video::ETC_CLAMP_TO_EDGE;
	mat.BackfaceCulling = false;

	m_materials[0] = mat;

	m_materials[1] = mat;
	//m_materials[1].MaterialType = video::EMT_TRANSPARENT_VERTEX_ALPHA;
	m_materials[1].MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL;

	m_materials[2] = mat;
	m_materials[2].setTexture(0, tsrc->getTextureForMesh("sunrisebg.png"));
	m_materials[2].MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL;
	//m_materials[2].MaterialType = video::EMT_TRANSPARENT_ADD_COLOR;

	m_sun_texture = tsrc->isKnownSourceImage("sun.png") ?
		tsrc->getTextureForMesh("sun.png") : NULL;
	m_moon_texture = tsrc->isKnownSourceImage("moon.png") ?
		tsrc->getTextureForMesh("moon.png") : NULL;
	m_sun_tonemap = tsrc->isKnownSourceImage("sun_tonemap.png") ?
		tsrc->getTexture("sun_tonemap.png") : NULL;
	m_moon_tonemap = tsrc->isKnownSourceImage("moon_tonemap.png") ?
		tsrc->getTexture("moon_tonemap.png") : NULL;

	if (m_sun_texture) {
		m_materials[3] = mat;
		m_materials[3].setTexture(0, m_sun_texture);
		m_materials[3].MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL;
		if (m_sun_tonemap)
			m_materials[3].Lighting = true;
	}

	if (m_moon_texture) {
		m_materials[4] = mat;
		m_materials[4].setTexture(0, m_moon_texture);
		m_materials[4].MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL;
		if (m_moon_tonemap)
			m_materials[4].Lighting = true;
	}

	for (u32 i = 0; i < SKY_STAR_COUNT; i++) {
		m_stars[i] = v3f(
			myrand_range(-10000, 10000),
			myrand_range(-10000, 10000),
			myrand_range(-10000, 10000)
		);
		m_stars[i].normalize();
	}

	m_directional_colored_fog = g_settings->getBool("directional_colored_fog");

	m_clouds_enabled = true;
}

void Sky::OnRegisterSceneNode()
{
	if (IsVisible)
		SceneManager->registerNodeForRendering(this, scene::ESNRP_SKY_BOX);

	scene::ISceneNode::OnRegisterSceneNode();
}

void Sky::render()
{
	if (!m_visible)
		return;

	video::IVideoDriver* driver = SceneManager->getVideoDriver();
	scene::ICameraSceneNode* camera = SceneManager->getActiveCamera();

	if (!camera || !driver)
		return;

	ScopeProfiler sp(g_profiler, "Sky::render()", SPT_AVG);

	// Draw perspective skybox

	core::matrix4 translate(AbsoluteTransformation);
	translate.setTranslation(camera->getAbsolutePosition());

	// Draw the sky box between the near and far clip plane
	const f32 viewDistance = (camera->getNearValue() + camera->getFarValue()) * 0.5f;
	core::matrix4 scale;
	scale.setScale(core::vector3df(viewDistance, viewDistance, viewDistance));

	driver->setTransform(video::ETS_WORLD, translate * scale);

	if (m_sunlight_seen) {
		float sunsize = 0.07;
		video::SColorf suncolor_f(1, 1, 0, 1);
		suncolor_f.r = 1;
		suncolor_f.g = MYMAX(0.3, MYMIN(1.0, 0.7 + m_time_brightness * 0.5));
		suncolor_f.b = MYMAX(0.0, m_brightness * 0.95);
		video::SColorf suncolor2_f(1, 1, 1, 1);
		suncolor_f.r = 1;
		suncolor_f.g = MYMAX(0.3, MYMIN(1.0, 0.85 + m_time_brightness * 0.5));
		suncolor_f.b = MYMAX(0.0, m_brightness);

		float moonsize = 0.04;
		video::SColorf mooncolor_f(0.50, 0.57, 0.65, 1);
		video::SColorf mooncolor2_f(0.85, 0.875, 0.9, 1);

		float nightlength = 0.415;
		float wn = nightlength / 2;
		float wicked_time_of_day = 0;
		if (m_time_of_day > wn && m_time_of_day < 1.0 - wn)
			wicked_time_of_day = (m_time_of_day - wn) / (1.0 - wn * 2) * 0.5 + 0.25;
		else if (m_time_of_day < 0.5)
			wicked_time_of_day = m_time_of_day / wn * 0.25;
		else
			wicked_time_of_day = 1.0 - ((1.0 - m_time_of_day) / wn * 0.25);
		/*std::cerr<<"time_of_day="<<m_time_of_day<<" -> "
				<<"wicked_time_of_day="<<wicked_time_of_day<<std::endl;*/

		video::SColor suncolor = suncolor_f.toSColor();
		video::SColor suncolor2 = suncolor2_f.toSColor();
		video::SColor mooncolor = mooncolor_f.toSColor();
		video::SColor mooncolor2 = mooncolor2_f.toSColor();

		// Calculate offset normalized to the X dimension of a 512x1 px tonemap
		float offset = (1.0 - fabs(sin((m_time_of_day - 0.5) * irr::core::PI))) * 511;

		if (m_sun_tonemap) {
			u8 * texels = (u8 *)m_sun_tonemap->lock();
			video::SColor* texel = (video::SColor *)(texels + (u32)offset * 4);
			video::SColor texel_color (255, texel->getRed(),
				texel->getGreen(), texel->getBlue());
			m_sun_tonemap->unlock();
			m_materials[3].EmissiveColor = texel_color;
		}

		if (m_moon_tonemap) {
			u8 * texels = (u8 *)m_moon_tonemap->lock();
			video::SColor* texel = (video::SColor *)(texels + (u32)offset * 4);
			video::SColor texel_color (255, texel->getRed(),
				texel->getGreen(), texel->getBlue());
			m_moon_tonemap->unlock();
			m_materials[4].EmissiveColor = texel_color;
		}

		const f32 t = 1.0f;
		const f32 o = 0.0f;
		static const u16 indices[4] = {0, 1, 2, 3};
		video::S3DVertex vertices[4];

		driver->setMaterial(m_materials[1]);

		video::SColor cloudyfogcolor = m_bgcolor;

		// Draw far cloudy fog thing blended with skycolor
		for (u32 j = 0; j < 4; j++) {
			video::SColor c = cloudyfogcolor.getInterpolated(m_skycolor, 0.45);
			vertices[0] = video::S3DVertex(-1, 0.08, -1, 0, 0, 1, c, t, t);
			vertices[1] = video::S3DVertex( 1, 0.08, -1, 0, 0, 1, c, o, t);
			vertices[2] = video::S3DVertex( 1, 0.12, -1, 0, 0, 1, c, o, o);
			vertices[3] = video::S3DVertex(-1, 0.12, -1, 0, 0, 1, c, t, o);
			for (u32 i = 0; i < 4; i++) {
				if (j == 0)
					// Don't switch
					{}
				else if (j == 1)
					// Switch from -Z (south) to +X (east)
					vertices[i].Pos.rotateXZBy(90);
				else if (j == 2)
					// Switch from -Z (south) to -X (west)
					vertices[i].Pos.rotateXZBy(-90);
				else
					// Switch from -Z (south) to +Z (north)
					vertices[i].Pos.rotateXZBy(-180);
			}
			driver->drawIndexedTriangleFan(&vertices[0], 4, indices, 2);
		}

		// Draw far cloudy fog thing
		for (u32 j = 0; j < 4; j++) {
			video::SColor c = cloudyfogcolor;
			vertices[0] = video::S3DVertex(-1, -1.0, -1, 0, 0, 1, c, t, t);
			vertices[1] = video::S3DVertex( 1, -1.0, -1, 0, 0, 1, c, o, t);
			vertices[2] = video::S3DVertex( 1, 0.08, -1, 0, 0, 1, c, o, o);
			vertices[3] = video::S3DVertex(-1, 0.08, -1, 0, 0, 1, c, t, o);
			for (u32 i = 0; i < 4; i++) {
				if (j == 0)
					// Don't switch
					{}
				else if (j == 1)
					// Switch from -Z (south) to +X (east)
					vertices[i].Pos.rotateXZBy(90);
				else if (j == 2)
					// Switch from -Z (south) to -X (west)
					vertices[i].Pos.rotateXZBy(-90);
				else
					// Switch from -Z (south) to +Z (north)
					vertices[i].Pos.rotateXZBy(-180);
			}
			driver->drawIndexedTriangleFan(&vertices[0], 4, indices, 2);
		}

		// Draw bottom far cloudy fog thing
		video::SColor c = cloudyfogcolor;
		vertices[0] = video::S3DVertex(-1, -1.0, -1, 0, 1, 0, c, t, t);
		vertices[1] = video::S3DVertex( 1, -1.0, -1, 0, 1, 0, c, o, t);
		vertices[2] = video::S3DVertex( 1, -1.0, 1, 0, 1, 0, c, o, o);
		vertices[3] = video::S3DVertex(-1, -1.0, 1, 0, 1, 0, c, t, o);
		driver->drawIndexedTriangleFan(&vertices[0], 4, indices, 2);

		driver->setMaterial(m_materials[2]);

		// Draw sunrise/sunset horizon glow texture (textures/base/pack/sunrisebg.png)
		{
			float mid1 = 0.25;
			float mid = wicked_time_of_day < 0.5 ? mid1 : (1.0 - mid1);
			float a_ = 1.0 - fabs(wicked_time_of_day - mid) * 35.0;
			float a = easeCurve(MYMAX(0, MYMIN(1, a_)));
			//std::cerr<<"a_="<<a_<<" a="<<a<<std::endl;
			video::SColor c(255, 255, 255, 255);
			float y = -(1.0 - a) * 0.22;
			vertices[0] = video::S3DVertex(-1, -0.05 + y, -1, 0, 0, 1, c, t, t);
			vertices[1] = video::S3DVertex( 1, -0.05 + y, -1, 0, 0, 1, c, o, t);
			vertices[2] = video::S3DVertex( 1,   0.2 + y, -1, 0, 0, 1, c, o, o);
			vertices[3] = video::S3DVertex(-1,   0.2 + y, -1, 0, 0, 1, c, t, o);
			for (u32 i = 0; i < 4; i++) {
				if (wicked_time_of_day < 0.5)
					// Switch from -Z (south) to +X (east)
					vertices[i].Pos.rotateXZBy(90);
				else
					// Switch from -Z (south) to -X (west)
					vertices[i].Pos.rotateXZBy(-90);
			}
			driver->drawIndexedTriangleFan(&vertices[0], 4, indices, 2);
		}

		// Draw sun
		if (wicked_time_of_day > 0.15 && wicked_time_of_day < 0.85) {
			if (!m_sun_texture) {
				driver->setMaterial(m_materials[1]);
				float d = sunsize * 1.7;
				video::SColor c = suncolor;
				c.setAlpha(0.05 * 255);
				vertices[0] = video::S3DVertex(-d, -d, -1, 0, 0, 1, c, t, t);
				vertices[1] = video::S3DVertex( d, -d, -1, 0, 0, 1, c, o, t);
				vertices[2] = video::S3DVertex( d,  d, -1, 0, 0, 1, c, o, o);
				vertices[3] = video::S3DVertex(-d,  d, -1, 0, 0, 1, c, t, o);
				for (u32 i = 0; i < 4; i++) {
					// Switch from -Z (south) to +X (east)
					vertices[i].Pos.rotateXZBy(90);
					vertices[i].Pos.rotateXYBy(wicked_time_of_day * 360 - 90);
				}
				driver->drawIndexedTriangleFan(&vertices[0], 4, indices, 2);

				d = sunsize * 1.2;
				c = suncolor;
				c.setAlpha(0.15 * 255);
				vertices[0] = video::S3DVertex(-d, -d, -1, 0, 0, 1, c, t, t);
				vertices[1] = video::S3DVertex( d, -d, -1, 0, 0, 1, c, o, t);
				vertices[2] = video::S3DVertex( d,  d, -1, 0, 0, 1, c, o, o);
				vertices[3] = video::S3DVertex(-d,  d, -1, 0, 0, 1, c, t, o);
				for (u32 i = 0; i < 4; i++) {
					// Switch from -Z (south) to +X (east)
					vertices[i].Pos.rotateXZBy(90);
					vertices[i].Pos.rotateXYBy(wicked_time_of_day * 360 - 90);
				}
				driver->drawIndexedTriangleFan(&vertices[0], 4, indices, 2);

				d = sunsize;
				vertices[0] = video::S3DVertex(-d, -d, -1, 0, 0, 1, suncolor, t, t);
				vertices[1] = video::S3DVertex( d, -d, -1, 0, 0, 1, suncolor, o, t);
				vertices[2] = video::S3DVertex( d,  d, -1, 0, 0, 1, suncolor, o, o);
				vertices[3] = video::S3DVertex(-d,  d, -1, 0, 0, 1, suncolor, t, o);
				for (u32 i = 0; i < 4; i++) {
					// Switch from -Z (south) to +X (east)
					vertices[i].Pos.rotateXZBy(90);
					vertices[i].Pos.rotateXYBy(wicked_time_of_day * 360 - 90);
				}
				driver->drawIndexedTriangleFan(&vertices[0], 4, indices, 2);

				d = sunsize * 0.7;
				vertices[0] = video::S3DVertex(-d, -d, -1, 0, 0, 1, suncolor2, t, t);
				vertices[1] = video::S3DVertex( d, -d, -1, 0, 0, 1, suncolor2, o, t);
				vertices[2] = video::S3DVertex( d,  d, -1, 0, 0, 1, suncolor2, o, o);
				vertices[3] = video::S3DVertex(-d,  d, -1, 0, 0, 1, suncolor2, t, o);
				for (u32 i = 0; i < 4; i++) {
					// Switch from -Z (south) to +X (east)
					vertices[i].Pos.rotateXZBy(90);
					vertices[i].Pos.rotateXYBy(wicked_time_of_day * 360 - 90);
				}
				driver->drawIndexedTriangleFan(&vertices[0], 4, indices, 2);
			} else {
				driver->setMaterial(m_materials[3]);
				float d = sunsize * 1.7;
				video::SColor c;
				if (m_sun_tonemap)
					c = video::SColor (0, 0, 0, 0);
				else
					c = video::SColor (255, 255, 255, 255);
				vertices[0] = video::S3DVertex(-d, -d, -1, 0, 0, 1, c, t, t);
				vertices[1] = video::S3DVertex( d, -d, -1, 0, 0, 1, c, o, t);
				vertices[2] = video::S3DVertex( d,  d, -1, 0, 0, 1, c, o, o);
				vertices[3] = video::S3DVertex(-d,  d, -1, 0, 0, 1, c, t, o);
				for(u32 i = 0; i < 4; i++) {
					// Switch from -Z (south) to +X (east)
					vertices[i].Pos.rotateXZBy(90);
					vertices[i].Pos.rotateXYBy(wicked_time_of_day * 360 - 90);
				}
				driver->drawIndexedTriangleFan(&vertices[0], 4, indices, 2);
			}
		}

		// Draw moon
		if (wicked_time_of_day < 0.3 || wicked_time_of_day > 0.7) {
			if (!m_moon_texture) {
				driver->setMaterial(m_materials[1]);
				float d = moonsize * 1.9;
				video::SColor c = mooncolor;
				c.setAlpha(0.05 * 255);
				vertices[0] = video::S3DVertex(-d, -d, -1, 0, 0, 1, c, t, t);
				vertices[1] = video::S3DVertex( d, -d, -1, 0, 0, 1, c, o, t);
				vertices[2] = video::S3DVertex( d,  d, -1, 0, 0, 1, c, o, o);
				vertices[3] = video::S3DVertex(-d,  d, -1, 0, 0, 1, c, t, o);
				for (u32 i = 0; i < 4; i++) {
					// Switch from -Z (south) to -X (west)
					vertices[i].Pos.rotateXZBy(-90);
					vertices[i].Pos.rotateXYBy(wicked_time_of_day * 360 - 90);
				}
				driver->drawIndexedTriangleFan(&vertices[0], 4, indices, 2);

				d = moonsize * 1.3;
				c = mooncolor;
				c.setAlpha(0.15 * 255);
				vertices[0] = video::S3DVertex(-d, -d, -1, 0, 0, 1, c, t, t);
				vertices[1] = video::S3DVertex( d, -d, -1, 0, 0, 1, c, o, t);
				vertices[2] = video::S3DVertex( d,  d, -1, 0, 0, 1, c, o, o);
				vertices[3] = video::S3DVertex(-d,  d, -1, 0, 0, 1, c, t, o);
				for (u32 i = 0; i < 4; i++) {
					// Switch from -Z (south) to -X (west)
					vertices[i].Pos.rotateXZBy(-90);
					vertices[i].Pos.rotateXYBy(wicked_time_of_day * 360 - 90);
				}
				driver->drawIndexedTriangleFan(&vertices[0], 4, indices, 2);

				d = moonsize;
				vertices[0] = video::S3DVertex(-d, -d, -1, 0, 0, 1, mooncolor, t, t);
				vertices[1] = video::S3DVertex( d, -d, -1, 0, 0, 1, mooncolor, o, t);
				vertices[2] = video::S3DVertex( d,  d, -1, 0, 0, 1, mooncolor, o, o);
				vertices[3] = video::S3DVertex(-d,  d, -1, 0, 0, 1, mooncolor, t, o);
				for (u32 i = 0; i < 4; i++) {
					// Switch from -Z (south) to -X (west)
					vertices[i].Pos.rotateXZBy(-90);
					vertices[i].Pos.rotateXYBy(wicked_time_of_day * 360 - 90);
				}
				driver->drawIndexedTriangleFan(&vertices[0], 4, indices, 2);

				float d2 = moonsize * 0.6;
				vertices[0] = video::S3DVertex(-d, -d,  -1, 0, 0, 1, mooncolor2, t, t);
				vertices[1] = video::S3DVertex( d2,-d,  -1, 0, 0, 1, mooncolor2, o, t);
				vertices[2] = video::S3DVertex( d2, d2, -1, 0, 0, 1, mooncolor2, o, o);
				vertices[3] = video::S3DVertex(-d,  d2, -1, 0, 0, 1, mooncolor2, t, o);
				for (u32 i = 0; i < 4; i++) {
					// Switch from -Z (south) to -X (west)
					vertices[i].Pos.rotateXZBy(-90);
					vertices[i].Pos.rotateXYBy(wicked_time_of_day * 360 - 90);
				}
				driver->drawIndexedTriangleFan(&vertices[0], 4, indices, 2);
			} else {
				driver->setMaterial(m_materials[4]);
				float d = moonsize * 1.9;
				video::SColor c;
				if (m_moon_tonemap)
					c = video::SColor (0, 0, 0, 0);
				else
					c = video::SColor (255, 255, 255, 255);
				vertices[0] = video::S3DVertex(-d, -d, -1, 0, 0, 1, c, t, t);
				vertices[1] = video::S3DVertex( d, -d, -1, 0, 0, 1, c, o, t);
				vertices[2] = video::S3DVertex( d,  d, -1, 0, 0, 1, c, o, o);
				vertices[3] = video::S3DVertex(-d,  d, -1, 0, 0, 1, c, t, o);
				for (u32 i = 0; i < 4; i++) {
					// Switch from -Z (south) to -X (west)
					vertices[i].Pos.rotateXZBy(-90);
					vertices[i].Pos.rotateXYBy(wicked_time_of_day * 360 - 90);
				}
				driver->drawIndexedTriangleFan(&vertices[0], 4, indices, 2);
			}
		}

		// Draw stars
		driver->setMaterial(m_materials[1]);
		do {
			float starbrightness = MYMAX(0, MYMIN(1,
				(0.285 - fabs(wicked_time_of_day < 0.5 ?
				wicked_time_of_day : (1.0 - wicked_time_of_day))) * 10));
			float f = starbrightness;
			float d = 0.007;
			video::SColor starcolor(255, f * 90, f * 90, f * 90);
			if (starcolor.getBlue() < m_skycolor.getBlue())
				break;
			u16 indices[SKY_STAR_COUNT * 4];
			video::S3DVertex vertices[SKY_STAR_COUNT * 4];
			for (u32 i = 0; i < SKY_STAR_COUNT; i++) {
				indices[i * 4 + 0] = i * 4 + 0;
				indices[i * 4 + 1] = i * 4 + 1;
				indices[i * 4 + 2] = i * 4 + 2;
				indices[i * 4 + 3] = i * 4 + 3;
				v3f p = m_stars[i];
				core::CMatrix4<f32> a;
				a.buildRotateFromTo(v3f(0, 1, 0), v3f(d, 1 + d / 2, 0));
				v3f p1 = p;
				a.rotateVect(p1);
				a.buildRotateFromTo(v3f(0, 1, 0), v3f(d, 1, d));
				v3f p2 = p;
				a.rotateVect(p2);
				a.buildRotateFromTo(v3f(0, 1, 0), v3f(0, 1 - d / 2, d));
				v3f p3 = p;
				a.rotateVect(p3);
				p.rotateXYBy(wicked_time_of_day * 360 - 90);
				p1.rotateXYBy(wicked_time_of_day * 360 - 90);
				p2.rotateXYBy(wicked_time_of_day * 360 - 90);
				p3.rotateXYBy(wicked_time_of_day * 360 - 90);
				vertices[i * 4 + 0].Pos = p;
				vertices[i * 4 + 0].Color = starcolor;
				vertices[i * 4 + 1].Pos = p1;
				vertices[i * 4 + 1].Color = starcolor;
				vertices[i * 4 + 2].Pos = p2;
				vertices[i * 4 + 2].Color = starcolor;
				vertices[i * 4 + 3].Pos = p3;
				vertices[i * 4 + 3].Color = starcolor;
			}
			driver->drawVertexPrimitiveList(vertices, SKY_STAR_COUNT * 4,
				indices, SKY_STAR_COUNT, video::EVT_STANDARD,
				scene::EPT_QUADS, video::EIT_16BIT);
		} while(0);

		// Draw far cloudy fog thing below east and west horizons
		for (u32 j = 0; j < 2; j++) {
			video::SColor c = cloudyfogcolor;
			vertices[0] = video::S3DVertex(-1, -1.0,  -1, 0, 0, 1, c, t, t);
			vertices[1] = video::S3DVertex( 1, -1.0,  -1, 0, 0, 1, c, o, t);
			vertices[2] = video::S3DVertex( 1, -0.02, -1, 0, 0, 1, c, o, o);
			vertices[3] = video::S3DVertex(-1, -0.02, -1, 0, 0, 1, c, t, o);
			for (u32 i = 0; i < 4; i++) {
				//if (wicked_time_of_day < 0.5)
				if (j == 0)
					// Switch from -Z (south) to +X (east)
					vertices[i].Pos.rotateXZBy(90);
				else
					// Switch from -Z (south) to -X (west)
					vertices[i].Pos.rotateXZBy(-90);
			}
			driver->drawIndexedTriangleFan(&vertices[0], 4, indices, 2);
		}
	}
}

void Sky::update(float time_of_day, float time_brightness,
		float direct_brightness, bool sunlight_seen,
		CameraMode cam_mode, float yaw, float pitch)
{
	// Stabilize initial brightness and color values by flooding updates
	if (m_first_update) {
		/*dstream<<"First update with time_of_day="<<time_of_day
				<<" time_brightness="<<time_brightness
				<<" direct_brightness="<<direct_brightness
				<<" sunlight_seen="<<sunlight_seen<<std::endl;*/
		m_first_update = false;
		for (u32 i = 0; i < 100; i++) {
			update(time_of_day, time_brightness, direct_brightness,
				sunlight_seen, cam_mode, yaw, pitch);
		}
		return;
	}

	m_time_of_day = time_of_day;
	m_time_brightness = time_brightness;
	m_sunlight_seen = sunlight_seen;

	bool is_dawn = (time_brightness >= 0.20 && time_brightness < 0.35);

	/*
	Development colours

	video::SColorf bgcolor_bright_normal_f(170. / 255, 200. / 255, 230. / 255, 1.0);
	video::SColorf bgcolor_bright_dawn_f(0.666, 200. / 255 * 0.7, 230. / 255 * 0.5, 1.0);
	video::SColorf bgcolor_bright_dawn_f(0.666, 0.549, 0.220, 1.0);
	video::SColorf bgcolor_bright_dawn_f(0.666 * 1.2, 0.549 * 1.0, 0.220 * 1.0, 1.0);
	video::SColorf bgcolor_bright_dawn_f(0.666 * 1.2, 0.549 * 1.0, 0.220 * 1.2, 1.0);

	video::SColorf cloudcolor_bright_dawn_f(1.0, 0.591, 0.4);
	video::SColorf cloudcolor_bright_dawn_f(1.0, 0.65, 0.44);
	video::SColorf cloudcolor_bright_dawn_f(1.0, 0.7, 0.5);
	*/

	video::SColorf bgcolor_bright_normal_f = video::SColor(255, 155, 193, 240);
	video::SColorf bgcolor_bright_indoor_f = video::SColor(255, 100, 100, 100);
	video::SColorf bgcolor_bright_dawn_f = video::SColor(255, 186, 193, 240);
	video::SColorf bgcolor_bright_night_f = video::SColor(255, 64, 144, 255);

	video::SColorf skycolor_bright_normal_f = video::SColor(255, 140, 186, 250);
	video::SColorf skycolor_bright_dawn_f = video::SColor(255, 180, 186, 250);
	video::SColorf skycolor_bright_night_f = video::SColor(255, 0, 107, 255);

	// pure white: becomes "diffuse light component" for clouds
	video::SColorf cloudcolor_bright_normal_f = video::SColor(255, 255, 255, 255);
	// dawn-factoring version of pure white (note: R is above 1.0)
	video::SColorf cloudcolor_bright_dawn_f(255.0f/240.0f, 223.0f/240.0f, 191.0f/255.0f);

	float cloud_color_change_fraction = 0.95;
	if (sunlight_seen) {
		if (fabs(time_brightness - m_brightness) < 0.2) {
			m_brightness = m_brightness * 0.95 + time_brightness * 0.05;
		} else {
			m_brightness = m_brightness * 0.80 + time_brightness * 0.20;
			cloud_color_change_fraction = 0.0;
		}
	} else {
		if (direct_brightness < m_brightness)
			m_brightness = m_brightness * 0.95 + direct_brightness * 0.05;
		else
			m_brightness = m_brightness * 0.98 + direct_brightness * 0.02;
	}

	m_clouds_visible = true;
	float color_change_fraction = 0.98;
	if (sunlight_seen) {
		if (is_dawn) {  // Dawn
			m_bgcolor_bright_f = m_bgcolor_bright_f.getInterpolated(
				bgcolor_bright_dawn_f, color_change_fraction);
			m_skycolor_bright_f = m_skycolor_bright_f.getInterpolated(
				skycolor_bright_dawn_f, color_change_fraction);
			m_cloudcolor_bright_f = m_cloudcolor_bright_f.getInterpolated(
				cloudcolor_bright_dawn_f, color_change_fraction);
		} else {
			if (time_brightness < 0.07) {  // Night
				m_bgcolor_bright_f = m_bgcolor_bright_f.getInterpolated(
					bgcolor_bright_night_f, color_change_fraction);
				m_skycolor_bright_f = m_skycolor_bright_f.getInterpolated(
					skycolor_bright_night_f, color_change_fraction);
			} else {  // Day
				m_bgcolor_bright_f = m_bgcolor_bright_f.getInterpolated(
					bgcolor_bright_normal_f, color_change_fraction);
				m_skycolor_bright_f = m_skycolor_bright_f.getInterpolated(
					skycolor_bright_normal_f, color_change_fraction);
			}

			m_cloudcolor_bright_f = m_cloudcolor_bright_f.getInterpolated(
				cloudcolor_bright_normal_f, color_change_fraction);
		}
	} else {
		m_bgcolor_bright_f = m_bgcolor_bright_f.getInterpolated(
			bgcolor_bright_indoor_f, color_change_fraction);
		m_skycolor_bright_f = m_skycolor_bright_f.getInterpolated(
			bgcolor_bright_indoor_f, color_change_fraction);
		m_cloudcolor_bright_f = m_cloudcolor_bright_f.getInterpolated(
			cloudcolor_bright_normal_f, color_change_fraction);
		m_clouds_visible = false;
	}

	video::SColor bgcolor_bright = m_bgcolor_bright_f.toSColor();
	m_bgcolor = video::SColor(
		255,
		bgcolor_bright.getRed() * m_brightness,
		bgcolor_bright.getGreen() * m_brightness,
		bgcolor_bright.getBlue() * m_brightness
	);

	video::SColor skycolor_bright = m_skycolor_bright_f.toSColor();
	m_skycolor = video::SColor(
		255,
		skycolor_bright.getRed() * m_brightness,
		skycolor_bright.getGreen() * m_brightness,
		skycolor_bright.getBlue() * m_brightness
	);

	// Horizon coloring based on sun and moon direction during sunset and sunrise
	video::SColor pointcolor = video::SColor(m_bgcolor.getAlpha(), 255, 255, 255);
	if (m_directional_colored_fog) {
		if (m_horizon_blend() != 0) {
			// Calculate hemisphere value from yaw, (inverted in third person front view)
			s8 dir_factor = 1;
			if (cam_mode > CAMERA_MODE_THIRD)
				dir_factor = -1;
			f32 pointcolor_blend = wrapDegrees_0_360(yaw * dir_factor + 90);
			if (pointcolor_blend > 180)
				pointcolor_blend = 360 - pointcolor_blend;
			pointcolor_blend /= 180;
			// Bound view angle to determine where transition starts and ends
			pointcolor_blend = rangelim(1 - pointcolor_blend * 1.375, 0, 1 / 1.375) *
				1.375;
			// Combine the colors when looking up or down, otherwise turning looks weird
			pointcolor_blend += (0.5 - pointcolor_blend) *
				(1 - MYMIN((90 - std::fabs(pitch)) / 90 * 1.5, 1));
			// Invert direction to match where the sun and moon are rising
			if (m_time_of_day > 0.5)
				pointcolor_blend = 1 - pointcolor_blend;
			// Horizon colors of sun and moon
			f32 pointcolor_light = rangelim(m_time_brightness * 3, 0.2, 1);

			video::SColorf pointcolor_sun_f(1, 1, 1, 1);
			if (m_sun_tonemap) {
				pointcolor_sun_f.r = pointcolor_light *
					(float)m_materials[3].EmissiveColor.getRed() / 255;
				pointcolor_sun_f.b = pointcolor_light *
					(float)m_materials[3].EmissiveColor.getBlue() / 255;
				pointcolor_sun_f.g = pointcolor_light *
					(float)m_materials[3].EmissiveColor.getGreen() / 255;
			} else {
				pointcolor_sun_f.r = pointcolor_light * 1;
				pointcolor_sun_f.b = pointcolor_light *
					(0.25 + (rangelim(m_time_brightness, 0.25, 0.75) - 0.25) * 2 * 0.75);
				pointcolor_sun_f.g = pointcolor_light * (pointcolor_sun_f.b * 0.375 +
					(rangelim(m_time_brightness, 0.05, 0.15) - 0.05) * 10 * 0.625);
			}

			video::SColorf pointcolor_moon_f(0.5 * pointcolor_light,
				0.6 * pointcolor_light, 0.8 * pointcolor_light, 1);
			if (m_moon_tonemap) {
				pointcolor_moon_f.r = pointcolor_light *
					(float)m_materials[4].EmissiveColor.getRed() / 255;
				pointcolor_moon_f.b = pointcolor_light *
					(float)m_materials[4].EmissiveColor.getBlue() / 255;
				pointcolor_moon_f.g = pointcolor_light *
					(float)m_materials[4].EmissiveColor.getGreen() / 255;
			}

			video::SColor pointcolor_sun = pointcolor_sun_f.toSColor();
			video::SColor pointcolor_moon = pointcolor_moon_f.toSColor();
			// Calculate the blend color
			pointcolor = m_mix_scolor(pointcolor_moon, pointcolor_sun, pointcolor_blend);
		}
		m_bgcolor = m_mix_scolor(m_bgcolor, pointcolor, m_horizon_blend() * 0.5);
		m_skycolor = m_mix_scolor(m_skycolor, pointcolor, m_horizon_blend() * 0.25);
	}

	float cloud_direct_brightness = 0;
	if (sunlight_seen) {
		if (!m_directional_colored_fog) {
			cloud_direct_brightness = time_brightness;
			if (time_brightness >= 0.2 && time_brightness < 0.7)
				cloud_direct_brightness *= 1.3;
		} else {
			cloud_direct_brightness = MYMIN(m_horizon_blend() * 0.15 +
				m_time_brightness, 1);
		}
	} else {
		cloud_direct_brightness = direct_brightness;
	}

	m_cloud_brightness = m_cloud_brightness * cloud_color_change_fraction +
		cloud_direct_brightness * (1.0 - cloud_color_change_fraction);
	m_cloudcolor_f = video::SColorf(
		m_cloudcolor_bright_f.r * m_cloud_brightness,
		m_cloudcolor_bright_f.g * m_cloud_brightness,
		m_cloudcolor_bright_f.b * m_cloud_brightness,
		1.0
	);
	if (m_directional_colored_fog) {
		m_cloudcolor_f = m_mix_scolorf(m_cloudcolor_f,
			video::SColorf(pointcolor), m_horizon_blend() * 0.25);
	}
}


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 <ISceneNode.h>
#include "camera.h"
#include "irrlichttypes_extrabloated.h"

#ifndef SKY_HEADER
#define SKY_HEADER

#define SKY_MATERIAL_COUNT 5
#define SKY_STAR_COUNT 200

class ITextureSource;

// Skybox, rendered with zbuffer turned off, before all other nodes.
class Sky : public scene::ISceneNode
{
public:
	//! constructor
	Sky(scene::ISceneNode *parent, scene::ISceneManager *mgr, s32 id,
			ITextureSource *tsrc);

	virtual void OnRegisterSceneNode();

	//! renders the node.
	virtual void render();

	virtual const aabb3f &getBoundingBox() const { return m_box; }

	// Used by Irrlicht for optimizing rendering
	virtual video::SMaterial &getMaterial(u32 i) { return m_materials[i]; }

	// Used by Irrlicht for optimizing rendering
	virtual u32 getMaterialCount() const { return SKY_MATERIAL_COUNT; }

	void update(float m_time_of_day, float time_brightness, float direct_brightness,
			bool sunlight_seen, CameraMode cam_mode, float yaw, float pitch);

	float getBrightness() { return m_brightness; }

	const video::SColor &getBgColor() const
	{
		return m_visible ? m_bgcolor : m_fallback_bg_color;
	}

	const video::SColor &getSkyColor() const
	{
		return m_visible ? m_skycolor : m_fallback_bg_color;
	}

	bool getCloudsVisible() { return m_clouds_visible && m_clouds_enabled; }
	const video::SColorf &getCloudColor() { return m_cloudcolor_f; }

	void setVisible(bool visible) { m_visible = visible; }
	// Set only from set_sky API
	void setCloudsEnabled(bool clouds_enabled) { m_clouds_enabled = clouds_enabled; }
	void setFallbackBgColor(const video::SColor &fallback_bg_color)
	{
		m_fallback_bg_color = fallback_bg_color;
	}

private:
	aabb3f m_box;
	video::SMaterial m_materials[SKY_MATERIAL_COUNT];

	// How much sun & moon transition should affect horizon color
	float m_horizon_blend()
	{
		if (!m_sunlight_seen)
			return 0;
		float x = m_time_of_day >= 0.5 ? (1 - m_time_of_day) * 2
					       : m_time_of_day * 2;

		if (x <= 0.3)
			return 0;
		if (x <= 0.4) // when the sun and moon are aligned
			return (x - 0.3) * 10;
		if (x <= 0.5)
			return (0.5 - x) * 10;
		return 0;
	}

	// Mix two colors by a given amount
	video::SColor m_mix_scolor(video::SColor col1, video::SColor col2, f32 factor)
	{
		video::SColor result = video::SColor(
				col1.getAlpha() * (1 - factor) + col2.getAlpha() * factor,
				col1.getRed() * (1 - factor) + col2.getRed() * factor,
				col1.getGreen() * (1 - factor) + col2.getGreen() * factor,
				col1.getBlue() * (1 - factor) + col2.getBlue() * factor);
		return result;
	}
	video::SColorf m_mix_scolorf(video::SColorf col1, video::SColorf col2, f32 factor)
	{
		video::SColorf result =
				video::SColorf(col1.r * (1 - factor) + col2.r * factor,
						col1.g * (1 - factor) + col2.g * factor,
						col1.b * (1 - factor) + col2.b * factor,
						col1.a * (1 - factor) + col2.a * factor);
		return result;
	}

	bool m_visible;
	video::SColor m_fallback_bg_color; // Used when m_visible=false
	bool m_first_update;
	float m_time_of_day;
	float m_time_brightness;
	bool m_sunlight_seen;
	float m_brightness;
	float m_cloud_brightness;
	bool m_clouds_visible; // Whether clouds are disabled due to player underground
	bool m_clouds_enabled; // Initialised to true, reset only by set_sky API
	bool m_directional_colored_fog;
	video::SColorf m_bgcolor_bright_f;
	video::SColorf m_skycolor_bright_f;
	video::SColorf m_cloudcolor_bright_f;
	video::SColor m_bgcolor;
	video::SColor m_skycolor;
	video::SColorf m_cloudcolor_f;
	v3f m_stars[SKY_STAR_COUNT];
	video::S3DVertex m_star_vertices[SKY_STAR_COUNT * 4];
	video::ITexture *m_sun_texture;
	video::ITexture *m_moon_texture;
	video::ITexture *m_sun_tonemap;
	video::ITexture *m_moon_tonemap;
};

#endif

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

#include "socket.h"

#include <stdio.h>
#include <iostream>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <sstream>
#include <iomanip>
#include "util/string.h"
#include "util/numeric.h"
#include "constants.h"
#include "debug.h"
#include "settings.h"
#include "log.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>
	#include <ws2tcpip.h>
	#define LAST_SOCKET_ERR() WSAGetLastError()
	typedef SOCKET socket_t;
	typedef int socklen_t;
#else
	#include <sys/types.h>
	#include <sys/socket.h>
	#include <netinet/in.h>
	#include <fcntl.h>
	#include <netdb.h>
	#include <unistd.h>
	#include <arpa/inet.h>
	#define LAST_SOCKET_ERR() (errno)
	typedef int socket_t;
#endif

// Set to true to enable verbose debug output
bool socket_enable_debug_output = false;        // yuck

static bool g_sockets_initialized = false;

// Initialize sockets
void sockets_init()
{
#ifdef _WIN32
	// Windows needs sockets to be initialized before use
	WSADATA WsaData;
	if(WSAStartup( MAKEWORD(2,2), &WsaData ) != NO_ERROR)
		throw SocketException("WSAStartup failed");
#endif
	g_sockets_initialized = true;
}

void sockets_cleanup()
{
#ifdef _WIN32
	// On Windows, cleanup sockets after use
	WSACleanup();
#endif
}

/*
	Address
*/

Address::Address()
{
	m_addr_family = 0;
	memset(&m_address, 0, sizeof(m_address));
	m_port = 0;
}

Address::Address(u32 address, u16 port)
{
	memset(&m_address, 0, sizeof(m_address));
	setAddress(address);
	setPort(port);
}

Address::Address(u8 a, u8 b, u8 c, u8 d, u16 port)
{
	memset(&m_address, 0, sizeof(m_address));
	setAddress(a, b, c, d);
	setPort(port);
}

Address::Address(const IPv6AddressBytes *ipv6_bytes, u16 port)
{
	memset(&m_address, 0, sizeof(m_address));
	setAddress(ipv6_bytes);
	setPort(port);
}

// Equality (address family, address and port must be equal)
bool Address::operator==(const Address &address)
{
	if(address.m_addr_family != m_addr_family || address.m_port != m_port)
		return false;
	else if(m_addr_family == AF_INET)
	{
		return m_address.ipv4.sin_addr.s_addr ==
		       address.m_address.ipv4.sin_addr.s_addr;
	}
	else if(m_addr_family == AF_INET6)
	{
		return memcmp(m_address.ipv6.sin6_addr.s6_addr,
		              address.m_address.ipv6.sin6_addr.s6_addr, 16) == 0;
	}
	else
		return false;
}

bool Address::operator!=(const Address &address)
{
	return !(*this == address);
}

void Address::Resolve(const char *name)
{
	if (!name || name[0] == 0) {
		if (m_addr_family == AF_INET) {
			setAddress((u32) 0);
		} else if (m_addr_family == AF_INET6) {
			setAddress((IPv6AddressBytes*) 0);
		}
		return;
	}

	struct addrinfo *resolved, hints;
	memset(&hints, 0, sizeof(hints));

	// Setup hints
	hints.ai_socktype = 0;
	hints.ai_protocol = 0;
	hints.ai_flags    = 0;
	if(g_settings->getBool("enable_ipv6"))
	{
		// AF_UNSPEC allows both IPv6 and IPv4 addresses to be returned
		hints.ai_family = AF_UNSPEC;
	}
	else
	{
		hints.ai_family = AF_INET;
	}

	// Do getaddrinfo()
	int e = getaddrinfo(name, NULL, &hints, &resolved);
	if(e != 0)
		throw ResolveError(gai_strerror(e));

	// Copy data
	if(resolved->ai_family == AF_INET)
	{
		struct sockaddr_in *t = (struct sockaddr_in *) resolved->ai_addr;
		m_addr_family = AF_INET;
		m_address.ipv4 = *t;
	}
	else if(resolved->ai_family == AF_INET6)
	{
		struct sockaddr_in6 *t = (struct sockaddr_in6 *) resolved->ai_addr;
		m_addr_family = AF_INET6;
		m_address.ipv6 = *t;
	}
	else
	{
		freeaddrinfo(resolved);
		throw ResolveError("");
	}
	freeaddrinfo(resolved);
}

// IP address -> textual representation
std::string Address::serializeString() const
{
// windows XP doesnt have inet_ntop, maybe use better func
#ifdef _WIN32
	if(m_addr_family == AF_INET)
	{
		u8 a, b, c, d;
		u32 addr;
		addr = ntohl(m_address.ipv4.sin_addr.s_addr);
		a = (addr & 0xFF000000) >> 24;
		b = (addr & 0x00FF0000) >> 16;
		c = (addr & 0x0000FF00) >> 8;
		d = (addr & 0x000000FF);
		return itos(a) + "." + itos(b) + "." + itos(c) + "." + itos(d);
	}
	else if(m_addr_family == AF_INET6)
	{
		std::ostringstream os;
		for(int i = 0; i < 16; i += 2)
		{
			u16 section =
			(m_address.ipv6.sin6_addr.s6_addr[i] << 8) |
			(m_address.ipv6.sin6_addr.s6_addr[i + 1]);
			os << std::hex << section;
			if(i < 14)
				os << ":";
		}
		return os.str();
	}
	else
		return std::string("");
#else
	char str[INET6_ADDRSTRLEN];
	if (inet_ntop(m_addr_family, (m_addr_family == AF_INET) ? (void*)&(m_address.ipv4.sin_addr) : (void*)&(m_address.ipv6.sin6_addr), str, INET6_ADDRSTRLEN) == NULL) {
		return std::string("");
	}
	return std::string(str);
#endif
}

struct sockaddr_in Address::getAddress() const
{
	return m_address.ipv4; // NOTE: NO PORT INCLUDED, use getPort()
}

struct sockaddr_in6 Address::getAddress6() const
{
	return m_address.ipv6; // NOTE: NO PORT INCLUDED, use getPort()
}

u16 Address::getPort() const
{
	return m_port;
}

int Address::getFamily() const
{
	return m_addr_family;
}

bool Address::isIPv6() const
{
	return m_addr_family == AF_INET6;
}

bool Address::isZero() const
{
	if (m_addr_family == AF_INET) {
		return m_address.ipv4.sin_addr.s_addr == 0;
	} else if (m_addr_family == AF_INET6) {
		static const char zero[16] = {0};
		return memcmp(m_address.ipv6.sin6_addr.s6_addr,
		              zero, 16) == 0;
	}
	return false;
}

void Address::setAddress(u32 address)
{
	m_addr_family = AF_INET;
	m_address.ipv4.sin_family = AF_INET;
	m_address.ipv4.sin_addr.s_addr = htonl(address);
}

void Address::setAddress(u8 a, u8 b, u8 c, u8 d)
{
	m_addr_family = AF_INET;
	m_address.ipv4.sin_family = AF_INET;
	u32 addr = htonl((a << 24) | (b << 16) | (c << 8) | d);
	m_address.ipv4.sin_addr.s_addr = addr;
}

void Address::setAddress(const IPv6AddressBytes *ipv6_bytes)
{
	m_addr_family = AF_INET6;
	m_address.ipv6.sin6_family = AF_INET6;
	if (ipv6_bytes)
		memcpy(m_address.ipv6.sin6_addr.s6_addr, ipv6_bytes->bytes, 16);
	else
		memset(m_address.ipv6.sin6_addr.s6_addr, 0, 16);
}

void Address::setPort(u16 port)
{
	m_port = port;
}

void Address::print(std::ostream *s) const
{
	if(m_addr_family == AF_INET6)
		*s << "[" << serializeString() << "]:" << m_port;
	else
		*s << serializeString() << ":" << m_port;
}

/*
	UDPSocket
*/

UDPSocket::UDPSocket(bool ipv6)
{
	init(ipv6, false);
}

bool UDPSocket::init(bool ipv6, bool noExceptions)
{
	if (g_sockets_initialized == false) {
		dstream << "Sockets not initialized" << std::endl;
		return false;
	}

	// Use IPv6 if specified
	m_addr_family = ipv6 ? AF_INET6 : AF_INET;
	m_handle = socket(m_addr_family, SOCK_DGRAM, IPPROTO_UDP);

	if (socket_enable_debug_output) {
		dstream << "UDPSocket(" << (int) m_handle
		        << ")::UDPSocket(): ipv6 = "
		        << (ipv6 ? "true" : "false")
		        << std::endl;
	}

	if (m_handle <= 0) {
		if (noExceptions) {
			return false;
		} else {
			throw SocketException(std::string("Failed to create socket: error ")
				+ itos(LAST_SOCKET_ERR()));
		}
	}

	setTimeoutMs(0);

	return true;
}

UDPSocket::~UDPSocket()
{
	if (socket_enable_debug_output) {
		dstream << "UDPSocket( " << (int) m_handle << ")::~UDPSocket()"
		        << std::endl;
	}

#ifdef _WIN32
	closesocket(m_handle);
#else
	close(m_handle);
#endif
}

void UDPSocket::Bind(Address addr)
{
	if(socket_enable_debug_output) {
		dstream << "UDPSocket(" << (int) m_handle << ")::Bind(): "
		        << addr.serializeString() << ":"
		        << addr.getPort() << std::endl;
	}

	if (addr.getFamily() != m_addr_family) {
		static const char *errmsg = "Socket and bind address families do not match";
		errorstream << "Bind failed: " << errmsg << std::endl;
		throw SocketException(errmsg);
	}

	if(m_addr_family == AF_INET6) {
		struct sockaddr_in6 address;
		memset(&address, 0, sizeof(address));

		address             = addr.getAddress6();
		address.sin6_family = AF_INET6;
		address.sin6_port   = htons(addr.getPort());

		if(bind(m_handle, (const struct sockaddr *) &address,
				sizeof(struct sockaddr_in6)) < 0) {
			dstream << (int) m_handle << ": Bind failed: "
			        << strerror(errno) << std::endl;
			throw SocketException("Failed to bind socket");
		}
	} else {
		struct sockaddr_in address;
		memset(&address, 0, sizeof(address));

		address                 = addr.getAddress();
		address.sin_family      = AF_INET;
		address.sin_port        = htons(addr.getPort());

		if (bind(m_handle, (const struct sockaddr *) &address,
				sizeof(struct sockaddr_in)) < 0) {
			dstream << (int)m_handle << ": Bind failed: "
			        << strerror(errno) << std::endl;
			throw SocketException("Failed to bind socket");
		}
	}
}

void UDPSocket::Send(const Address & destination, const void * data, int size)
{
	bool dumping_packet = false; // for INTERNET_SIMULATOR

	if(INTERNET_SIMULATOR)
		dumping_packet = myrand() % INTERNET_SIMULATOR_PACKET_LOSS == 0;

	if(socket_enable_debug_output) {
		// Print packet destination and size
		dstream << (int)m_handle << " -> ";
		destination.print(&dstream);
		dstream << ", size=" << size;

		// Print packet contents
		dstream << ", data=";
		for(int i = 0; i < size && i < 20; i++) {
			if(i % 2 == 0)
				dstream << " ";
			unsigned int a = ((const unsigned char *)data)[i];
			dstream << std::hex << std::setw(2) << std::setfill('0') << a;
		}

		if(size > 20)
			dstream << "...";

		if(dumping_packet)
			dstream << " (DUMPED BY INTERNET_SIMULATOR)";

		dstream << std::endl;
	}

	if(dumping_packet) {
		// Lol let's forget it
		dstream << "UDPSocket::Send(): INTERNET_SIMULATOR: dumping packet."
				<< std::endl;
		return;
	}

	if(destination.getFamily() != m_addr_family)
		throw SendFailedException("Address family mismatch");

	int sent;
	if(m_addr_family == AF_INET6) {
		struct sockaddr_in6 address = destination.getAddress6();
		address.sin6_port = htons(destination.getPort());
		sent = sendto(m_handle, (const char *)data, size,
				0, (struct sockaddr *)&address, sizeof(struct sockaddr_in6));
	} else {
		struct sockaddr_in address = destination.getAddress();
		address.sin_port = htons(destination.getPort());
		sent = sendto(m_handle, (const char *)data, size,
				0, (struct sockaddr *)&address, sizeof(struct sockaddr_in));
	}

	if(sent != size)
		throw SendFailedException("Failed to send packet");
}

int UDPSocket::Receive(Address & sender, void *data, int size)
{
	// Return on timeout
	if(WaitData(m_timeout_ms) == false)
		return -1;

	int received;
	if (m_addr_family == AF_INET6) {
		struct sockaddr_in6 address;
		memset(&address, 0, sizeof(address));
		socklen_t address_len = sizeof(address);

		received = recvfrom(m_handle, (char *) data,
				size, 0, (struct sockaddr *) &address, &address_len);

		if(received < 0)
			return -1;

		u16 address_port = ntohs(address.sin6_port);
		IPv6AddressBytes bytes;
		memcpy(bytes.bytes, address.sin6_addr.s6_addr, 16);
		sender = Address(&bytes, address_port);
	} else {
		struct sockaddr_in address;
		memset(&address, 0, sizeof(address));

		socklen_t address_len = sizeof(address);

		received = recvfrom(m_handle, (char *)data,
				size, 0, (struct sockaddr *)&address, &address_len);

		if(received < 0)
			return -1;

		u32 address_ip = ntohl(address.sin_addr.s_addr);
		u16 address_port = ntohs(address.sin_port);

		sender = Address(address_ip, address_port);
	}

	if (socket_enable_debug_output) {
		// Print packet sender and size
		dstream << (int) m_handle << " <- ";
		sender.print(&dstream);
		dstream << ", size=" << received;

		// Print packet contents
		dstream << ", data=";
		for(int i = 0; i < received && i < 20; i++) {
			if(i % 2 == 0)
				dstream << " ";
			unsigned int a = ((const unsigned char *) data)[i];
			dstream << std::hex << std::setw(2) << std::setfill('0') << a;
		}
		if(received > 20)
			dstream << "...";

		dstream << std::endl;
	}

	return received;
}

int UDPSocket::GetHandle()
{
	return m_handle;
}

void UDPSocket::setTimeoutMs(int timeout_ms)
{
	m_timeout_ms = timeout_ms;
}

bool UDPSocket::WaitData(int timeout_ms)
{
	fd_set readset;
	int result;

	// Initialize the set
	FD_ZERO(&readset);
	FD_SET(m_handle, &readset);

	// Initialize time out struct
	struct timeval tv;
	tv.tv_sec = 0;
	tv.tv_usec = timeout_ms * 1000;

	// select()
	result = select(m_handle+1, &readset, NULL, NULL, &tv);

	if (result == 0)
		return false;
	else if (result < 0 && (errno == EINTR || errno == EBADF)) {
		// N.B. select() fails when sockets are destroyed on Connection's dtor
		// with EBADF.  Instead of doing tricky synchronization, allow this
		// thread to exit but don't throw an exception.
		return false;
	} else if (result < 0) {
		dstream << (int) m_handle << ": Select failed: "
		        << strerror(errno) << std::endl;

#ifdef _WIN32
		int e = WSAGetLastError();
		dstream << (int) m_handle << ": WSAGetLastError()="
		        << e << std::endl;
		if (e == 10004 /* WSAEINTR */ || e == 10009 /* WSAEBADF */) {
			infostream << "Ignoring WSAEINTR/WSAEBADF." << std::endl;
			return false;
		}
#endif

		throw SocketException("Select failed");
	} else if(FD_ISSET(m_handle, &readset) == false) {
		// No data
		return false;
	}

	// There is data
	return true;
}


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

#ifdef _WIN32
	#ifndef WIN32_LEAN_AND_MEAN
		#define WIN32_LEAN_AND_MEAN
	#endif
#ifndef _WIN32_WINNT
	#define _WIN32_WINNT 0x0501
#endif
	#include <windows.h>
	#include <winsock2.h>
	#include <ws2tcpip.h>
#else
	#include <sys/socket.h>
	#include <netinet/in.h>
#endif

#include <ostream>
#include <string.h>
#include "irrlichttypes.h"
#include "exceptions.h"

extern bool socket_enable_debug_output;

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

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

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

void sockets_init();
void sockets_cleanup();

class IPv6AddressBytes
{
public:
	u8 bytes[16];
	IPv6AddressBytes() { memset(bytes, 0, 16); }
};

class Address
{
public:
	Address();
	Address(u32 address, u16 port);
	Address(u8 a, u8 b, u8 c, u8 d, u16 port);
	Address(const IPv6AddressBytes *ipv6_bytes, u16 port);
	bool operator==(const Address &address);
	bool operator!=(const Address &address);
	// Resolve() may throw ResolveError (address is unchanged in this case)
	void Resolve(const char *name);
	struct sockaddr_in getAddress() const;
	unsigned short getPort() const;
	void setAddress(u32 address);
	void setAddress(u8 a, u8 b, u8 c, u8 d);
	void setAddress(const IPv6AddressBytes *ipv6_bytes);
	struct sockaddr_in6 getAddress6() const;
	int getFamily() const;
	bool isIPv6() const;
	bool isZero() const;
	void setPort(unsigned short port);
	void print(std::ostream *s) const;
	std::string serializeString() const;
private:
	unsigned int m_addr_family;
	union
	{
		struct sockaddr_in  ipv4;
		struct sockaddr_in6 ipv6;
	} m_address;
	u16 m_port; // Port is separate from sockaddr structures
};

class UDPSocket
{
public:
	UDPSocket() { }
	UDPSocket(bool ipv6);
	~UDPSocket();
	void Bind(Address addr);

	bool init(bool ipv6, bool noExceptions = false);

	//void Close();
	//bool IsOpen();
	void Send(const Address & destination, const void * data, int size);
	// Returns -1 if there is no data
	int Receive(Address & sender, void * data, int size);
	int GetHandle(); // For debugging purposes only
	void setTimeoutMs(int timeout_ms);
	// Returns true if there is data, false if timeout occurred
	bool WaitData(int timeout_ms);
private:
	int m_handle;
	int m_timeout_ms;
	int m_addr_family;
};

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

// Global DummySoundManager singleton
DummySoundManager dummySoundManager;


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

#include <set>
#include <string>
#include "irrlichttypes_bloated.h"

class OnDemandSoundFetcher
{
public:
	virtual void fetchSounds(const std::string &name,
			std::set<std::string> &dst_paths,
			std::set<std::string> &dst_datas) = 0;
};

struct SimpleSoundSpec
{
	SimpleSoundSpec(const std::string &name = "", float gain = 1.0, float fade = 0.0)
	    : name(name), gain(gain), fade(fade)
	{
	}

	bool exists() const { return name != ""; }

	std::string name;
	float gain;
	float fade;
};

class ISoundManager
{
public:
	virtual ~ISoundManager() {}
	// Multiple sounds can be loaded per name; when played, the sound
	// should be chosen randomly from alternatives
	// Return value determines success/failure
	virtual bool loadSoundFile(
			const std::string &name, const std::string &filepath) = 0;
	virtual bool loadSoundData(
			const std::string &name, const std::string &filedata) = 0;

	virtual void updateListener(v3f pos, v3f vel, v3f at, v3f up) = 0;
	virtual void setListenerGain(float gain) = 0;

	// playSound functions return -1 on failure, otherwise a handle to the
	// sound. If name=="", call should be ignored without error.
	virtual int playSound(const std::string &name, bool loop, float volume,
			float fade = 0) = 0;
	virtual int playSoundAt(
			const std::string &name, bool loop, float volume, v3f pos) = 0;
	virtual void stopSound(int sound) = 0;
	virtual bool soundExists(int sound) = 0;
	virtual void updateSoundPosition(int sound, v3f pos) = 0;
	virtual bool updateSoundGain(int id, float gain) = 0;
	virtual float getSoundGain(int id) = 0;
	virtual void step(float dtime) = 0;
	virtual void fadeSound(int sound, float step, float gain) = 0;

	int playSound(const SimpleSoundSpec &spec, bool loop)
	{
		return playSound(spec.name, loop, spec.gain, spec.fade);
	}
	int playSoundAt(const SimpleSoundSpec &spec, bool loop, v3f pos)
	{
		return playSoundAt(spec.name, loop, spec.gain, pos);
	}
};

class DummySoundManager : public ISoundManager
{
public:
	virtual bool loadSoundFile(const std::string &name, const std::string &filepath)
	{
		return true;
	}
	virtual bool loadSoundData(const std::string &name, const std::string &filedata)
	{
		return true;
	}
	void updateListener(v3f pos, v3f vel, v3f at, v3f up) {}
	void setListenerGain(float gain) {}
	int playSound(const std::string &name, bool loop, float volume, float fade)
	{
		return 0;
	}
	int playSoundAt(const std::string &name, bool loop, float volume, v3f pos)
	{
		return 0;
	}
	void stopSound(int sound) {}
	bool soundExists(int sound) { return false; }
	void updateSoundPosition(int sound, v3f pos) {}
	bool updateSoundGain(int id, float gain) { return false; }
	float getSoundGain(int id) { return 0; }
	void step(float dtime) {}
	void fadeSound(int sound, float step, float gain) {}
};

// Global DummySoundManager singleton
extern DummySoundManager dummySoundManager;

#endif


code:
/*
Minetest
Copyright (C) 2013 celeron55, Perttu Ahola <celeron55@gmail.com>
OpenAL support based on work by:
Copyright (C) 2011 Sebastian 'Bahamada' Rühl
Copyright (C) 2011 Cyriaque 'Cisoun' Skrapits <cysoun@gmail.com>
Copyright (C) 2011 Giuseppe Bilotta <giuseppe.bilotta@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; ifnot, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/

#include "sound_openal.h"

#if defined(_WIN32)
	#include <al.h>
	#include <alc.h>
	//#include <alext.h>
#elif defined(__APPLE__)
	#include <OpenAL/al.h>
	#include <OpenAL/alc.h>
	//#include <OpenAL/alext.h>
#else
	#include <AL/al.h>
	#include <AL/alc.h>
	#include <AL/alext.h>
#endif
#include <cmath>
#include <vorbis/vorbisfile.h>
#include <assert.h>
#include "log.h"
#include "util/numeric.h" // myrand()
#include "porting.h"
#include <vector>
#include <fstream>
#include "util/cpp11_container.h"

#define BUFFER_SIZE 30000

static const char *alcErrorString(ALCenum err)
{
	switch (err) {
	case ALC_NO_ERROR:
		return "no error";
	case ALC_INVALID_DEVICE:
		return "invalid device";
	case ALC_INVALID_CONTEXT:
		return "invalid context";
	case ALC_INVALID_ENUM:
		return "invalid enum";
	case ALC_INVALID_VALUE:
		return "invalid value";
	case ALC_OUT_OF_MEMORY:
		return "out of memory";
	default:
		return "<unknown OpenAL error>";
	}
}

static const char *alErrorString(ALenum err)
{
	switch (err) {
	case AL_NO_ERROR:
		return "no error";
	case AL_INVALID_NAME:
		return "invalid name";
	case AL_INVALID_ENUM:
		return "invalid enum";
	case AL_INVALID_VALUE:
		return "invalid value";
	case AL_INVALID_OPERATION:
		return "invalid operation";
	case AL_OUT_OF_MEMORY:
		return "out of memory";
	default:
		return "<unknown OpenAL error>";
	}
}

static ALenum warn_if_error(ALenum err, const char *desc)
{
	if(err == AL_NO_ERROR)
		return err;
	warningstream<<desc<<": "<<alErrorString(err)<<std::endl;
	return err;
}

void f3_set(ALfloat *f3, v3f v)
{
	f3[0] = v.X;
	f3[1] = v.Y;
	f3[2] = v.Z;
}

struct SoundBuffer
{
	ALenum format;
	ALsizei freq;
	ALuint buffer_id;
	std::vector<char> buffer;
};

SoundBuffer *load_opened_ogg_file(OggVorbis_File *oggFile,
		const std::string &filename_for_logging)
{
	int endian = 0; // 0 for Little-Endian, 1 for Big-Endian
	int bitStream;
	long bytes;
	char array[BUFFER_SIZE]; // Local fixed size array
	vorbis_info *pInfo;

	SoundBuffer *snd = new SoundBuffer;

	// Get some information about the OGG file
	pInfo = ov_info(oggFile, -1);

	// Check the number of channels... always use 16-bit samples
	if(pInfo->channels == 1)
		snd->format = AL_FORMAT_MONO16;
	else
		snd->format = AL_FORMAT_STEREO16;

	// The frequency of the sampling rate
	snd->freq = pInfo->rate;

	// Keep reading until all is read
	do
	{
		// Read up to a buffer's worth of decoded sound data
		bytes = ov_read(oggFile, array, BUFFER_SIZE, endian, 2, 1, &bitStream);

		if(bytes < 0)
		{
			ov_clear(oggFile);
			infostream << "Audio: Error decoding "
				<< filename_for_logging << std::endl;
			delete snd;
			return NULL;
		}

		// Append to end of buffer
		snd->buffer.insert(snd->buffer.end(), array, array + bytes);
	} while (bytes > 0);

	alGenBuffers(1, &snd->buffer_id);
	alBufferData(snd->buffer_id, snd->format,
			&(snd->buffer[0]), snd->buffer.size(),
			snd->freq);

	ALenum error = alGetError();

	if(error != AL_NO_ERROR){
		infostream<<"Audio: OpenAL error: "<<alErrorString(error)
				<<"preparing sound buffer"<<std::endl;
	}

	infostream << "Audio file "
		<< filename_for_logging << " loaded" << std::endl;

	// Clean up!
	ov_clear(oggFile);

	return snd;
}

SoundBuffer *load_ogg_from_file(const std::string &path)
{
	OggVorbis_File oggFile;

	// Try opening the given file.
	// This requires libvorbis >= 1.3.2, as
	// previous versions expect a non-const char *
	if (ov_fopen(path.c_str(), &oggFile) != 0) {
		infostream << "Audio: Error opening " << path
			<< " for decoding" << std::endl;
		return NULL;
	}

	return load_opened_ogg_file(&oggFile, path);
}

struct BufferSource {
	const char *buf;
	size_t cur_offset;
	size_t len;
};

size_t buffer_sound_read_func(void *ptr, size_t size, size_t nmemb, void *datasource)
{
	BufferSource *s = (BufferSource *)datasource;
	size_t copied_size = MYMIN(s->len - s->cur_offset, size);
	memcpy(ptr, s->buf + s->cur_offset, copied_size);
	s->cur_offset += copied_size;
	return copied_size;
}

int buffer_sound_seek_func(void *datasource, ogg_int64_t offset, int whence)
{
	BufferSource *s = (BufferSource *)datasource;
	if (whence == SEEK_SET) {
		if (offset < 0 || (size_t)MYMAX(offset, 0) >= s->len) {
			// offset out of bounds
			return -1;
		}
		s->cur_offset = offset;
		return 0;
	} else if (whence == SEEK_CUR) {
		if ((size_t)MYMIN(-offset, 0) > s->cur_offset
				|| s->cur_offset + offset > s->len) {
			// offset out of bounds
			return -1;
		}
		s->cur_offset += offset;
		return 0;
	}
	// invalid whence param (SEEK_END doesn't have to be supported)
	return -1;
}

long BufferSourceell_func(void *datasource)
{
	BufferSource *s = (BufferSource *)datasource;
	return s->cur_offset;
}

static ov_callbacks g_buffer_ov_callbacks = {
	&buffer_sound_read_func,
	&buffer_sound_seek_func,
	NULL,
	&BufferSourceell_func
};

SoundBuffer *load_ogg_from_buffer(const std::string &buf, const std::string &id_for_log)
{
	OggVorbis_File oggFile;

	BufferSource s;
	s.buf = buf.c_str();
	s.cur_offset = 0;
	s.len = buf.size();

	if (ov_open_callbacks(&s, &oggFile, NULL, 0, g_buffer_ov_callbacks) != 0) {
		infostream << "Audio: Error opening " << id_for_log
			<< " for decoding" << std::endl;
		return NULL;
	}

	return load_opened_ogg_file(&oggFile, id_for_log);
}

struct PlayingSound
{
	ALuint source_id;
	bool loop;
};

class OpenALSoundManager: public ISoundManager
{
private:
	OnDemandSoundFetcher *m_fetcher;
	ALCdevice *m_device;
	ALCcontext *m_context;
	int m_next_id;
	UNORDERED_MAP<std::string, std::vector<SoundBuffer*> > m_buffers;
	UNORDERED_MAP<int, PlayingSound*> m_sounds_playing;
	v3f m_listener_pos;
	struct FadeState {
		FadeState() {}
		FadeState(float step, float current_gain, float target_gain):
			step(step),
			current_gain(current_gain),
			target_gain(target_gain) {}
		float step;
		float current_gain;
		float target_gain;
	};

	UNORDERED_MAP<int, FadeState> m_sounds_fading;
	float m_fade_delay;
public:
	bool m_is_initialized;
	OpenALSoundManager(OnDemandSoundFetcher *fetcher):
		m_fetcher(fetcher),
		m_device(NULL),
		m_context(NULL),
		m_next_id(1),
		m_fade_delay(0),
		m_is_initialized(false)
	{
		ALCenum error = ALC_NO_ERROR;

		infostream<<"Audio: Initializing..."<<std::endl;

		m_device = alcOpenDevice(NULL);
		if(!m_device){
			infostream<<"Audio: No audio device available, audio system "
				<<"not initialized"<<std::endl;
			return;
		}

		m_context = alcCreateContext(m_device, NULL);
		if(!m_context){
			error = alcGetError(m_device);
			infostream<<"Audio: Unable to initialize audio context, "
					<<"aborting audio initialization ("<<alcErrorString(error)
					<<")"<<std::endl;
			alcCloseDevice(m_device);
			m_device = NULL;
			return;
		}

		if(!alcMakeContextCurrent(m_context) ||
				(error = alcGetError(m_device) != ALC_NO_ERROR))
		{
			infostream<<"Audio: Error setting audio context, aborting audio "
					<<"initialization ("<<alcErrorString(error)<<")"<<std::endl;
			alcDestroyContext(m_context);
			m_context = NULL;
			alcCloseDevice(m_device);
			m_device = NULL;
			return;
		}

		alDistanceModel(AL_INVERSE_DISTANCE_CLAMPED);

		infostream<<"Audio: Initialized: OpenAL "<<alGetString(AL_VERSION)
				<<", using "<<alcGetString(m_device, ALC_DEVICE_SPECIFIER)
				<<std::endl;

		m_is_initialized = true;
	}

	~OpenALSoundManager()
	{
		infostream<<"Audio: Deinitializing..."<<std::endl;
		// KABOOM!
		// TODO: Clear SoundBuffers
		alcMakeContextCurrent(NULL);
		alcDestroyContext(m_context);
		m_context = NULL;
		alcCloseDevice(m_device);
		m_device = NULL;

		for (UNORDERED_MAP<std::string, std::vector<SoundBuffer*> >::iterator i = m_buffers.begin();
				i != m_buffers.end(); ++i) {
			for (std::vector<SoundBuffer*>::iterator iter = (*i).second.begin();
					iter != (*i).second.end(); ++iter) {
				delete *iter;
			}
			(*i).second.clear();
		}
		m_buffers.clear();
		infostream<<"Audio: Deinitialized."<<std::endl;
	}

	void step(float dtime)
	{
		doFades(dtime);
	}

	void addBuffer(const std::string &name, SoundBuffer *buf)
	{
		UNORDERED_MAP<std::string, std::vector<SoundBuffer*> >::iterator i =
				m_buffers.find(name);
		if(i != m_buffers.end()){
			i->second.push_back(buf);
			return;
		}
		std::vector<SoundBuffer*> bufs;
		bufs.push_back(buf);
		m_buffers[name] = bufs;
		return;
	}

	SoundBuffer* getBuffer(const std::string &name)
	{
		UNORDERED_MAP<std::string, std::vector<SoundBuffer*> >::iterator i =
				m_buffers.find(name);
		if(i == m_buffers.end())
			return NULL;
		std::vector<SoundBuffer*> &bufs = i->second;
		int j = myrand() % bufs.size();
		return bufs[j];
	}

	PlayingSound* createPlayingSound(SoundBuffer *buf, bool loop,
			float volume)
	{
		infostream<<"OpenALSoundManager: Creating playing sound"<<std::endl;
		assert(buf);
		PlayingSound *sound = new PlayingSound;
		assert(sound);
		warn_if_error(alGetError(), "before createPlayingSound");
		alGenSources(1, &sound->source_id);
		alSourcei(sound->source_id, AL_BUFFER, buf->buffer_id);
		alSourcei(sound->source_id, AL_SOURCE_RELATIVE, true);
		alSource3f(sound->source_id, AL_POSITION, 0, 0, 0);
		alSource3f(sound->source_id, AL_VELOCITY, 0, 0, 0);
		alSourcei(sound->source_id, AL_LOOPING, loop ? AL_TRUE : AL_FALSE);
		volume = std::fmax(0.0f, volume);
		alSourcef(sound->source_id, AL_GAIN, volume);
		alSourcePlay(sound->source_id);
		warn_if_error(alGetError(), "createPlayingSound");
		return sound;
	}

	PlayingSound* createPlayingSoundAt(SoundBuffer *buf, bool loop,
			float volume, v3f pos)
	{
		infostream<<"OpenALSoundManager: Creating positional playing sound"
				<<std::endl;
		assert(buf);
		PlayingSound *sound = new PlayingSound;
		assert(sound);
		warn_if_error(alGetError(), "before createPlayingSoundAt");
		alGenSources(1, &sound->source_id);
		alSourcei(sound->source_id, AL_BUFFER, buf->buffer_id);
		alSourcei(sound->source_id, AL_SOURCE_RELATIVE, false);
		alSource3f(sound->source_id, AL_POSITION, pos.X, pos.Y, pos.Z);
		alSource3f(sound->source_id, AL_VELOCITY, 0, 0, 0);
		// Use alDistanceModel(AL_INVERSE_DISTANCE_CLAMPED) and set reference
		// distance to clamp gain at <1 node distance, to avoid excessive
		// volume when closer
		alSourcef(sound->source_id, AL_REFERENCE_DISTANCE, 10.0f);
		alSourcei(sound->source_id, AL_LOOPING, loop ? AL_TRUE : AL_FALSE);
		// Multiply by 3 to compensate for reducing AL_REFERENCE_DISTANCE from
		// the previous value of 30 to the new value of 10
		volume = std::fmax(0.0f, volume * 3.0f);
		alSourcef(sound->source_id, AL_GAIN, volume);
		alSourcePlay(sound->source_id);
		warn_if_error(alGetError(), "createPlayingSoundAt");
		return sound;
	}

	int playSoundRaw(SoundBuffer *buf, bool loop, float volume)
	{
		assert(buf);
		PlayingSound *sound = createPlayingSound(buf, loop, volume);
		if(!sound)
			return -1;
		int id = m_next_id++;
		m_sounds_playing[id] = sound;
		return id;
	}

	int playSoundRawAt(SoundBuffer *buf, bool loop, float volume, v3f pos)
	{
		assert(buf);
		PlayingSound *sound = createPlayingSoundAt(buf, loop, volume, pos);
		if(!sound)
			return -1;
		int id = m_next_id++;
		m_sounds_playing[id] = sound;
		return id;
	}

	void deleteSound(int id)
	{
		UNORDERED_MAP<int, PlayingSound*>::iterator i = m_sounds_playing.find(id);
		if(i == m_sounds_playing.end())
			return;
		PlayingSound *sound = i->second;

		alDeleteSources(1, &sound->source_id);

		delete sound;
		m_sounds_playing.erase(id);
	}

	/* If buffer does not exist, consult the fetcher */
	SoundBuffer* getFetchBuffer(const std::string &name)
	{
		SoundBuffer *buf = getBuffer(name);
		if(buf)
			return buf;
		if(!m_fetcher)
			return NULL;
		std::set<std::string> paths;
		std::set<std::string> datas;
		m_fetcher->fetchSounds(name, paths, datas);
		for(std::set<std::string>::iterator i = paths.begin();
				i != paths.end(); ++i){
			loadSoundFile(name, *i);
		}
		for(std::set<std::string>::iterator i = datas.begin();
				i != datas.end(); ++i){
			loadSoundData(name, *i);
		}
		return getBuffer(name);
	}

	// Remove stopped sounds
	void maintain()
	{
		verbosestream<<"OpenALSoundManager::maintain(): "
				<<m_sounds_playing.size()<<" playing sounds, "
				<<m_buffers.size()<<" sound names loaded"<<std::endl;
		std::set<int> del_list;
		for(UNORDERED_MAP<int, PlayingSound*>::iterator i = m_sounds_playing.begin();
				i != m_sounds_playing.end(); ++i) {
			int id = i->first;
			PlayingSound *sound = i->second;
			// If not playing, remove it
			{
				ALint state;
				alGetSourcei(sound->source_id, AL_SOURCE_STATE, &state);
				if(state != AL_PLAYING){
					del_list.insert(id);
				}
			}
		}
		if(!del_list.empty())
			verbosestream<<"OpenALSoundManager::maintain(): deleting "
					<<del_list.size()<<" playing sounds"<<std::endl;
		for(std::set<int>::iterator i = del_list.begin();
				i != del_list.end(); ++i)
		{
			deleteSound(*i);
		}
	}

	/* Interface */

	bool loadSoundFile(const std::string &name,
			const std::string &filepath)
	{
		SoundBuffer *buf = load_ogg_from_file(filepath);
		if (buf)
			addBuffer(name, buf);
		return false;
	}

	bool loadSoundData(const std::string &name,
			const std::string &filedata)
	{
		SoundBuffer *buf = load_ogg_from_buffer(filedata, name);
		if (buf)
			addBuffer(name, buf);
		return false;
	}

	void updateListener(v3f pos, v3f vel, v3f at, v3f up)
	{
		m_listener_pos = pos;
		alListener3f(AL_POSITION, pos.X, pos.Y, pos.Z);
		alListener3f(AL_VELOCITY, vel.X, vel.Y, vel.Z);
		ALfloat f[6];
		f3_set(f, at);
		f3_set(f+3, -up);
		alListenerfv(AL_ORIENTATION, f);
		warn_if_error(alGetError(), "updateListener");
	}

	void setListenerGain(float gain)
	{
		alListenerf(AL_GAIN, gain);
	}

	int playSound(const std::string &name, bool loop, float volume, float fade)
	{
		maintain();
		if(name == "")
			return 0;
		SoundBuffer *buf = getFetchBuffer(name);
		if(!buf){
			infostream<<"OpenALSoundManager: \""<<name<<"\" not found."
					<<std::endl;
			return -1;
		}
		int handle = -1;
		if (fade > 0) {
			handle = playSoundRaw(buf, loop, 0);
			fadeSound(handle, fade, volume);
		} else {
			handle = playSoundRaw(buf, loop, volume);
		}
		return handle;
	}

	int playSoundAt(const std::string &name, bool loop, float volume, v3f pos)
	{
		maintain();
		if(name == "")
			return 0;
		SoundBuffer *buf = getFetchBuffer(name);
		if(!buf){
			infostream<<"OpenALSoundManager: \""<<name<<"\" not found."
					<<std::endl;
			return -1;
		}
		return playSoundRawAt(buf, loop, volume, pos);
	}

	void stopSound(int sound)
	{
		maintain();
		deleteSound(sound);
	}

	void fadeSound(int soundid, float step, float gain)
	{
		m_sounds_fading[soundid] = FadeState(step, getSoundGain(soundid), gain);
	}

	void doFades(float dtime)
	{
		m_fade_delay += dtime;

		if (m_fade_delay < 0.1f)
			return;

		float chkGain = 0;
		for (UNORDERED_MAP<int, FadeState>::iterator i = m_sounds_fading.begin();
				i != m_sounds_fading.end();) {
			if (i->second.step < 0.f)
				chkGain = -(i->second.current_gain);
			else
				chkGain = i->second.current_gain;

			if (chkGain < i->second.target_gain) {
				i->second.current_gain += (i->second.step * m_fade_delay);
				i->second.current_gain = rangelim(i->second.current_gain, 0, 1);

				updateSoundGain(i->first, i->second.current_gain);
				++i;
			} else {
				if (i->second.target_gain <= 0.f)
					stopSound(i->first);

				m_sounds_fading.erase(i++);
			}
		}
		m_fade_delay = 0;
	}

	bool soundExists(int sound)
	{
		maintain();
		return (m_sounds_playing.count(sound) != 0);
	}

	void updateSoundPosition(int id, v3f pos)
	{
		UNORDERED_MAP<int, PlayingSound*>::iterator i = m_sounds_playing.find(id);
		if (i == m_sounds_playing.end())
			return;
		PlayingSound *sound = i->second;

		alSourcei(sound->source_id, AL_SOURCE_RELATIVE, false);
		alSource3f(sound->source_id, AL_POSITION, pos.X, pos.Y, pos.Z);
		alSource3f(sound->source_id, AL_VELOCITY, 0, 0, 0);
		alSourcef(sound->source_id, AL_REFERENCE_DISTANCE, 30.0);
	}

	bool updateSoundGain(int id, float gain)
	{
		UNORDERED_MAP<int, PlayingSound*>::iterator i = m_sounds_playing.find(id);
		if (i == m_sounds_playing.end())
			return false;

		PlayingSound *sound = i->second;
		alSourcef(sound->source_id, AL_GAIN, gain);
		return true;
	}

	float getSoundGain(int id)
	{
		UNORDERED_MAP<int, PlayingSound*>::iterator i = m_sounds_playing.find(id);
		if (i == m_sounds_playing.end())
			return 0;

		PlayingSound *sound = i->second;
		ALfloat gain;
		alGetSourcef(sound->source_id, AL_GAIN, &gain);
		return gain;
	}
};

ISoundManager *createOpenALSoundManager(OnDemandSoundFetcher *fetcher)
{
	OpenALSoundManager *m = new OpenALSoundManager(fetcher);
	if(m->m_is_initialized)
		return m;
	delete m;
	return NULL;
};


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

#include "sound.h"

ISoundManager *createOpenALSoundManager(OnDemandSoundFetcher *fetcher);

#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 "staticobject.h"
#include "util/serialize.h"
#include "log.h"

void StaticObject::serialize(std::ostream &os)
{
	// type
	writeU8(os, type);
	// pos
	writeV3F1000(os, pos);
	// data
	os<<serializeString(data);
}
void StaticObject::deSerialize(std::istream &is, u8 version)
{
	// type
	type = readU8(is);
	// pos
	pos = readV3F1000(is);
	// data
	data = deSerializeString(is);
}

void StaticObjectList::serialize(std::ostream &os)
{
	// version
	u8 version = 0;
	writeU8(os, version);

	// count
	size_t count = m_stored.size() + m_active.size();
	// Make sure it fits into u16, else it would get truncated and cause e.g.
	// issue #2610 (Invalid block data in database: unsupported NameIdMapping version).
	if (count > U16_MAX) {
		errorstream << "StaticObjectList::serialize(): "
			<< "too many objects (" << count << ") in list, "
			<< "not writing them to disk." << std::endl;
		writeU16(os, 0);  // count = 0
		return;
	}
	writeU16(os, count);

	for(std::vector<StaticObject>::iterator
			i = m_stored.begin();
			i != m_stored.end(); ++i) {
		StaticObject &s_obj = *i;
		s_obj.serialize(os);
	}
	for(std::map<u16, StaticObject>::iterator
			i = m_active.begin();
			i != m_active.end(); ++i)
	{
		StaticObject s_obj = i->second;
		s_obj.serialize(os);
	}
}
void StaticObjectList::deSerialize(std::istream &is)
{
	// version
	u8 version = readU8(is);
	// count
	u16 count = readU16(is);
	for(u16 i = 0; i < count; i++) {
		StaticObject s_obj;
		s_obj.deSerialize(is, version);
		m_stored.push_back(s_obj);
	}
}


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

#include "irrlichttypes_bloated.h"
#include <string>
#include <sstream>
#include <vector>
#include <map>
#include "debug.h"

struct StaticObject
{
	u8 type;
	v3f pos;
	std::string data;

	StaticObject():
		type(0),
		pos(0,0,0)
	{
	}
	StaticObject(u8 type_, v3f pos_, const std::string &data_):
		type(type_),
		pos(pos_),
		data(data_)
	{
	}

	void serialize(std::ostream &os);
	void deSerialize(std::istream &is, u8 version);
};

class StaticObjectList
{
public:
	/*
		Inserts an object to the container.
		Id must be unique (active) or 0 (stored).
	*/
	void insert(u16 id, StaticObject obj)
	{
		if(id == 0)
		{
			m_stored.push_back(obj);
		}
		else
		{
			if(m_active.find(id) != m_active.end())
			{
				dstream<<"ERROR: StaticObjectList::insert(): "
						<<"id already exists"<<std::endl;
				FATAL_ERROR("StaticObjectList::insert()");
			}
			m_active[id] = obj;
		}
	}

	void remove(u16 id)
	{
		assert(id != 0); // Pre-condition
		if(m_active.find(id) == m_active.end())
		{
			warningstream<<"StaticObjectList::remove(): id="<<id
					<<" not found"<<std::endl;
			return;
		}
		m_active.erase(id);
	}

	void serialize(std::ostream &os);
	void deSerialize(std::istream &is);

	/*
		NOTE: When an object is transformed to active, it is removed
		from m_stored and inserted to m_active.
		The caller directly manipulates these containers.
	*/
	std::vector<StaticObject> m_stored;
	std::map<u16, StaticObject> m_active;

private:
};

#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 "subgame.h"
#include "porting.h"
#include "filesys.h"
#include "settings.h"
#include "log.h"
#include "util/strfnd.h"
#include "defaultsettings.h"  // for override_default_settings
#include "mapgen.h"  // for MapgenParams
#include "util/string.h"

#ifndef SERVER
	#include "client/tile.h" // getImagePath
#endif

bool getGameMinetestConfig(const std::string &game_path, Settings &conf)
{
	std::string conf_path = game_path + DIR_DELIM + "minetest.conf";
	return conf.readConfigFile(conf_path.c_str());
}

bool getGameConfig(const std::string &game_path, Settings &conf)
{
	std::string conf_path = game_path + DIR_DELIM + "game.conf";
	return conf.readConfigFile(conf_path.c_str());
}

std::string getGameName(const std::string &game_path)
{
	Settings conf;
	if(!getGameConfig(game_path, conf))
		return "";
	if(!conf.exists("name"))
		return "";
	return conf.get("name");
}

struct GameFindPath
{
	std::string path;
	bool user_specific;
	GameFindPath(const std::string &path, bool user_specific):
		path(path),
		user_specific(user_specific)
	{}
};

std::string getSubgamePathEnv()
{
	char *subgame_path = getenv("MINETEST_SUBGAME_PATH");
	return subgame_path ? std::string(subgame_path) : "";
}

SubgameSpec findSubgame(const std::string &id)
{
	if(id == "")
		return SubgameSpec();
	std::string share = porting::path_share;
	std::string user = porting::path_user;
	std::vector<GameFindPath> find_paths;

	Strfnd search_paths(getSubgamePathEnv());

	while (!search_paths.at_end()) {
		std::string path = search_paths.next(PATH_DELIM);
		find_paths.push_back(GameFindPath(
				path + DIR_DELIM + id, false));
		find_paths.push_back(GameFindPath(
				path + DIR_DELIM + id + "_game", false));
	}

	find_paths.push_back(GameFindPath(
			user + DIR_DELIM + "games" + DIR_DELIM + id + "_game", true));
	find_paths.push_back(GameFindPath(
			user + DIR_DELIM + "games" + DIR_DELIM + id, true));
	find_paths.push_back(GameFindPath(
			share + DIR_DELIM + "games" + DIR_DELIM + id + "_game", false));
	find_paths.push_back(GameFindPath(
			share + DIR_DELIM + "games" + DIR_DELIM + id, false));
	// Find game directory
	std::string game_path;
	bool user_game = true; // Game is in user's directory
	for(u32 i=0; i<find_paths.size(); i++){
		const std::string &try_path = find_paths[i].path;
		if(fs::PathExists(try_path)){
			game_path = try_path;
			user_game = find_paths[i].user_specific;
			break;
		}
	}
	if(game_path == "")
		return SubgameSpec();
	std::string gamemod_path = game_path + DIR_DELIM + "mods";
	// Find mod directories
	std::set<std::string> mods_paths;
	if(!user_game)
		mods_paths.insert(share + DIR_DELIM + "mods");
	if(user != share || user_game)
		mods_paths.insert(user + DIR_DELIM + "mods");
	std::string game_name = getGameName(game_path);
	if(game_name == "")
		game_name = id;
	std::string menuicon_path;
#ifndef SERVER
	menuicon_path = getImagePath(game_path + DIR_DELIM + "menu" + DIR_DELIM + "icon.png");
#endif
	return SubgameSpec(id, game_path, gamemod_path, mods_paths, game_name,
			menuicon_path);
}

SubgameSpec findWorldSubgame(const std::string &world_path)
{
	std::string world_gameid = getWorldGameId(world_path, true);
	// See if world contains an embedded game; if so, use it.
	std::string world_gamepath = world_path + DIR_DELIM + "game";
	if(fs::PathExists(world_gamepath)){
		SubgameSpec gamespec;
		gamespec.id = world_gameid;
		gamespec.path = world_gamepath;
		gamespec.gamemods_path= world_gamepath + DIR_DELIM + "mods";
		gamespec.name = getGameName(world_gamepath);
		if(gamespec.name == "")
			gamespec.name = "unknown";
		return gamespec;
	}
	return findSubgame(world_gameid);
}

std::set<std::string> getAvailableGameIds()
{
	std::set<std::string> gameids;
	std::set<std::string> gamespaths;
	gamespaths.insert(porting::path_share + DIR_DELIM + "games");
	gamespaths.insert(porting::path_user + DIR_DELIM + "games");

	Strfnd search_paths(getSubgamePathEnv());

	while (!search_paths.at_end())
		gamespaths.insert(search_paths.next(PATH_DELIM));

	for (std::set<std::string>::const_iterator i = gamespaths.begin();
			i != gamespaths.end(); ++i){
		std::vector<fs::DirListNode> dirlist = fs::GetDirListing(*i);
		for(u32 j=0; j<dirlist.size(); j++){
			if(!dirlist[j].dir)
				continue;
			// If configuration file is not found or broken, ignore game
			Settings conf;
			if(!getGameConfig(*i + DIR_DELIM + dirlist[j].name, conf))
				continue;
			// Add it to result
			const char *ends[] = {"_game", NULL};
			std::string shorter = removeStringEnd(dirlist[j].name, ends);
			if(shorter != "")
				gameids.insert(shorter);
			else
				gameids.insert(dirlist[j].name);
		}
	}
	return gameids;
}

std::vector<SubgameSpec> getAvailableGames()
{
	std::vector<SubgameSpec> specs;
	std::set<std::string> gameids = getAvailableGameIds();
	for(std::set<std::string>::const_iterator i = gameids.begin();
			i != gameids.end(); ++i)
		specs.push_back(findSubgame(*i));
	return specs;
}

#define LEGACY_GAMEID "minetest"

bool getWorldExists(const std::string &world_path)
{
	return (fs::PathExists(world_path + DIR_DELIM + "map_meta.txt") ||
			fs::PathExists(world_path + DIR_DELIM + "world.mt"));
}

std::string getWorldGameId(const std::string &world_path, bool can_be_legacy)
{
	std::string conf_path = world_path + DIR_DELIM + "world.mt";
	Settings conf;
	bool succeeded = conf.readConfigFile(conf_path.c_str());
	if(!succeeded){
		if(can_be_legacy){
			// If map_meta.txt exists, it is probably an old minetest world
			if(fs::PathExists(world_path + DIR_DELIM + "map_meta.txt"))
				return LEGACY_GAMEID;
		}
		return "";
	}
	if(!conf.exists("gameid"))
		return "";
	// The "mesetint" gameid has been discarded
	if(conf.get("gameid") == "mesetint")
		return "minetest";
	return conf.get("gameid");
}

std::string getWorldPathEnv()
{
	char *world_path = getenv("MINETEST_WORLD_PATH");
	return world_path ? std::string(world_path) : "";
}

std::vector<WorldSpec> getAvailableWorlds()
{
	std::vector<WorldSpec> worlds;
	std::set<std::string> worldspaths;

	Strfnd search_paths(getWorldPathEnv());

	while (!search_paths.at_end())
		worldspaths.insert(search_paths.next(PATH_DELIM));

	worldspaths.insert(porting::path_user + DIR_DELIM + "worlds");
	infostream << "Searching worlds..." << std::endl;
	for (std::set<std::string>::const_iterator i = worldspaths.begin();
			i != worldspaths.end(); ++i) {
		infostream << "  In " << (*i) << ": " <<std::endl;
		std::vector<fs::DirListNode> dirvector = fs::GetDirListing(*i);
		for(u32 j=0; j<dirvector.size(); j++){
			if(!dirvector[j].dir)
				continue;
			std::string fullpath = *i + DIR_DELIM + dirvector[j].name;
			std::string name = dirvector[j].name;
			// Just allow filling in the gameid always for now
			bool can_be_legacy = true;
			std::string gameid = getWorldGameId(fullpath, can_be_legacy);
			WorldSpec spec(fullpath, name, gameid);
			if(!spec.isValid()){
				infostream<<"(invalid: "<<name<<") ";
			} else {
				infostream<<name<<" ";
				worlds.push_back(spec);
			}
		}
		infostream<<std::endl;
	}
	// Check old world location
	do{
		std::string fullpath = porting::path_user + DIR_DELIM + "world";
		if(!fs::PathExists(fullpath))
			break;
		std::string name = "Old World";
		std::string gameid = getWorldGameId(fullpath, true);
		WorldSpec spec(fullpath, name, gameid);
		infostream<<"Old world found."<<std::endl;
		worlds.push_back(spec);
	}while(0);
	infostream<<worlds.size()<<" found."<<std::endl;
	return worlds;
}

bool loadGameConfAndInitWorld(const std::string &path, const SubgameSpec &gamespec)
{
	// Override defaults with those provided by the game.
	// We clear and reload the defaults because the defaults
	// might have been overridden by other subgame config
	// files that were loaded before.
	g_settings->clearDefaults();
	set_default_settings(g_settings);
	Settings game_defaults;
	getGameMinetestConfig(gamespec.path, game_defaults);
	override_default_settings(g_settings, &game_defaults);

	infostream << "Initializing world at " << path << std::endl;

	fs::CreateAllDirs(path);

	// Create world.mt if does not already exist
	std::string worldmt_path = path + DIR_DELIM "world.mt";
	if (!fs::PathExists(worldmt_path)) {
		std::ostringstream ss(std::ios_base::binary);
		ss << "gameid = " << gamespec.id
			<< "\nbackend = sqlite3"
			<< "\ncreative_mode = " << g_settings->get("creative_mode")
			<< "\nenable_damage = " << g_settings->get("enable_damage")
			<< "\n";
		if (!fs::safeWriteToFile(worldmt_path, ss.str()))
			return false;

		infostream << "Wrote world.mt (" << worldmt_path << ")" << std::endl;
	}

	// Create map_meta.txt if does not already exist
	std::string map_meta_path = path + DIR_DELIM + "map_meta.txt";
	if (!fs::PathExists(map_meta_path)){
		verbosestream << "Creating map_meta.txt (" << map_meta_path << ")" << std::endl;
		fs::CreateAllDirs(path);
		std::ostringstream oss(std::ios_base::binary);

		Settings conf;
		MapgenParams params;

		params.readParams(g_settings);
		params.writeParams(&conf);
		conf.writeLines(oss);
		oss << "[end_of_params]\n";

		fs::safeWriteToFile(map_meta_path, oss.str());
	}
	return true;
}


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

#include <string>
#include <set>
#include <vector>

class Settings;

#define WORLDNAME_BLACKLISTED_CHARS "/\\"

struct SubgameSpec
{
	std::string id; // "" = game does not exist
	std::string path; // path to game
	std::string gamemods_path; //path to mods of the game
	std::set<std::string> addon_mods_paths; //paths to addon mods for this game
	std::string name;
	std::string menuicon_path;

	SubgameSpec(const std::string &id_="",
			const std::string &path_="",
			const std::string &gamemods_path_="",
			const std::set<std::string> &addon_mods_paths_=std::set<std::string>(),
			const std::string &name_="",
			const std::string &menuicon_path_=""):
		id(id_),
		path(path_),
		gamemods_path(gamemods_path_),
		addon_mods_paths(addon_mods_paths_),
		name(name_),
		menuicon_path(menuicon_path_)
	{}

	bool isValid() const
	{
		return (id != "" && path != "");
	}
};

// minetest.conf
bool getGameMinetestConfig(const std::string &game_path, Settings &conf);
// game.conf
bool getGameConfig(const std::string &game_path, Settings &conf);

std::string getGameName(const std::string &game_path);

SubgameSpec findSubgame(const std::string &id);
SubgameSpec findWorldSubgame(const std::string &world_path);

std::set<std::string> getAvailableGameIds();
std::vector<SubgameSpec> getAvailableGames();

bool getWorldExists(const std::string &world_path);
std::string getWorldGameId(const std::string &world_path,
		bool can_be_legacy=false);

struct WorldSpec
{
	std::string path;
	std::string name;
	std::string gameid;

	WorldSpec(
		const std::string &path_="",
		const std::string &name_="",
		const std::string &gameid_=""
	):
		path(path_),
		name(name_),
		gameid(gameid_)
	{}

	bool isValid() const
	{
		return (name != "" && path != "" && gameid != "");
	}
};

std::vector<WorldSpec> getAvailableWorlds();

// loads the subgame's config and creates world directory
// and world.mt if they don't exist
bool loadGameConfAndInitWorld(const std::string &path, const SubgameSpec &gamespec);

#endif