aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorsapier <Sapier at GMX dot net>2013-07-07 15:53:40 -0400
committerkwolekr <kwolekr@minetest.net>2013-07-07 19:49:52 -0400
commit88d43af276a4ed85500863b427bf370d8eca2095 (patch)
treed02fe1196639c48ad14bc4cfc6c5d621015488bf /src
parent7e73b7cae9faf203e369dc5cf2b85cff6e6efbad (diff)
Fix many formspec menu bugs
Diffstat (limited to 'src')
-rw-r--r--src/defaultsettings.cpp2
-rw-r--r--src/environment.cpp2
-rw-r--r--src/guiEngine.cpp2
-rw-r--r--src/guiFormSpecMenu.cpp132
-rw-r--r--src/guiFormSpecMenu.h11
-rw-r--r--src/itemdef.cpp2
-rw-r--r--src/mapblock_mesh.cpp3
-rw-r--r--src/shader.cpp12
8 files changed, 117 insertions, 49 deletions
diff --git a/src/defaultsettings.cpp b/src/defaultsettings.cpp
index 101eb35a4..b6ee5e71c 100644
--- a/src/defaultsettings.cpp
+++ b/src/defaultsettings.cpp
@@ -126,8 +126,8 @@ void set_default_settings(Settings *settings)
settings->setDefault("bilinear_filter", "false");
settings->setDefault("trilinear_filter", "false");
settings->setDefault("preload_item_visuals", "true");
- settings->setDefault("enable_shaders", "2");
settings->setDefault("enable_bumpmapping", "false");
+ settings->setDefault("enable_shaders", "true");
settings->setDefault("repeat_rightclick_time", "0.25");
settings->setDefault("enable_particles", "true");
diff --git a/src/environment.cpp b/src/environment.cpp
index 21a6258b7..cd8783984 100644
--- a/src/environment.cpp
+++ b/src/environment.cpp
@@ -204,7 +204,7 @@ void Environment::printPlayers(std::ostream &o)
u32 Environment::getDayNightRatio()
{
- bool smooth = (g_settings->getS32("enable_shaders") != 0);
+ bool smooth = g_settings->getBool("enable_shaders");
return time_to_daynight_ratio(m_time_of_day_f*24000, smooth);
}
diff --git a/src/guiEngine.cpp b/src/guiEngine.cpp
index 2c5000d8b..f3b3a6845 100644
--- a/src/guiEngine.cpp
+++ b/src/guiEngine.cpp
@@ -182,7 +182,7 @@ GUIEngine::GUIEngine( irr::IrrlichtDevice* dev,
errorstream
<< "GUIEngine::GUIEngine unable to load builtin menu"
<< std::endl;
- return;
+ assert("no future without mainmenu" == 0);
}
}
diff --git a/src/guiFormSpecMenu.cpp b/src/guiFormSpecMenu.cpp
index 994730619..abe519798 100644
--- a/src/guiFormSpecMenu.cpp
+++ b/src/guiFormSpecMenu.cpp
@@ -171,6 +171,10 @@ GUIFormSpecMenu::GUIFormSpecMenu(irr::IrrlichtDevice* dev,
m_use_gettext(false),
m_lock(false)
{
+ current_keys_pending.key_down = false;
+ current_keys_pending.key_up = false;
+ current_keys_pending.key_enter = false;
+
}
GUIFormSpecMenu::~GUIFormSpecMenu()
@@ -243,28 +247,25 @@ std::vector<std::string> split(const std::string &s, char delim, bool escape=fal
else {
std::string current = "";
current += s.c_str()[0];
- bool last_was_delim = false;
+ bool last_was_escape = false;
for(unsigned int i=1; i < s.size(); i++) {
- if (last_was_delim) {
+ if (last_was_escape) {
+ current += '\\';
+ current += s.c_str()[i];
+ last_was_escape = false;
+ }
+ else {
if (s.c_str()[i] == delim) {
- current += s.c_str()[i];
- last_was_delim = false;
- }
- else {
tokens.push_back(current);
-
current = "";
- current += s.c_str()[i];
- last_was_delim = false;
+ last_was_escape = false;
}
- }
- else {
- if (s.c_str()[i] == delim) {
- last_was_delim = true;
+ else if (s.c_str()[i] == '\\'){
+ last_was_escape = true;
}
else {
- last_was_delim = false;
current += s.c_str()[i];
+ last_was_escape = false;
}
}
}
@@ -659,32 +660,34 @@ void GUIFormSpecMenu::parseTextList(parserData* data,std::string element) {
for (unsigned int i=0; i < items.size(); i++) {
if (items[i].c_str()[0] == '#') {
if (items[i].c_str()[1] == '#') {
- e->addItem(narrow_to_wide(items[i]).c_str() +1);
+ e->addItem(narrow_to_wide(unescape_string(items[i])).c_str() +1);
}
else {
- std::wstring toadd = narrow_to_wide(items[i].c_str() + 7);
std::string color = items[i].substr(1,6);
+ std::wstring toadd =
+ narrow_to_wide(unescape_string(items[i]).c_str() + 7);
+
e->addItem(toadd.c_str());
irr::video::SColor toset;
- if (parseColor(color,toset))
+ if (parseColor(color, toset))
e->setItemOverrideColor(i,toset);
}
}
else {
- e->addItem(narrow_to_wide(items[i]).c_str());
+ e->addItem(narrow_to_wide(unescape_string(items[i])).c_str());
}
}
- if (str_initial_selection != "")
- e->setSelected(stoi(str_initial_selection.c_str())-1);
-
if (data->listbox_selections.find(fname_w) != data->listbox_selections.end()) {
e->setSelected(data->listbox_selections[fname_w]);
}
+ if (str_initial_selection != "")
+ e->setSelected(stoi(str_initial_selection.c_str())-1);
+
m_listboxes.push_back(std::pair<FieldSpec,gui::IGUIListBox*>(spec,e));
m_fields.push_back(spec);
return;
@@ -1335,7 +1338,7 @@ void GUIFormSpecMenu::parseBox(parserData* data,std::string element) {
irr::video::SColor color;
- if (parseColor(color_str,color)) {
+ if (parseColor(color_str, color)) {
BoxDrawSpec spec(pos,geom,color);
m_boxes.push_back(spec);
@@ -1355,8 +1358,19 @@ void GUIFormSpecMenu::parseElement(parserData* data,std::string element) {
std::vector<std::string> parts = split(element,'[', true);
- if (parts.size() != 2)
+ // ugly workaround to keep compatibility
+ if (parts.size() > 2) {
+ if (trim(parts[0]) == "image") {
+ for (unsigned int i=2;i< parts.size(); i++) {
+ parts[1] += "[" + parts[i];
+ }
+ }
+ else { return; }
+ }
+
+ if (parts.size() < 2) {
return;
+ }
std::string type = trim(parts[0]);
std::string description = trim(parts[1]);
@@ -2010,6 +2024,26 @@ void GUIFormSpecMenu::acceptInput(int eventtype)
{
std::map<std::string, std::string> fields;
+ if (current_keys_pending.key_down) {
+ fields["key_down"] = "true";
+ current_keys_pending.key_down = false;
+ }
+
+ if (current_keys_pending.key_up) {
+ fields["key_up"] = "true";
+ current_keys_pending.key_up = false;
+ }
+
+ if (current_keys_pending.key_enter) {
+ fields["key_enter"] = "true";
+ current_keys_pending.key_enter = false;
+ }
+
+ if (current_keys_pending.key_escape) {
+ fields["key_escape"] = "true";
+ current_keys_pending.key_escape = false;
+ }
+
for(u32 i=0; i<m_fields.size(); i++)
{
const FieldSpec &s = m_fields[i];
@@ -2101,16 +2135,38 @@ bool GUIFormSpecMenu::OnEvent(const SEvent& event)
m_text_dst->gotText(narrow_to_wide("MenuQuit"));
return true;
}
- if(event.KeyInput.Key==KEY_RETURN && event.KeyInput.PressedDown)
- {
+ if (event.KeyInput.PressedDown &&
+ (event.KeyInput.Key==KEY_RETURN ||
+ event.KeyInput.Key==KEY_UP ||
+ event.KeyInput.Key==KEY_DOWN)
+ ) {
+
+
+ switch (event.KeyInput.Key) {
+ case KEY_RETURN:
+ if (m_allowclose) {
+ acceptInput();
+ quitMenu();
+ }
+ else
+ current_keys_pending.key_enter = true;
+ break;
+ case KEY_UP:
+ current_keys_pending.key_up = true;
+ break;
+ case KEY_DOWN:
+ current_keys_pending.key_down = true;
+ break;
+ break;
+ default:
+ //can't happen at all!
+ assert("reached a source line that can't ever been reached" == 0);
+ break;
+ }
acceptInput();
-
- if (m_allowclose)
- quitMenu();
- else
- m_text_dst->gotText(narrow_to_wide("KeyEnter"));
return true;
}
+
}
if(event.EventType==EET_MOUSE_INPUT_EVENT
&& event.MouseInput.Event != EMIE_MOUSE_MOVED)
@@ -2477,11 +2533,15 @@ bool GUIFormSpecMenu::OnEvent(const SEvent& event)
{
if(event.GUIEvent.Caller->getID() > 257)
{
- acceptInput();
- if (m_allowclose)
+
+ if (m_allowclose) {
+ acceptInput();
quitMenu();
- else
- m_text_dst->gotText(narrow_to_wide("EditBoxEnter"));
+ }
+ else {
+ current_keys_pending.key_enter = true;
+ acceptInput();
+ }
// quitMenu deallocates menu
return true;
}
@@ -2519,15 +2579,15 @@ bool GUIFormSpecMenu::OnEvent(const SEvent& event)
bool GUIFormSpecMenu::parseColor(std::string color, irr::video::SColor& outcolor) {
outcolor = irr::video::SColor(0,0,0,0);
- if(!string_allowed(color, "0123456789abcdefABCDEF"))
+ if (!string_allowed(color, "0123456789abcdefABCDEF"))
return false;
u32 color_value;
std::istringstream iss(color);
iss >> std::hex >> color_value;
+
outcolor = irr::video::SColor(color_value);
outcolor.setAlpha(255);
return true;
}
-
diff --git a/src/guiFormSpecMenu.h b/src/guiFormSpecMenu.h
index 60d170fda..116f7b95d 100644
--- a/src/guiFormSpecMenu.h
+++ b/src/guiFormSpecMenu.h
@@ -297,8 +297,17 @@ private:
std::map<std::wstring,int> listbox_selections;
} parserData;
+ typedef struct {
+ bool key_up;
+ bool key_down;
+ bool key_enter;
+ bool key_escape;
+ } fs_key_pendig;
+
std::vector<video::ITexture *> m_Textures;
+ fs_key_pendig current_keys_pending;
+
void parseElement(parserData* data,std::string element);
void parseSize(parserData* data,std::string element);
@@ -321,7 +330,7 @@ private:
void parseTabHeader(parserData* data,std::string element);
void parseBox(parserData* data,std::string element);
- bool parseColor(std::string color, irr::video::SColor& outcolor);
+ bool parseColor(std::string color, irr::video::SColor& outcolor);
};
class FormspecFormSource: public IFormSource
diff --git a/src/itemdef.cpp b/src/itemdef.cpp
index 238ff58c0..f692ccf55 100644
--- a/src/itemdef.cpp
+++ b/src/itemdef.cpp
@@ -389,7 +389,7 @@ public:
scene::IMesh *node_mesh = mapblock_mesh.getMesh();
assert(node_mesh);
video::SColor c(255, 255, 255, 255);
- if(g_settings->getS32("enable_shaders") != 0)
+ if(g_settings->getBool("enable_shaders"))
c = MapBlock_LightColor(255, 0xffff, decode_light(f.light_source));
setMeshColor(node_mesh, c);
diff --git a/src/mapblock_mesh.cpp b/src/mapblock_mesh.cpp
index 5e4d713b0..4386a1f13 100644
--- a/src/mapblock_mesh.cpp
+++ b/src/mapblock_mesh.cpp
@@ -1069,10 +1069,9 @@ MapBlockMesh::MapBlockMesh(MeshMakeData *data):
/*
Convert MeshCollector to SMesh
- Also store animation info
*/
- bool enable_shaders = (g_settings->getS32("enable_shaders") > 0);
bool enable_bumpmapping = g_settings->getBool("enable_bumpmapping");
+ bool enable_shaders = g_settings->getBool("enable_shaders");
video::E_MATERIAL_TYPE shadermat1 = m_gamedef->getShaderSource()->
getShader("test_shader_1").material;
video::E_MATERIAL_TYPE shadermat2 = m_gamedef->getShaderSource()->
diff --git a/src/shader.cpp b/src/shader.cpp
index 58042e651..19b8d7c9d 100644
--- a/src/shader.cpp
+++ b/src/shader.cpp
@@ -362,7 +362,7 @@ ShaderInfo generate_shader(std::string name, IrrlichtDevice *device,
Load shader programs
*/
void load_shaders(std::string name, SourceShaderCache *sourcecache,
- video::E_DRIVER_TYPE drivertype, s32 enable_shaders,
+ video::E_DRIVER_TYPE drivertype, bool enable_shaders,
std::string &vertex_program, std::string &pixel_program,
std::string &geometry_program, bool &is_highlevel);
@@ -625,8 +625,8 @@ ShaderInfo generate_shader(std::string name, IrrlichtDevice *device,
}
// 0 = off, 1 = assembly shaders only, 2 = highlevel or assembly
- s32 enable_shaders = g_settings->getS32("enable_shaders");
- if(enable_shaders <= 0)
+ bool enable_shaders = g_settings->getBool("enable_shaders");
+ if(!enable_shaders)
return shaderinfo;
video::IVideoDriver* driver = device->getVideoDriver();
@@ -748,7 +748,7 @@ ShaderInfo generate_shader(std::string name, IrrlichtDevice *device,
}
void load_shaders(std::string name, SourceShaderCache *sourcecache,
- video::E_DRIVER_TYPE drivertype, s32 enable_shaders,
+ video::E_DRIVER_TYPE drivertype, bool enable_shaders,
std::string &vertex_program, std::string &pixel_program,
std::string &geometry_program, bool &is_highlevel)
{
@@ -757,7 +757,7 @@ void load_shaders(std::string name, SourceShaderCache *sourcecache,
geometry_program = "";
is_highlevel = false;
- if(enable_shaders >= 2){
+ if(enable_shaders){
// Look for high level shaders
if(drivertype == video::EDT_DIRECT3D9){
// Direct3D 9: HLSL
@@ -778,7 +778,7 @@ void load_shaders(std::string name, SourceShaderCache *sourcecache,
}
}
- if(enable_shaders >= 1){
+ if(enable_shaders){
// Look for assembly shaders
if(drivertype == video::EDT_DIRECT3D8){
// Direct3D 8 assembly shaders
f='#n838'>838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952
/*
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 "nodedef.h"

#include "main.h" // For g_settings
#include "itemdef.h"
#ifndef SERVER
#include "tile.h"
#endif
#include "log.h"
#include "settings.h"
#include "nameidmapping.h"
#include "util/serialize.h"
//#include "profiler.h" // For TimeTaker

/*
	NodeBox
*/

void NodeBox::reset()
{
	type = NODEBOX_REGULAR;
	// default is empty
	fixed.clear();
	// default is sign/ladder-like
	wall_top = aabb3f(-BS/2, BS/2-BS/16., -BS/2, BS/2, BS/2, BS/2);
	wall_bottom = aabb3f(-BS/2, -BS/2, -BS/2, BS/2, -BS/2+BS/16., BS/2);
	wall_side = aabb3f(-BS/2, -BS/2, -BS/2, -BS/2+BS/16., BS/2, BS/2);
}

void NodeBox::serialize(std::ostream &os) const
{
	writeU8(os, 1); // version
	writeU8(os, type);

	if(type == NODEBOX_FIXED)
	{
		writeU16(os, fixed.size());
		for(std::vector<aabb3f>::const_iterator
				i = fixed.begin();
				i != fixed.end(); i++)
		{
			writeV3F1000(os, i->MinEdge);
			writeV3F1000(os, i->MaxEdge);
		}
	}
	else if(type == NODEBOX_WALLMOUNTED)
	{
		writeV3F1000(os, wall_top.MinEdge);
		writeV3F1000(os, wall_top.MaxEdge);
		writeV3F1000(os, wall_bottom.MinEdge);
		writeV3F1000(os, wall_bottom.MaxEdge);
		writeV3F1000(os, wall_side.MinEdge);
		writeV3F1000(os, wall_side.MaxEdge);
	}
}

void NodeBox::deSerialize(std::istream &is)
{
	int version = readU8(is);
	if(version != 1)
		throw SerializationError("unsupported NodeBox version");

	reset();

	type = (enum NodeBoxType)readU8(is);

	if(type == NODEBOX_FIXED)
	{
		u16 fixed_count = readU16(is);
		while(fixed_count--)
		{
			aabb3f box;
			box.MinEdge = readV3F1000(is);
			box.MaxEdge = readV3F1000(is);
			fixed.push_back(box);
		}
	}
	else if(type == NODEBOX_WALLMOUNTED)
	{
		wall_top.MinEdge = readV3F1000(is);
		wall_top.MaxEdge = readV3F1000(is);
		wall_bottom.MinEdge = readV3F1000(is);
		wall_bottom.MaxEdge = readV3F1000(is);
		wall_side.MinEdge = readV3F1000(is);
		wall_side.MaxEdge = readV3F1000(is);
	}
}

/*
	TileDef
*/

void TileDef::serialize(std::ostream &os, u16 protocol_version) const
{
	if(protocol_version >= 17)
		writeU8(os, 1); 
	else
		writeU8(os, 0);
	os<<serializeString(name);
	writeU8(os, animation.type);
	writeU16(os, animation.aspect_w);
	writeU16(os, animation.aspect_h);
	writeF1000(os, animation.length);
	if(protocol_version >= 17)
		writeU8(os, backface_culling);
}

void TileDef::deSerialize(std::istream &is)
{
	int version = readU8(is);
	name = deSerializeString(is);
	animation.type = (TileAnimationType)readU8(is);
	animation.aspect_w = readU16(is);
	animation.aspect_h = readU16(is);
	animation.length = readF1000(is);
	if(version >= 1)
		backface_culling = readU8(is);
}

/*
	SimpleSoundSpec serialization
*/

static void serializeSimpleSoundSpec(const SimpleSoundSpec &ss,
		std::ostream &os)
{
	os<<serializeString(ss.name);
	writeF1000(os, ss.gain);
}
static void deSerializeSimpleSoundSpec(SimpleSoundSpec &ss, std::istream &is)
{
	ss.name = deSerializeString(is);
	ss.gain = readF1000(is);
}

/*
	ContentFeatures
*/

ContentFeatures::ContentFeatures()
{
	reset();
}

ContentFeatures::~ContentFeatures()
{
}

void ContentFeatures::reset()
{
	/*
		Cached stuff
	*/
#ifndef SERVER
	solidness = 2;
	visual_solidness = 0;
	backface_culling = true;
#endif
	has_on_construct = false;
	has_on_destruct = false;
	has_after_destruct = false;
	/*
		Actual data

		NOTE: Most of this is always overridden by the default values given
		      in builtin.lua
	*/
	name = "";
	groups.clear();
	// Unknown nodes can be dug
	groups["dig_immediate"] = 2;
	drawtype = NDT_NORMAL;
	visual_scale = 1.0;
	for(u32 i=0; i<6; i++)
		tiledef[i] = TileDef();
	for(u16 j=0; j<CF_SPECIAL_COUNT; j++)
		tiledef_special[j] = TileDef();
	alpha = 255;
	post_effect_color = video::SColor(0, 0, 0, 0);
	param_type = CPT_NONE;
	param_type_2 = CPT2_NONE;
	is_ground_content = false;
	light_propagates = false;
	sunlight_propagates = false;
	walkable = true;
	pointable = true;
	diggable = true;
	climbable = false;
	buildable_to = false;
	rightclickable = true;
	liquid_type = LIQUID_NONE;
	liquid_alternative_flowing = "";
	liquid_alternative_source = "";
	liquid_viscosity = 0;
	liquid_renewable = true;
	light_source = 0;
	damage_per_second = 0;
	node_box = NodeBox();
	selection_box = NodeBox();
	legacy_facedir_simple = false;
	legacy_wallmounted = false;
	sound_footstep = SimpleSoundSpec();
	sound_dig = SimpleSoundSpec("__group");
	sound_dug = SimpleSoundSpec();
}

void ContentFeatures::serialize(std::ostream &os, u16 protocol_version)
{
	if(protocol_version < 14){
		serializeOld(os, protocol_version);
		return;
	}

	writeU8(os, 6); // version
	os<<serializeString(name);
	writeU16(os, groups.size());
	for(ItemGroupList::const_iterator
			i = groups.begin(); i != groups.end(); i++){
		os<<serializeString(i->first);
		writeS16(os, i->second);
	}
	writeU8(os, drawtype);
	writeF1000(os, visual_scale);
	writeU8(os, 6);
	for(u32 i=0; i<6; i++)
		tiledef[i].serialize(os, protocol_version);
	writeU8(os, CF_SPECIAL_COUNT);
	for(u32 i=0; i<CF_SPECIAL_COUNT; i++){
		tiledef_special[i].serialize(os, protocol_version);
	}
	writeU8(os, alpha);
	writeU8(os, post_effect_color.getAlpha());
	writeU8(os, post_effect_color.getRed());
	writeU8(os, post_effect_color.getGreen());
	writeU8(os, post_effect_color.getBlue());
	writeU8(os, param_type);
	writeU8(os, param_type_2);
	writeU8(os, is_ground_content);
	writeU8(os, light_propagates);
	writeU8(os, sunlight_propagates);
	writeU8(os, walkable);
	writeU8(os, pointable);
	writeU8(os, diggable);
	writeU8(os, climbable);
	writeU8(os, buildable_to);
	os<<serializeString(""); // legacy: used to be metadata_name
	writeU8(os, liquid_type);
	os<<serializeString(liquid_alternative_flowing);
	os<<serializeString(liquid_alternative_source);
	writeU8(os, liquid_viscosity);
	writeU8(os, liquid_renewable);
	writeU8(os, light_source);
	writeU32(os, damage_per_second);
	node_box.serialize(os);
	selection_box.serialize(os);
	writeU8(os, legacy_facedir_simple);
	writeU8(os, legacy_wallmounted);
	serializeSimpleSoundSpec(sound_footstep, os);
	serializeSimpleSoundSpec(sound_dig, os);
	serializeSimpleSoundSpec(sound_dug, os);
	writeU8(os, rightclickable);
	// Stuff below should be moved to correct place in a version that otherwise changes
	// the protocol version
}

void ContentFeatures::deSerialize(std::istream &is)
{
	int version = readU8(is);
	if(version != 6){
		deSerializeOld(is, version);
		return;
	}

	name = deSerializeString(is);
	groups.clear();
	u32 groups_size = readU16(is);
	for(u32 i=0; i<groups_size; i++){
		std::string name = deSerializeString(is);
		int value = readS16(is);
		groups[name] = value;
	}
	drawtype = (enum NodeDrawType)readU8(is);
	visual_scale = readF1000(is);
	if(readU8(is) != 6)
		throw SerializationError("unsupported tile count");
	for(u32 i=0; i<6; i++)
		tiledef[i].deSerialize(is);
	if(readU8(is) != CF_SPECIAL_COUNT)
		throw SerializationError("unsupported CF_SPECIAL_COUNT");
	for(u32 i=0; i<CF_SPECIAL_COUNT; i++)
		tiledef_special[i].deSerialize(is);
	alpha = readU8(is);
	post_effect_color.setAlpha(readU8(is));
	post_effect_color.setRed(readU8(is));
	post_effect_color.setGreen(readU8(is));
	post_effect_color.setBlue(readU8(is));
	param_type = (enum ContentParamType)readU8(is);
	param_type_2 = (enum ContentParamType2)readU8(is);
	is_ground_content = readU8(is);
	light_propagates = readU8(is);
	sunlight_propagates = readU8(is);
	walkable = readU8(is);
	pointable = readU8(is);
	diggable = readU8(is);
	climbable = readU8(is);
	buildable_to = readU8(is);
	deSerializeString(is); // legacy: used to be metadata_name
	liquid_type = (enum LiquidType)readU8(is);
	liquid_alternative_flowing = deSerializeString(is);
	liquid_alternative_source = deSerializeString(is);
	liquid_viscosity = readU8(is);
	liquid_renewable = readU8(is);
	light_source = readU8(is);
	damage_per_second = readU32(is);
	node_box.deSerialize(is);
	selection_box.deSerialize(is);
	legacy_facedir_simple = readU8(is);
	legacy_wallmounted = readU8(is);
	deSerializeSimpleSoundSpec(sound_footstep, is);
	deSerializeSimpleSoundSpec(sound_dig, is);
	deSerializeSimpleSoundSpec(sound_dug, is);
	rightclickable = readU8(is);
	// If you add anything here, insert it primarily inside the try-catch
	// block to not need to increase the version.
	try{
		// Stuff below should be moved to correct place in a version that
		// otherwise changes the protocol version
	}catch(SerializationError &e) {};
}

/*
	CNodeDefManager
*/

class CNodeDefManager: public IWritableNodeDefManager
{
public:
	void clear()
	{
		m_name_id_mapping.clear();
		m_name_id_mapping_with_aliases.clear();

		for(u16 i=0; i<=MAX_CONTENT; i++)
		{
			ContentFeatures &f = m_content_features[i];
			f.reset(); // Reset to defaults
		}

		// Set CONTENT_AIR
		{
			ContentFeatures f;
			f.name = "air";
			f.drawtype = NDT_AIRLIKE;
			f.param_type = CPT_LIGHT;
			f.light_propagates = true;
			f.sunlight_propagates = true;
			f.walkable = false;
			f.pointable = false;
			f.diggable = false;
			f.buildable_to = true;
			// Insert directly into containers
			content_t c = CONTENT_AIR;
			m_content_features[c] = f;
			addNameIdMapping(c, f.name);
		}
		// Set CONTENT_IGNORE
		{
			ContentFeatures f;
			f.name = "ignore";
			f.drawtype = NDT_AIRLIKE;
			f.param_type = CPT_NONE;
			f.light_propagates = false;
			f.sunlight_propagates = false;
			f.walkable = false;
			f.pointable = false;
			f.diggable = false;
			// A way to remove accidental CONTENT_IGNOREs
			f.buildable_to = true;
			// Insert directly into containers
			content_t c = CONTENT_IGNORE;
			m_content_features[c] = f;
			addNameIdMapping(c, f.name);
		}
	}
	// CONTENT_IGNORE = not found
	content_t getFreeId()
	{
		for(u32 i=0; i<=0xffff; i++){
			const ContentFeatures &f = m_content_features[i];
			if(f.name == "")
				return i;
		}
		return CONTENT_IGNORE;
	}
	CNodeDefManager()
	{
		clear();
	}
	virtual ~CNodeDefManager()
	{
	}
	virtual IWritableNodeDefManager* clone()
	{
		CNodeDefManager *mgr = new CNodeDefManager();
		for(u16 i=0; i<=MAX_CONTENT; i++)
		{
			mgr->set(i, get(i));
		}
		return mgr;
	}
	virtual const ContentFeatures& get(content_t c) const
	{
		assert(c <= MAX_CONTENT);
		return m_content_features[c];
	}
	virtual const ContentFeatures& get(const MapNode &n) const
	{
		return get(n.getContent());
	}
	virtual bool getId(const std::string &name, content_t &result) const
	{
		std::map<std::string, content_t>::const_iterator
			i = m_name_id_mapping_with_aliases.find(name);
		if(i == m_name_id_mapping_with_aliases.end())
			return false;
		result = i->second;
		return true;
	}
	virtual content_t getId(const std::string &name) const
	{
		content_t id = CONTENT_IGNORE;
		getId(name, id);
		return id;
	}
	virtual void getIds(const std::string &name, std::set<content_t> &result)
			const
	{
		//TimeTaker t("getIds", NULL, PRECISION_MICRO);
		if(name.substr(0,6) != "group:"){
			content_t id = CONTENT_IGNORE;
			if(getId(name, id))
				result.insert(id);
			return;
		}
		std::string group = name.substr(6);

#if 1	// Optimized version, takes less than 1 microsecond at -O1
		std::map<std::string, GroupItems>::const_iterator
			i = m_group_to_items.find(group);
		if (i == m_group_to_items.end())
			return;

		const GroupItems &items = i->second;
		for (GroupItems::const_iterator j = items.begin();
			j != items.end(); ++j) {
			if ((*j).second != 0)
				result.insert((*j).first);
		}
#else	// Old version, takes about ~150-200us at -O1
		for(u16 id=0; id<=MAX_CONTENT; id++)
		{
			const ContentFeatures &f = m_content_features[id];
			if(f.name == "") // Quickly discard undefined nodes
				continue;
			if(itemgroup_get(f.groups, group) != 0)
				result.insert(id);
		}
#endif
		//printf("getIds: %dus\n", t.stop());
	}
	virtual const ContentFeatures& get(const std::string &name) const
	{
		content_t id = CONTENT_IGNORE;
		getId(name, id);
		return get(id);
	}
	// IWritableNodeDefManager
	virtual void set(content_t c, const ContentFeatures &def)
	{
		verbosestream<<"registerNode: registering content id \""<<c
				<<"\": name=\""<<def.name<<"\""<<std::endl;
		assert(c <= MAX_CONTENT);
		// Don't allow redefining CONTENT_IGNORE (but allow air)
		if(def.name == "ignore" || c == CONTENT_IGNORE){
			infostream<<"registerNode: WARNING: Ignoring "
					<<"CONTENT_IGNORE redefinition"<<std::endl;
			return;
		}
		// Check that the special contents are not redefined as different id
		// because it would mess up everything
		if((def.name == "ignore" && c != CONTENT_IGNORE) ||
			(def.name == "air" && c != CONTENT_AIR)){
			errorstream<<"registerNode: IGNORING ERROR: "
					<<"trying to register built-in type \""
					<<def.name<<"\" as different id"<<std::endl;
			return;
		}
		m_content_features[c] = def;
		if(def.name != "")
			addNameIdMapping(c, def.name);

		// Add this content to the list of all groups it belongs to
		for (ItemGroupList::const_iterator i = def.groups.begin();
			i != def.groups.end(); ++i) {
			std::string group_name = i->first;
			
			std::map<std::string, GroupItems>::iterator
				j = m_group_to_items.find(group_name);
			if (j == m_group_to_items.end()) {
				m_group_to_items[group_name].push_back(std::make_pair(c, i->second));
			} else {
				GroupItems &items = j->second;
				items.push_back(std::make_pair(c, i->second));
			}
		}
	}
	virtual content_t set(const std::string &name,
			const ContentFeatures &def)
	{
		assert(name == def.name);
		u16 id = CONTENT_IGNORE;
		bool found = m_name_id_mapping.getId(name, id);  // ignore aliases
		if(!found){
			// Get some id
			id = getFreeId();
			if(id == CONTENT_IGNORE)
				return CONTENT_IGNORE;
			if(name != "")
				addNameIdMapping(id, name);
		}
		set(id, def);
		return id;
	}
	virtual content_t allocateDummy(const std::string &name)
	{
		assert(name != "");
		ContentFeatures f;
		f.name = name;
		return set(name, f);
	}
	virtual void updateAliases(IItemDefManager *idef)
	{
		std::set<std::string> all = idef->getAll();
		m_name_id_mapping_with_aliases.clear();
		for(std::set<std::string>::iterator
				i = all.begin(); i != all.end(); i++)
		{
			std::string name = *i;
			std::string convert_to = idef->getAlias(name);
			content_t id;
			if(m_name_id_mapping.getId(convert_to, id))
			{
				m_name_id_mapping_with_aliases.insert(
						std::make_pair(name, id));
			}
		}
	}
	virtual void updateTextures(ITextureSource *tsrc)
	{
#ifndef SERVER
		infostream<<"CNodeDefManager::updateTextures(): Updating "
				<<"textures in node definitions"<<std::endl;

		bool new_style_water = g_settings->getBool("new_style_water");
		bool new_style_leaves = g_settings->getBool("new_style_leaves");
		bool opaque_water = g_settings->getBool("opaque_water");

		for(u16 i=0; i<=MAX_CONTENT; i++)
		{
			ContentFeatures *f = &m_content_features[i];

			// Figure out the actual tiles to use
			TileDef tiledef[6];
			for(u32 j=0; j<6; j++)
			{
				tiledef[j] = f->tiledef[j];
				if(tiledef[j].name == "")
					tiledef[j].name = "unknown_node.png";
			}

			bool is_liquid = false;
			switch(f->drawtype){
			default:
			case NDT_NORMAL:
				f->solidness = 2;
				break;
			case NDT_AIRLIKE:
				f->solidness = 0;
				break;
			case NDT_LIQUID:
				assert(f->liquid_type == LIQUID_SOURCE);
				if(opaque_water)
					f->alpha = 255;
				if(new_style_water){
					f->solidness = 0;
				} else {
					f->solidness = 1;
					f->backface_culling = false;
				}
				is_liquid = true;
				break;
			case NDT_FLOWINGLIQUID:
				assert(f->liquid_type == LIQUID_FLOWING);
				f->solidness = 0;
				if(opaque_water)
					f->alpha = 255;
				is_liquid = true;
				break;
			case NDT_GLASSLIKE:
				f->solidness = 0;
				f->visual_solidness = 1;
				break;
			case NDT_GLASSLIKE_FRAMED:
				f->solidness = 0;
				f->visual_solidness = 1;
				break;
			case NDT_ALLFACES:
				f->solidness = 0;
				f->visual_solidness = 1;
				break;
			case NDT_ALLFACES_OPTIONAL:
				if(new_style_leaves){
					f->drawtype = NDT_ALLFACES;
					f->solidness = 0;
					f->visual_solidness = 1;
				} else {
					f->drawtype = NDT_NORMAL;
					f->solidness = 2;
					for(u32 i=0; i<6; i++){
						tiledef[i].name += std::string("^[noalpha");
					}
				}
				break;
			case NDT_PLANTLIKE:
				f->solidness = 0;
				f->backface_culling = false;
				break;
			case NDT_TORCHLIKE:
			case NDT_SIGNLIKE:
			case NDT_FENCELIKE:
			case NDT_RAILLIKE:
			case NDT_NODEBOX:
				f->solidness = 0;
				break;
			}

			u8 material_type;
			if (is_liquid)
				material_type = (f->alpha == 255) ? TILE_MATERIAL_LIQUID_OPAQUE : TILE_MATERIAL_LIQUID_TRANSPARENT;
			else
				material_type = (f->alpha == 255) ? TILE_MATERIAL_BASIC : TILE_MATERIAL_ALPHA;

			// Tiles (fill in f->tiles[])
			for(u16 j=0; j<6; j++){
				// Texture
				f->tiles[j].texture = tsrc->getTexture(tiledef[j].name);
				// Alpha
				f->tiles[j].alpha = f->alpha;
				// Material type
				f->tiles[j].material_type = material_type;
				// Material flags
				f->tiles[j].material_flags = 0;
				if(f->backface_culling)
					f->tiles[j].material_flags |= MATERIAL_FLAG_BACKFACE_CULLING;
				if(tiledef[j].animation.type == TAT_VERTICAL_FRAMES)
					f->tiles[j].material_flags |= MATERIAL_FLAG_ANIMATION_VERTICAL_FRAMES;
				// Animation parameters
				if(f->tiles[j].material_flags &
						MATERIAL_FLAG_ANIMATION_VERTICAL_FRAMES)
				{
					// Get raw texture size to determine frame count by
					// aspect ratio
					video::ITexture *t = tsrc->getTextureRaw(tiledef[j].name);
					v2u32 size = t->getOriginalSize();
					int frame_height = (float)size.X /
							(float)tiledef[j].animation.aspect_w *
							(float)tiledef[j].animation.aspect_h;
					int frame_count = size.Y / frame_height;
					int frame_length_ms = 1000.0 *
							tiledef[j].animation.length / frame_count;
					f->tiles[j].animation_frame_count = frame_count;
					f->tiles[j].animation_frame_length_ms = frame_length_ms;

					// If there are no frames for an animation, switch
					// animation off (so that having specified an animation
					// for something but not using it in the texture pack
					// gives no overhead)
					if(frame_count == 1){
						f->tiles[j].material_flags &=
								~MATERIAL_FLAG_ANIMATION_VERTICAL_FRAMES;
					}
				}
			}
			// Special tiles (fill in f->special_tiles[])
			for(u16 j=0; j<CF_SPECIAL_COUNT; j++){
				// Texture
				f->special_tiles[j].texture =
						tsrc->getTexture(f->tiledef_special[j].name);
				// Alpha
				f->special_tiles[j].alpha = f->alpha;
				// Material type
				f->special_tiles[j].material_type = material_type;
				// Material flags
				f->special_tiles[j].material_flags = 0;
				if(f->tiledef_special[j].backface_culling)
					f->special_tiles[j].material_flags |= MATERIAL_FLAG_BACKFACE_CULLING;
				if(f->tiledef_special[j].animation.type == TAT_VERTICAL_FRAMES)
					f->special_tiles[j].material_flags |= MATERIAL_FLAG_ANIMATION_VERTICAL_FRAMES;
				// Animation parameters
				if(f->special_tiles[j].material_flags &
						MATERIAL_FLAG_ANIMATION_VERTICAL_FRAMES)
				{
					// Get raw texture size to determine frame count by
					// aspect ratio
					video::ITexture *t = tsrc->getTextureRaw(f->tiledef_special[j].name);
					v2u32 size = t->getOriginalSize();
					int frame_height = (float)size.X /
							(float)f->tiledef_special[j].animation.aspect_w *
							(float)f->tiledef_special[j].animation.aspect_h;
					int frame_count = size.Y / frame_height;
					int frame_length_ms = 1000.0 *
							f->tiledef_special[j].animation.length / frame_count;
					f->special_tiles[j].animation_frame_count = frame_count;
					f->special_tiles[j].animation_frame_length_ms = frame_length_ms;

					// If there are no frames for an animation, switch
					// animation off (so that having specified an animation
					// for something but not using it in the texture pack
					// gives no overhead)
					if(frame_count == 1){
						f->special_tiles[j].material_flags &=
								~MATERIAL_FLAG_ANIMATION_VERTICAL_FRAMES;
					}
				}
			}
		}
#endif
	}
	void serialize(std::ostream &os, u16 protocol_version)
	{
		writeU8(os, 1); // version
		u16 count = 0;
		std::ostringstream os2(std::ios::binary);
		for(u16 i=0; i<=MAX_CONTENT; i++)
		{
			if(i == CONTENT_IGNORE || i == CONTENT_AIR)
				continue;
			ContentFeatures *f = &m_content_features[i];
			if(f->name == "")
				continue;
			writeU16(os2, i);
			// Wrap it in a string to allow different lengths without
			// strict version incompatibilities
			std::ostringstream wrapper_os(std::ios::binary);
			f->serialize(wrapper_os, protocol_version);
			os2<<serializeString(wrapper_os.str());
			count++;
		}
		writeU16(os, count);
		os<<serializeLongString(os2.str());
	}
	void deSerialize(std::istream &is)
	{
		clear();
		int version = readU8(is);
		if(version != 1)
			throw SerializationError("unsupported NodeDefinitionManager version");
		u16 count = readU16(is);
		std::istringstream is2(deSerializeLongString(is), std::ios::binary);
		for(u16 n=0; n<count; n++){
			u16 i = readU16(is2);
			if(i > MAX_CONTENT){
				errorstream<<"ContentFeatures::deSerialize(): "
						<<"Too large content id: "<<i<<std::endl;
				continue;
			}
			/*// Do not deserialize special types
			if(i == CONTENT_IGNORE || i == CONTENT_AIR)
				continue;*/
			ContentFeatures *f = &m_content_features[i];
			// Read it from the string wrapper
			std::string wrapper = deSerializeString(is2);
			std::istringstream wrapper_is(wrapper, std::ios::binary);
			f->deSerialize(wrapper_is);
			verbosestream<<"deserialized "<<f->name<<std::endl;
			if(f->name != "")
				addNameIdMapping(i, f->name);
		}
	}
private:
	void addNameIdMapping(content_t i, std::string name)
	{
		m_name_id_mapping.set(i, name);
		m_name_id_mapping_with_aliases.insert(std::make_pair(name, i));
	}
private:
	// Features indexed by id
	ContentFeatures m_content_features[MAX_CONTENT+1];
	// A mapping for fast converting back and forth between names and ids
	NameIdMapping m_name_id_mapping;
	// Like m_name_id_mapping, but only from names to ids, and includes
	// item aliases too. Updated by updateAliases()
	// Note: Not serialized.
	std::map<std::string, content_t> m_name_id_mapping_with_aliases;
	// A mapping from groups to a list of content_ts (and their levels)
	// that belong to it.  Necessary for a direct lookup in getIds().
	// Note: Not serialized.
	std::map<std::string, GroupItems> m_group_to_items;
};

IWritableNodeDefManager* createNodeDefManager()
{
	return new CNodeDefManager();
}

/*
	Serialization of old ContentFeatures formats
*/

void ContentFeatures::serializeOld(std::ostream &os, u16 protocol_version)
{
	if(protocol_version == 13)
	{
		writeU8(os, 5); // version
		os<<serializeString(name);
		writeU16(os, groups.size());
		for(ItemGroupList::const_iterator
				i = groups.begin(); i != groups.end(); i++){
			os<<serializeString(i->first);
			writeS16(os, i->second);
		}
		writeU8(os, drawtype);
		writeF1000(os, visual_scale);
		writeU8(os, 6);
		for(u32 i=0; i<6; i++)
			tiledef[i].serialize(os, protocol_version);
		writeU8(os, CF_SPECIAL_COUNT);
		for(u32 i=0; i<CF_SPECIAL_COUNT; i++){
			tiledef_special[i].serialize(os, protocol_version);