diff --git a/.gitignore b/.gitignore
index 3530326373badb4ed30c8ba0917d793651e4f999..21e2371a861075f2c2ea15af4b0ef4b1d7364098 100644
--- a/.gitignore
+++ b/.gitignore
@@ -4,13 +4,16 @@
 *bak*
 tags
 *.vim
+*.orig
+*.rej
 
 ## Non-static Minetest directories
 /bin/
 /games/*
 !/games/minimal/
 /cache/
-/textures/
+/textures/*
+!/textures/base/
 /sounds/
 /mods/*
 !/mods/minetest/
@@ -41,7 +44,9 @@ src/cguittfont/libcguittfont.a
 src/cguittfont/cmake_install.cmake
 src/cguittfont/Makefile
 src/json/CMakeFiles/
-src/json/libjson.a
+src/json/libjsoncpp.a
+src/sqlite/CMakeFiles/*
+src/sqlite/libsqlite3.a
 CMakeCache.txt
 CPackConfig.cmake
 CPackSourceConfig.cmake
diff --git a/CMakeLists.txt b/CMakeLists.txt
index fbf46d05957eccbe5802909b5d518c9e1a3d9e29..437d31cf597dda53d40015151f584286c86b16fe 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -12,7 +12,7 @@ set(VERSION_EXTRA "" CACHE STRING "Stuff to append to version string")
 # Also remember to set PROTOCOL_VERSION in clientserver.h when releasing
 set(VERSION_MAJOR 0)
 set(VERSION_MINOR 4)
-set(VERSION_PATCH 4-d1)
+set(VERSION_PATCH 5)
 if(VERSION_EXTRA)
 	set(VERSION_PATCH ${VERSION_PATCH}-${VERSION_EXTRA})
 endif()
@@ -133,6 +133,11 @@ endif()
 install(DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/builtin" DESTINATION "${SHAREDIR}")
 install(DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/client" DESTINATION "${SHAREDIR}")
 install(DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/games/minimal" DESTINATION "${SHAREDIR}/games")
+set(COMMON_SOURCE "${CMAKE_CURRENT_SOURCE_DIR}/games/common")
+if(EXISTS ${COMMON_SOURCE} AND IS_DIRECTORY ${COMMON_SOURCE})
+	install(FILES ${COMMON_SOURCE}/README.txt DESTINATION "${SHAREDIR}/games/common/")
+	install(DIRECTORY ${COMMON_SOURCE}/mods DESTINATION "${SHAREDIR}/games/common")
+endif()
 set(MINETEST_GAME_SOURCE "${CMAKE_CURRENT_SOURCE_DIR}/games/minetest_game")
 if(EXISTS ${MINETEST_GAME_SOURCE} AND IS_DIRECTORY ${MINETEST_GAME_SOURCE})
 	install(FILES ${MINETEST_GAME_SOURCE}/game.conf DESTINATION "${SHAREDIR}/games/minetest_game/")
diff --git a/README.txt b/README.txt
index 20917d77292c59faf263180cd3e77c1457ff1ba4..74940a1473c0d366d514715d988f14dd86f8f239 100644
--- a/README.txt
+++ b/README.txt
@@ -9,9 +9,10 @@ and contributors (see source file comments and the version control log)
 In case you downloaded the source code:
 ---------------------------------------
 If you downloaded the Minetest Engine source code in which this file is
-contained, you probably want to download the minetest_game project too:
+contained, you probably want to download these projects too:
+  https://github.com/minetest/common/
   https://github.com/minetest/minetest_game/
-See the README.txt in it.
+See the README.txt in them.
 
 Further documentation
 ----------------------
@@ -80,17 +81,24 @@ Compiling on GNU/Linux:
 -----------------------
 
 Install dependencies. Here's an example for Debian/Ubuntu:
-$ apt-get install build-essential libirrlicht-dev cmake libbz2-dev libpng12-dev libjpeg8-dev libxxf86vm-dev libgl1-mesa-dev libsqlite3-dev libogg-dev libvorbis-dev libopenal-dev
+$ apt-get install build-essential libirrlicht-dev cmake libbz2-dev libpng12-dev libjpeg8-dev libxxf86vm-dev libgl1-mesa-dev libsqlite3-dev libogg-dev libvorbis-dev libopenal-dev libcurl4-gnutls-dev libfreetype6-dev
 
 Download source, extract (this is the URL to the latest of source repository, which might not work at all times):
 $ wget https://github.com/minetest/minetest/tarball/master -O master.tar.gz
 $ tar xf master.tar.gz
 $ cd minetest-minetest-286edd4 (or similar)
 
+Download common (needed for minetest_game and some others)
+$ cd games/
+$ wget https://github.com/minetest/common/tarball/master -O common.tar.gz
+$ tar xf common.tar.gz
+$ mv minetest-common-* common
+$ cd ..
+
 Download minetest_game (otherwise only the "Minimal development test" game is available)
 $ cd games/
-$ wget https://github.com/minetest/minetest_game/tarball/master -O master.tar.gz
-$ tar xf master.tar.gz
+$ wget https://github.com/minetest/minetest_game/tarball/master -O minetest_game.tar.gz
+$ tar xf minetest_game.tar.gz
 $ mv minetest-minetest_game-* minetest_game
 $ cd ..
 
@@ -107,11 +115,12 @@ $ ./minetest
 - You can build a bare server or a bare client by specifying -DBUILD_CLIENT=0 or -DBUILD_SERVER=0
 - You can select between Release and Debug build by -DCMAKE_BUILD_TYPE=<Debug or Release>
   - Debug build is slower, but gives much more useful output in a debugger
+- If you build a bare server, you don't need to have Irrlicht installed. In that case use -DIRRLICHT_SOURCE_DIR=/the/irrlicht/source
 
 Compiling on Windows:
 ---------------------
 - This section is outdated. In addition to what is described here:
-  - In addition to minetest, you need to download minetest_game.
+  - In addition to minetest, you need to download common and minetest_game.
   - If you wish to have sound support, you need libogg, libvorbis and libopenal
 
 - You need:
diff --git a/builtin/falling.lua b/builtin/falling.lua
index d3af36f2934c823225a28076ad8b3d52d85e31fd..1c09f98560fb298338f56e31e0b72c3d93eaa643 100644
--- a/builtin/falling.lua
+++ b/builtin/falling.lua
@@ -57,6 +57,10 @@ minetest.register_entity("__builtin:falling_node", {
 		-- Note: walkable is in the node definition, not in item groups
 		if minetest.registered_nodes[bcn.name] and
 				minetest.registered_nodes[bcn.name].walkable then
+			if minetest.registered_nodes[bcn.name].buildable_to then
+				minetest.env:remove_node(bcp)
+				return
+			end
 			local np = {x=bcp.x, y=bcp.y+1, z=bcp.z}
 			-- Check what's here
 			local n2 = minetest.env:get_node(np)
@@ -80,6 +84,7 @@ minetest.register_entity("__builtin:falling_node", {
 			-- Create node and remove entity
 			minetest.env:add_node(np, {name=self.nodename})
 			self.object:remove()
+			nodeupdate(np)
 		else
 			-- Do nothing
 		end
@@ -144,7 +149,8 @@ function nodeupdate_single(p)
 		n_bottom = minetest.env:get_node(p_bottom)
 		-- Note: walkable is in the node definition, not in item groups
 		if minetest.registered_nodes[n_bottom.name] and
-				not minetest.registered_nodes[n_bottom.name].walkable then
+				(not minetest.registered_nodes[n_bottom.name].walkable or 
+					minetest.registered_nodes[n_bottom.name].buildable_to) then
 			minetest.env:remove_node(p)
 			spawn_falling_node(p, n.name)
 			nodeupdate(p)
diff --git a/builtin/item.lua b/builtin/item.lua
index 1349fdf63834d133bbb0cf46a5ce22c5a1cc8065..8e2f75a1a2a806ffed887f3a4e98631af32f796e 100644
--- a/builtin/item.lua
+++ b/builtin/item.lua
@@ -129,11 +129,18 @@ function minetest.item_place_node(itemstack, placer, pointed_thing)
 	end
 
 	local under = pointed_thing.under
-	local oldnode_under = minetest.env:get_node(under)
+	local oldnode_under = minetest.env:get_node_or_nil(under)
+	local above = pointed_thing.above
+	local oldnode_above = minetest.env:get_node_or_nil(above)
+
+	if not oldnode_under or not oldnode_above then
+		minetest.log("info", placer:get_player_name() .. " tried to place"
+			.. " node in unloaded position " .. minetest.pos_to_string(above))
+		return itemstack
+	end
+
 	local olddef_under = ItemStack({name=oldnode_under.name}):get_definition()
 	olddef_under = olddef_under or minetest.nodedef_default
-	local above = pointed_thing.above
-	local oldnode_above = minetest.env:get_node(above)
 	local olddef_above = ItemStack({name=oldnode_above.name}):get_definition()
 	olddef_above = olddef_above or minetest.nodedef_default
 
diff --git a/builtin/item_entity.lua b/builtin/item_entity.lua
index 1699cb03c0c49d590b516c8ca20a84af9d9e8b1a..50ce7eafedc3b8212079dfac4e4941796ef0a54a 100644
--- a/builtin/item_entity.lua
+++ b/builtin/item_entity.lua
@@ -111,6 +111,7 @@ minetest.register_entity("__builtin:item", {
 		if self.itemstring ~= '' then
 			local left = hitter:get_inventory():add_item("main", self.itemstring)
 			if not left:is_empty() then
+				self.itemstring = left:to_string()
 				return
 			end
 		end
diff --git a/builtin/misc.lua b/builtin/misc.lua
index e018aff85ce2517194fc6d028737771efbf7fb09..8308b3d6b4d3e4250df0203710990c3085eb304f 100644
--- a/builtin/misc.lua
+++ b/builtin/misc.lua
@@ -14,14 +14,14 @@ minetest.register_globalstep(function(dtime)
 	for index, timer in ipairs(minetest.timers) do
 		timer.time = timer.time - dtime
 		if timer.time <= 0 then
-			timer.func(timer.param)
+			timer.func(unpack(timer.args or {}))
 			table.remove(minetest.timers,index)
 		end
 	end
 end)
 
-function minetest.after(time, func, param)
-	table.insert(minetest.timers_to_add, {time=time, func=func, param=param})
+function minetest.after(time, func, ...)
+	table.insert(minetest.timers_to_add, {time=time, func=func, args={...}})
 end
 
 function minetest.check_player_privs(name, privs)
@@ -99,3 +99,10 @@ function minetest.setting_get_pos(name)
 	return minetest.string_to_pos(value)
 end
 
+function minetest.formspec_escape(str)
+	str = string.gsub(str, "\\", "\\\\")
+	str = string.gsub(str, "%[", "\\[")
+	str = string.gsub(str, "%]", "\\]")
+	return str
+end
+
diff --git a/client/shaders/test_shader_2/opengl_vertex.glsl b/client/shaders/test_shader_2/opengl_vertex.glsl
index 80fd6d4275086f117f1a60fbd5df02ba7f44d8ec..2881bad21d556cb35420596367769cc2dc38baba 100644
--- a/client/shaders/test_shader_2/opengl_vertex.glsl
+++ b/client/shaders/test_shader_2/opengl_vertex.glsl
@@ -8,9 +8,7 @@ varying vec3 vPosition;
 
 void main(void)
 {
-	vec4 pos = gl_Vertex;
-	pos.y -= 2.0;
-	gl_Position = mWorldViewProj * pos;
+	gl_Position = mWorldViewProj * gl_Vertex;
 
 	vPosition = (mWorldViewProj * gl_Vertex).xyz;
 
diff --git a/cmake/Modules/FindJson.cmake b/cmake/Modules/FindJson.cmake
index bc4e71a298cc581f8baa2c32d9f38fd6f45735cb..a9178a2251dca2a3e865537420a86dc179b7330e 100644
--- a/cmake/Modules/FindJson.cmake
+++ b/cmake/Modules/FindJson.cmake
@@ -2,17 +2,17 @@
 
 #FIND_PATH(JSON_INCLUDE_DIR json.h)
 
-#FIND_LIBRARY(JSON_LIBRARY NAMES json)
+#FIND_LIBRARY(JSON_LIBRARY NAMES jsoncpp)
 
 #IF(JSON_LIBRARY AND JSON_INCLUDE_DIR)
 #	SET( JSON_FOUND TRUE )
 #ENDIF(JSON_LIBRARY AND JSON_INCLUDE_DIR)
 
 #IF(JSON_FOUND)
-#	MESSAGE(STATUS "Found system json header file in ${JSON_INCLUDE_DIR}")
-#	MESSAGE(STATUS "Found system json library ${JSON_LIBRARY}")
+#	MESSAGE(STATUS "Found system jsoncpp header file in ${JSON_INCLUDE_DIR}")
+#	MESSAGE(STATUS "Found system jsoncpp library ${JSON_LIBRARY}")
 #ELSE(JSON_FOUND)
 	SET(JSON_INCLUDE_DIR ${PROJECT_SOURCE_DIR}/json)
-	SET(JSON_LIBRARY json)
-	MESSAGE(STATUS "Using project json library")
+	SET(JSON_LIBRARY jsoncpp)
+	MESSAGE(STATUS "Using project jsoncpp library")
 #ENDIF(JSON_FOUND)
diff --git a/cmake/Modules/FindOpenGLES2.cmake b/cmake/Modules/FindOpenGLES2.cmake
new file mode 100644
index 0000000000000000000000000000000000000000..42d31c898f8878cca48e4447a8f7630059cc3b84
--- /dev/null
+++ b/cmake/Modules/FindOpenGLES2.cmake
@@ -0,0 +1,130 @@
+#-------------------------------------------------------------------
+# This file is stolen from part of the CMake build system for OGRE (Object-oriented Graphics Rendering Engine) http://www.ogre3d.org/
+#
+# The contents of this file are placed in the public domain. Feel
+# free to make use of it in any way you like.
+#-------------------------------------------------------------------
+
+# - Try to find OpenGLES and EGL
+# Once done this will define
+#
+#  OPENGLES2_FOUND        - system has OpenGLES
+#  OPENGLES2_INCLUDE_DIR  - the GL include directory
+#  OPENGLES2_LIBRARIES    - Link these to use OpenGLES
+#
+#  EGL_FOUND        - system has EGL
+#  EGL_INCLUDE_DIR  - the EGL include directory
+#  EGL_LIBRARIES    - Link these to use EGL
+
+# win32, apple, android NOT TESED
+# linux tested and works
+
+IF (WIN32)
+  IF (CYGWIN)
+
+    FIND_PATH(OPENGLES2_INCLUDE_DIR GLES2/gl2.h )
+
+    FIND_LIBRARY(OPENGLES2_gl_LIBRARY libGLESv2 )
+
+  ELSE (CYGWIN)
+
+    IF(BORLAND)
+      SET (OPENGLES2_gl_LIBRARY import32 CACHE STRING "OpenGL ES 2.x library for win32")
+    ELSE(BORLAND)
+      # todo
+      # SET (OPENGLES_gl_LIBRARY ${SOURCE_DIR}/Dependencies/lib/release/libGLESv2.lib CACHE STRING "OpenGL ES 2.x library for win32"
+    ENDIF(BORLAND)
+
+  ENDIF (CYGWIN)
+
+ELSE (WIN32)
+
+  IF (APPLE)
+
+	create_search_paths(/Developer/Platforms)
+	findpkg_framework(OpenGLES2)
+    set(OPENGLES2_gl_LIBRARY "-framework OpenGLES")
+
+  ELSE(APPLE)
+
+    FIND_PATH(OPENGLES2_INCLUDE_DIR GLES2/gl2.h
+      /usr/openwin/share/include
+      /opt/graphics/OpenGL/include /usr/X11R6/include
+      /usr/include
+    )
+
+    FIND_LIBRARY(OPENGLES2_gl_LIBRARY
+      NAMES GLESv2
+      PATHS /opt/graphics/OpenGL/lib
+            /usr/openwin/lib
+            /usr/shlib /usr/X11R6/lib
+            /usr/lib
+    )
+
+    IF (NOT BUILD_ANDROID)
+		FIND_PATH(EGL_INCLUDE_DIR EGL/egl.h
+		  /usr/openwin/share/include
+		  /opt/graphics/OpenGL/include /usr/X11R6/include
+		  /usr/include
+		)
+
+		FIND_LIBRARY(EGL_egl_LIBRARY
+		  NAMES EGL
+		  PATHS /opt/graphics/OpenGL/lib
+				/usr/openwin/lib
+				/usr/shlib /usr/X11R6/lib
+				/usr/lib
+		)
+
+		# On Unix OpenGL most certainly always requires X11.
+		# Feel free to tighten up these conditions if you don't 
+		# think this is always true.
+		# It's not true on OSX.
+
+		IF (OPENGLES2_gl_LIBRARY)
+		  IF(NOT X11_FOUND)
+			INCLUDE(FindX11)
+		  ENDIF(NOT X11_FOUND)
+		  IF (X11_FOUND)
+			IF (NOT APPLE)
+			  SET (OPENGLES2_LIBRARIES ${X11_LIBRARIES})
+			ENDIF (NOT APPLE)
+		  ENDIF (X11_FOUND)
+		ENDIF (OPENGLES2_gl_LIBRARY)
+    ENDIF ()
+
+  ENDIF(APPLE)
+ENDIF (WIN32)
+
+#SET( OPENGLES2_LIBRARIES ${OPENGLES2_gl_LIBRARY} ${OPENGLES2_LIBRARIES})
+
+IF (BUILD_ANDROID)
+  IF(OPENGLES2_gl_LIBRARY)
+      SET( OPENGLES2_LIBRARIES ${OPENGLES2_gl_LIBRARY} ${OPENGLES2_LIBRARIES})
+      SET( EGL_LIBRARIES)
+      SET( OPENGLES2_FOUND "YES" )
+  ENDIF(OPENGLES2_gl_LIBRARY)
+ELSE ()
+
+  SET( OPENGLES2_LIBRARIES ${OPENGLES2_gl_LIBRARY} ${OPENGLES2_LIBRARIES})
+
+  IF(OPENGLES2_gl_LIBRARY AND EGL_egl_LIBRARY)
+    SET( OPENGLES2_LIBRARIES ${OPENGLES2_gl_LIBRARY} ${OPENGLES2_LIBRARIES})
+    SET( EGL_LIBRARIES ${EGL_egl_LIBRARY} ${EGL_LIBRARIES})
+    SET( OPENGLES2_FOUND "YES" )
+  ENDIF(OPENGLES2_gl_LIBRARY AND EGL_egl_LIBRARY)
+
+ENDIF ()
+
+MARK_AS_ADVANCED(
+  OPENGLES2_INCLUDE_DIR
+  OPENGLES2_gl_LIBRARY
+  EGL_INCLUDE_DIR
+  EGL_egl_LIBRARY
+)
+
+IF(OPENGLES2_FOUND)
+    MESSAGE(STATUS "Found system opengles2 library ${OPENGLES2_LIBRARIES}")
+ELSE ()
+    SET(OPENGLES2_LIBRARIES "")
+ENDIF ()
diff --git a/doc/lua_api.txt b/doc/lua_api.txt
index 005d7c010af462ac90a9dae5dd5af8a0e53f5ed7..beb70db1577a7cbd7b35eb76797150b4792e0782 100644
--- a/doc/lua_api.txt
+++ b/doc/lua_api.txt
@@ -1,4 +1,4 @@
-Minetest Lua Modding API Reference 0.4.4
+Minetest Lua Modding API Reference 0.4.5
 ==========================================
 More information at http://c55.me/minetest/
 
@@ -27,6 +27,39 @@ Startup
 Mods are loaded during server startup from the mod load paths by running
 the init.lua scripts in a shared environment.
 
+Paths
+-----
+RUN_IN_PLACE=1: (Windows release, local build)
+ $path_user:  Linux:    <build directory>
+              Windows:  <build directory>
+ $path_share: Linux:    <build directory>
+              Windows:  <build directory>
+
+RUN_IN_PLACE=0: (Linux release)
+ $path_share: Linux:    /usr/share/minetest
+              Windows:  <install directory>/minetest-0.4.x
+ $path_user:  Linux:    ~/.minetest
+              Windows:  C:/users/<user>/AppData/minetest (maybe)
+
+Games
+-----
+Games are looked up from:
+  $path_share/games/gameid/
+  $path_user/games/gameid/
+where gameid is unique to each game.
+
+The game directory contains the file game.conf, which contains these fields:
+  name = <Human-readable full name of the game>
+  common_mods = <Comma-separated list of common mods>
+eg.
+  name = Minetest
+  common_mods = bucket, default, doors, fire, stairs
+
+Common mods are loaded from the pseudo-game "common".
+
+The game directory can contain the file minetest.conf, which will be used
+to set default settings when running the particular game.
+
 Mod load path
 -------------
 Generic:
@@ -170,18 +203,18 @@ from the available ones of the following files:
 Examples of sound parameter tables:
 -- Play locationless on all clients
 {
-	gain = 1.0, -- default
+    gain = 1.0, -- default
 }
 -- Play locationless to a player
 {
-	to_player = name,
-	gain = 1.0, -- default
+    to_player = name,
+    gain = 1.0, -- default
 }
 -- Play in a location
 {
-	pos = {x=1,y=2,z=3},
-	gain = 1.0, -- default
-	max_hear_distance = 32, -- default
+    pos = {x=1,y=2,z=3},
+    gain = 1.0, -- default
+    max_hear_distance = 32, -- default
 }
 -- Play connected to an object, looped
 {
@@ -233,11 +266,11 @@ local drawtype = get_nodedef_field(nodename, "drawtype")
 Example: minetest.get_item_group(name, group) has been implemented as:
 
 function minetest.get_item_group(name, group)
-	if not minetest.registered_items[name] or not
-			minetest.registered_items[name].groups[group] then
-		return 0
-	end
-	return minetest.registered_items[name].groups[group]
+    if not minetest.registered_items[name] or not
+            minetest.registered_items[name].groups[group] then
+        return 0
+    end
+    return minetest.registered_items[name].groups[group]
 end
 
 Nodes
@@ -277,6 +310,10 @@ param2 is reserved for the engine when any of these are used:
   paramtype2 == "facedir"
   ^ The rotation of the node is stored in param2. Furnaces and chests are
     rotated this way. Can be made by using minetest.dir_to_facedir().
+    Values range 0 - 23
+    facedir modulo 4 = axisdir
+    0 = y+    1 = z+    2 = z-    3 = x+    4 = x-    5 = y-
+    facedir's two less significant bits are rotation around the axis
 
 Nodes can also contain extra data. See "Node Metadata".
 
@@ -335,6 +372,28 @@ A box is defined as:
 A box of a regular node would look like:
   {-0.5, -0.5, -0.5, 0.5, 0.5, 0.5},
 
+Ore types
+---------------
+These tell in what manner the ore is generated.
+All default ores are of the uniformly-distributed scatter type.
+
+- scatter
+    Randomly chooses a location and generates a cluster of ore.
+    If noise_params is specified, the ore will be placed if the 3d perlin noise at 
+    that point is greater than the noise_threshhold, giving the ability to create a non-equal
+    distribution of ore.
+- sheet
+    Creates a sheet of ore in a blob shape according to the 2d perlin noise described by noise_params.
+    The relative height of the sheet can be controlled by the same perlin noise as well, by specifying
+    a non-zero 'scale' parameter in noise_params.  IMPORTANT: The noise is not transformed by offset or
+    scale when comparing against the noise threshhold, but scale is used to determine relative height.
+    The height of the blob is randomly scattered, with a maximum height of clust_size.
+    clust_scarcity and clust_num_ores are ignored.
+    This is essentially an improved version of the so-called "stratus" ore seen in some unofficial mods.
+- claylike - NOT YET IMPLEMENTED
+    Places ore if there are no more than clust_scarcity number of specified nodes within a Von Neumann
+    neighborhood of clust_size radius.
+
 Representations of simple things
 --------------------------------
 Position/vector:
@@ -412,9 +471,11 @@ a node is destroyable and how long it takes to destroy by a tool.
 Groups of entities
 -------------------
 For entities, groups are, as of now, used only for calculating damage.
+The rating is the percentage of damage caused by tools with this damage group.
+See "Entity damage mechanism".
 
-object.get_armor_groups() -> a group-rating table (eg. {fleshy=3})
-object.set_armor_groups({level=2, fleshy=2, cracky=2})
+object.get_armor_groups() -> a group-rating table (eg. {fleshy=100})
+object.set_armor_groups({fleshy=30, cracky=80})
 
 Groups of tools
 ----------------
@@ -435,7 +496,7 @@ An example: Make meat soup from any meat, any water and any bowl
 }
 An another example: Make red wool from white wool and red dye
 {
-	type = 'shapeless',
+    type = 'shapeless',
     output = 'wool:red',
     recipe = {'wool:white', 'group:dye,basecolor_red'},
 }
@@ -446,7 +507,7 @@ Special groups
 - level: Can be used to give an additional sense of progression in the game.
   - A larger level will cause eg. a weapon of a lower level make much less
     damage, and get weared out much faster, or not be able to get drops
-	from destroyed nodes.
+    from destroyed nodes.
   - 0 is something that is directly accessible at the start of gameplay
   - There is no upper limit
 - dig_immediate: (player can always pick up node without tool wear)
@@ -463,7 +524,6 @@ Special groups
 
 Known damage and digging time defining groups
 ----------------------------------------------
-Valid ratings for these are 0, 1, 2 and 3, unless otherwise stated.
 - crumbly: dirt, sand
 - cracky: tough but crackable stuff like stone.
 - snappy: something that can be cut using fine tools; eg. leaves, small
@@ -516,6 +576,7 @@ groups to enable interaction with tools.
     * Uses (until the tool breaks)
     * Maximum level (usually 0, 1, 2 or 3)
     * Digging times
+    * Damage groups
 
 **Full punch interval**:
 When used as a weapon, the tool will do full damage if this time is spent
@@ -547,17 +608,19 @@ maximum level.
     result in the tool to be able to dig nodes that have a rating of 2 or 3
     for this group, and unable to dig the rating 1, which is the toughest.
     Unless there is a matching group that enables digging otherwise.
-  * For entities, damage equals the amount of nodes dug in the time spent
-    between hits, with a maximum time of ''full_punch_interval''.
+
+**Damage groups**
+List of damage for groups of entities. See "Entity damage mechanism".
 
 Example definition of the capabilities of a tool
 -------------------------------------------------
 tool_capabilities = {
-	full_punch_interval=1.5,
-	max_drop_level=1,
-	groupcaps={
-		crumbly={maxlevel=2, uses=20, times={[1]=1.60, [2]=1.20, [3]=0.80}}
-	}
+    full_punch_interval=1.5,
+    max_drop_level=1,
+    groupcaps={
+        crumbly={maxlevel=2, uses=20, times={[1]=1.60, [2]=1.20, [3]=0.80}}
+    }
+    damage_groups = {fleshy=2},
 }
 
 This makes the tool be able to dig nodes that fullfill both of these:
@@ -588,10 +651,12 @@ Notes:
 Entity damage mechanism
 ------------------------
 Damage calculation:
-- Take the time spent after the last hit
-- Limit time to full_punch_interval
-- Take the damage groups and imagine a bunch of nodes that have them
-- Damage in HP is the amount of nodes destroyed in this time.
+damage = 0
+foreach group in cap.damage_groups:
+    damage += cap.damage_groups[group] * limit(actual_interval / cap.full_punch_interval, 0.0, 1.0)
+        * (object.armor_groups[group] / 100.0)
+        -- Where object.armor_groups[group] is 0 for inexisting values
+return damage
 
 Client predicts damage based on damage groups. Because of this, it is able to
 give an immediate response when an entity is damaged or dies; the response is
@@ -717,7 +782,7 @@ field[<X>,<Y>;<W>,<H>;<name>;<label>;<default>]
 ^ default is the default value of the field
   ^ default may contain variable references such as '${text}' which
     will fill the value from the metadata value 'text'
-	^ Note: no extra text or more than a single variable is supported ATM.
+    ^ Note: no extra text or more than a single variable is supported ATM.
 
 field[<name>;<label>;<default>]
 ^ as above but without position/size units
@@ -778,6 +843,9 @@ string:trim()
 minetest.pos_to_string({x=X,y=Y,z=Z}) -> "(X,Y,Z)"
 ^ Convert position to a printable string
 minetest.string_to_pos(string) -> position
+^ Same but in reverse
+minetest.formspec_escape(string) -> string
+^ escapes characters like [, ], and \ that can not be used in formspecs
 
 minetest namespace reference
 -----------------------------
@@ -804,6 +872,7 @@ minetest.register_tool(name, item definition)
 minetest.register_craftitem(name, item definition)
 minetest.register_alias(name, convert_to)
 minetest.register_craft(recipe)
+minetest.register_ore(ore definition)
 
 Global callback registration functions: (Call these only at load time)
 minetest.register_globalstep(func(dtime))
@@ -919,12 +988,17 @@ minetest.get_craft_result(input) -> output, decremented_input
 ^ output.time = number, if unsuccessful: 0
 ^ decremented_input = like input
 minetest.get_craft_recipe(output) -> input
+^ returns last registered recipe for output item (node)
 ^ output is a node or item type such as 'default:torch'
 ^ input.method = 'normal' or 'cooking' or 'fuel'
 ^ input.width = for example 3
 ^ input.items = for example { stack 1, stack 2, stack 3, stack 4,
                               stack 5, stack 6, stack 7, stack 8, stack 9 }
 ^ input.items = nil if no recipe found
+minetest.get_all_craft_recipes(output) -> table or nil
+^ returns table with all registered recipes for output item (node)
+^ returns nil if no recipe was found
+^ table entries have same format as minetest.get_craft_recipe
 minetest.handle_node_drops(pos, drops, digger)
 ^ drops: list of itemstrings
 ^ Handles drops from nodes after digging: Default action is to put them into
@@ -969,9 +1043,9 @@ minetest.sound_play(spec, parameters) -> handle
 minetest.sound_stop(handle)
 
 Timing:
-minetest.after(time, func, param)
+minetest.after(time, func, ...)
 ^ Call function after time seconds
-^ param is optional; to pass multiple parameters, pass a table.
+^ Optional: Variable number of arguments that are passed to func
 
 Server:
 minetest.request_shutdown() -> request for server shutdown
@@ -983,6 +1057,37 @@ minetest.get_ban_description(ip_or_name) -> ban description (string)
 minetest.ban_player(name) -> ban a player
 minetest.unban_player_or_ip(name) -> unban player or IP address
 
+Particles:
+minetest.add_particle(pos, velocity, acceleration, expirationtime,
+    size, collisiondetection, texture, playername)
+^ Spawn particle at pos with velocity and acceleration
+^ Disappears after expirationtime seconds
+^ collisiondetection: if true collides with physical objects
+^ Uses texture (string)
+^ Playername is optional, if specified spawns particle only on the player's client
+
+minetest.add_particlespawner(amount, time,
+    minpos, maxpos,
+    minvel, maxvel,
+    minacc, maxacc,
+    minexptime, maxexptime,
+    minsize, maxsize,
+    collisiondetection, texture, playername)
+^ Add a particlespawner, an object that spawns an amount of particles over time seconds
+^ The particle's properties are random values in between the boundings:
+^ minpos/maxpos, minvel/maxvel (velocity), minacc/maxacc (acceleration),
+^ minsize/maxsize, minexptime/maxexptime (expirationtime)
+^ collisiondetection: if true uses collisiondetection
+^ Uses texture (string)
+^ Playername is optional, if specified spawns particle only on the player's client
+^ If time is 0 has infinite lifespan and spawns the amount on a per-second base
+^ Returns and id
+
+minetest.delete_particlespawner(id, player)
+^ Delete ParticleSpawner with id (return value from add_particlespawner)
+^ If playername is specified, only deletes on the player's client,
+^ otherwise on all clients
+
 Random:
 minetest.get_connected_players() -> list of ObjectRefs
 minetest.hash_node_position({x=,y=,z=}) -> 48-bit integer
@@ -1196,9 +1301,15 @@ methods:
 - set_wielded_item(item): replaces the wielded item, returns true if successful
 - set_armor_groups({group1=rating, group2=rating, ...})
 - set_animation({x=1,y=1}, frame_speed=15, frame_blend=0)
-- set_attach(parent, "", {x=0,y=0,z=0}, {x=0,y=0,z=0})
+- set_attach(parent, bone, position, rotation)
+  ^ bone = string
+  ^ position = {x=num, y=num, z=num} (relative)
+  ^ rotation = {x=num, y=num, z=num}
 - set_detach()
-- set_bone_position("", {x=0,y=0,z=0}, {x=0,y=0,z=0})
+- set_bone_position(bone, position, rotation)
+  ^ bone = string
+  ^ position = {x=num, y=num, z=num} (relative)
+  ^ rotation = {x=num, y=num, z=num}
 - set_properties(object property table)
 LuaEntitySAO-only: (no-op for other objects)
 - setvelocity({x=num, y=num, z=num})
@@ -1220,15 +1331,17 @@ Player-only: (no-op for other objects)
 - get_look_dir(): get camera direction as a unit vector
 - get_look_pitch(): pitch in radians
 - get_look_yaw(): yaw in radians (wraps around pretty randomly as of now)
+- set_look_pitch(radians): sets look pitch
+- set_look_yaw(radians): sets look yaw
 - set_inventory_formspec(formspec)
   ^ Redefine player's inventory form
   ^ Should usually be called in on_joinplayer
 - get_inventory_formspec() -> formspec string
 - get_player_control(): returns table with player pressed keys
-	{jump=bool,right=bool,left=bool,LMB=bool,RMB=bool,sneak=bool,aux1=bool,down=bool,up=bool}
+    {jump=bool,right=bool,left=bool,LMB=bool,RMB=bool,sneak=bool,aux1=bool,down=bool,up=bool}
 - get_player_control_bits(): returns integer with bit packed player pressed keys
-	bit nr/meaning: 0/up ,1/down ,2/left ,3/right ,4/jump ,5/aux1 ,6/sneak ,7/LMB ,8/RMB
-	
+    bit nr/meaning: 0/up ,1/down ,2/left ,3/right ,4/jump ,5/aux1 ,6/sneak ,7/LMB ,8/RMB
+    
 InvRef: Reference to an inventory
 methods:
 - is_empty(listname): return true if list is empty
@@ -1313,8 +1426,8 @@ Registered entities
     ^ puncher: ObjectRef (can be nil)
     ^ time_from_last_punch: Meant for disallowing spamming of clicks (can be nil)
     ^ tool_capabilities: capability table of used tool (can be nil)
-	^ dir: unit vector of direction of punch. Always defined. Points from
-	       the puncher to the punched.
+    ^ dir: unit vector of direction of punch. Always defined. Points from
+           the puncher to the punched.
   - on_rightclick(self, clicker)
   - get_staticdata(self)
     ^ Should return a string that will be passed to on_activate when
@@ -1389,10 +1502,10 @@ Item definition (register_node, register_craftitem, register_tool)
         max_drop_level=0,
         groupcaps={
             -- For example:
-            fleshy={times={[2]=0.80, [3]=0.40}, maxwear=0.05, maxlevel=1},
             snappy={times={[2]=0.80, [3]=0.40}, maxwear=0.05, maxlevel=1},
             choppy={times={[3]=0.90}, maxwear=0.05, maxlevel=0}
-        }
+        },
+        damage_groups = {groupname=damage},
     }
     node_placement_prediction = nil,
     ^ If nil and item is node, prediction is made automatically
@@ -1401,6 +1514,9 @@ Item definition (register_node, register_craftitem, register_tool)
     ^ Otherwise should be name of node which the client immediately places
       on ground when the player places the item. Server will always update
       actual result to client in a short moment.
+    sound = {
+        place = <SimpleSoundSpec>,
+    }
 
     on_place = func(itemstack, placer, pointed_thing),
     ^ Shall place item and return the leftover itemstack
@@ -1435,10 +1551,10 @@ Node definition (register_node)
     drawtype = "normal", -- See "Node drawtypes"
     visual_scale = 1.0,
     tiles = {tile definition 1, def2, def3, def4, def5, def6},
-	^ Textures of node; +Y, -Y, +X, -X, +Z, -Z (old field name: tile_images)
+    ^ Textures of node; +Y, -Y, +X, -X, +Z, -Z (old field name: tile_images)
     ^ List can be shortened to needed length
     special_tiles = {tile definition 1, Tile definition 2},
-	^ Special textures of node; used rarely (old field name: special_materials)
+    ^ Special textures of node; used rarely (old field name: special_materials)
     ^ List can be shortened to needed length
     alpha = 255,
     post_effect_color = {a=0, r=0, g=0, b=0}, -- If player is inside node
@@ -1468,6 +1584,7 @@ Node definition (register_node)
         footstep = <SimpleSoundSpec>,
         dig = <SimpleSoundSpec>, -- "__group" = group-based sound (default)
         dug = <SimpleSoundSpec>,
+        place = <SimpleSoundSpec>,
     },
 
     on_construct = func(pos),
@@ -1494,7 +1611,7 @@ Node definition (register_node)
     can_dig = function(pos,player)
     ^ returns true if node can be dug, or false if not
     ^ default: nil
-	
+    
     on_punch = func(pos, node, puncher),
     ^ default: minetest.node_punch
     ^ By default: does nothing
@@ -1517,32 +1634,32 @@ Node definition (register_node)
     ^ Called when an UI form (eg. sign text input) returns data
     ^ default: nil
 
-	allow_metadata_inventory_move = func(pos, from_list, from_index,
-			to_list, to_index, count, player),
-	^ Called when a player wants to move items inside the inventory
-	^ Return value: number of items allowed to move
-	
-	allow_metadata_inventory_put = func(pos, listname, index, stack, player),
-	^ Called when a player wants to put something into the inventory
-	^ Return value: number of items allowed to put
-	^ Return value: -1: Allow and don't modify item count in inventory
+    allow_metadata_inventory_move = func(pos, from_list, from_index,
+            to_list, to_index, count, player),
+    ^ Called when a player wants to move items inside the inventory
+    ^ Return value: number of items allowed to move
+    
+    allow_metadata_inventory_put = func(pos, listname, index, stack, player),
+    ^ Called when a player wants to put something into the inventory
+    ^ Return value: number of items allowed to put
+    ^ Return value: -1: Allow and don't modify item count in inventory
   
-	allow_metadata_inventory_take = func(pos, listname, index, stack, player),
-	^ Called when a player wants to take something out of the inventory
-	^ Return value: number of items allowed to take
-	^ Return value: -1: Allow and don't modify item count in inventory
-
-	on_metadata_inventory_move = func(pos, from_list, from_index,
-			to_list, to_index, count, player),
-	on_metadata_inventory_put = func(pos, listname, index, stack, player),
-	on_metadata_inventory_take = func(pos, listname, index, stack, player),
-	^ Called after the actual action has happened, according to what was allowed.
-	^ No return value
+    allow_metadata_inventory_take = func(pos, listname, index, stack, player),
+    ^ Called when a player wants to take something out of the inventory
+    ^ Return value: number of items allowed to take
+    ^ Return value: -1: Allow and don't modify item count in inventory
+
+    on_metadata_inventory_move = func(pos, from_list, from_index,
+            to_list, to_index, count, player),
+    on_metadata_inventory_put = func(pos, listname, index, stack, player),
+    on_metadata_inventory_take = func(pos, listname, index, stack, player),
+    ^ Called after the actual action has happened, according to what was allowed.
+    ^ No return value
     
-	on_blast = func(pos, intensity),
-	^ intensity: 1.0 = mid range of regular TNT
-	^ If defined, called when an explosion touches the node, instead of
-	  removing the node
+    on_blast = func(pos, intensity),
+    ^ intensity: 1.0 = mid range of regular TNT
+    ^ If defined, called when an explosion touches the node, instead of
+      removing the node
 }
 
 Recipe for register_craft: (shaped)
@@ -1591,6 +1708,28 @@ Recipe for register_craft (furnace fuel)
     burntime = 1,
 }
 
+Ore definition (register_ore)
+{
+    ore_type = "scatter" -- See "Ore types"
+    ore = "default:stone_with_coal",
+    wherein = "default:stone",
+    clust_scarcity = 8*8*8,
+    ^ Ore has a 1 out of clust_scarcity chance of spawning in a node
+    ^ This value should be *MUCH* higher than your intuition might tell you!
+    clust_num_ores = 8,
+    ^ Number of ores in a cluster
+    clust_size = 3,
+    ^ Size of the bounding box of the cluster
+    ^ In this example, there is a 3x3x3 cluster where 8 out of the 27 nodes are coal ore
+    height_min = -31000,
+    height_max = 64,
+    noise_threshhold = 0.5,
+    ^ If noise is above this threshhold, ore is placed.  Not needed for a uniform distribution
+    noise_params = {offset=0, scale=1, spread={x=100, y=100, z=100}, seed=23, octaves=3, persist=0.70}
+    ^ NoiseParams structure describing the perlin noise used for ore distribution.
+    ^ Needed for sheet ore_type.  Omit from scatter ore_type for a uniform ore distribution
+}
+
 Chatcommand definition (register_chatcommand)
 {
     params = "<name> <privilege>", -- short parameter description
@@ -1601,24 +1740,24 @@ Chatcommand definition (register_chatcommand)
 
 Detached inventory callbacks
 {
-	allow_move = func(inv, from_list, from_index, to_list, to_index, count, player),
+    allow_move = func(inv, from_list, from_index, to_list, to_index, count, player),
     ^ Called when a player wants to move items inside the inventory
-	^ Return value: number of items allowed to move
-	
+    ^ Return value: number of items allowed to move
+    
     allow_put = func(inv, listname, index, stack, player),
     ^ Called when a player wants to put something into the inventory
-	^ Return value: number of items allowed to put
-	^ Return value: -1: Allow and don't modify item count in inventory
+    ^ Return value: number of items allowed to put
+    ^ Return value: -1: Allow and don't modify item count in inventory
    
     allow_take = func(inv, listname, index, stack, player),
     ^ Called when a player wants to take something out of the inventory
-	^ Return value: number of items allowed to take
-	^ Return value: -1: Allow and don't modify item count in inventory
-	
-	on_move = func(inv, from_list, from_index, to_list, to_index, count, player),
+    ^ Return value: number of items allowed to take
+    ^ Return value: -1: Allow and don't modify item count in inventory
+    
+    on_move = func(inv, from_list, from_index, to_list, to_index, count, player),
     on_put = func(inv, listname, index, stack, player),
     on_take = func(inv, listname, index, stack, player),
-	^ Called after the actual action has happened, according to what was allowed.
-	^ No return value
+    ^ Called after the actual action has happened, according to what was allowed.
+    ^ No return value
 }
 
diff --git a/doc/protocol.txt b/doc/protocol.txt
index 160f1522602ce6715eae94ac30d2ad8ed12c8b17..b151f88d8b356d7941c777b9425e68b9363ef289 100644
--- a/doc/protocol.txt
+++ b/doc/protocol.txt
@@ -70,3 +70,39 @@ function check_if_minetestserver_up($host, $port)
 	return false;
 }
 
+- Here's a Python script for checking if a minetest server is up, confirmed working
+#!/usr/bin/env python
+import sys, time, socket
+address = ""
+port = 30000
+if len(sys.argv) <= 1:
+    print("Usage: %s <address>" % sys.argv[0])
+    exit()
+if ':' in sys.argv[1]:
+    address = sys.argv[1].split(':')[0]
+    try:
+        port = int(sys.argv[1].split(':')[1])
+    except ValueError:
+        print("Please specify a valid port")
+        exit()
+else:
+    address = sys.argv[1]
+
+try:
+    start = time.time()
+    sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
+    sock.settimeout(2.0)
+    buf = "\x4f\x45\x74\x03\x00\x00\x00\x01"
+    sock.sendto(buf, (address, port))
+    data, addr = sock.recvfrom(1000)
+    if data:
+        peer_id = data[12:14]
+        buf = "\x4f\x45\x74\x03" + peer_id + "\x00\x00\x03"
+        sock.sendto(buf, (address, port))
+        sock.close()
+        end = time.time()
+        print("%s is up (%0.5fms)" % (sys.argv[1],end-start))
+    else:
+        print("%s seems to be down " % sys.argv[1])
+except:
+    print("%s seems to be down " % sys.argv[1])
diff --git a/games/minimal/mods/default/init.lua b/games/minimal/mods/default/init.lua
index fbb481a0cd440c02edc02af285af3ec3403813bc..163998883bbd0ffe116fbd3b18d3f7169a7bbba7 100644
--- a/games/minimal/mods/default/init.lua
+++ b/games/minimal/mods/default/init.lua
@@ -659,6 +659,8 @@ function default.node_sound_dirt_defaults(table)
 			{name="", gain=0.5}
 	--table.dug = table.dug or
 	--		{name="default_dirt_break", gain=0.5}
+	table.place = table.place or
+			{name="default_grass_footstep", gain=0.5}
 	default.node_sound_defaults(table)
 	return table
 end
diff --git a/games/minimal/mods/default/mapgen.lua b/games/minimal/mods/default/mapgen.lua
index 115bb145879e8d70004604d7edfeb9f3df30372f..478567d0aa5e7e823e626426058172e732e73746 100644
--- a/games/minimal/mods/default/mapgen.lua
+++ b/games/minimal/mods/default/mapgen.lua
@@ -27,52 +27,74 @@ minetest.register_alias("mapgen_mese", "default:mese")
 -- Ore generation
 --
 
-local function generate_ore(name, wherein, minp, maxp, seed, chunks_per_volume, ore_per_chunk, height_min, height_max, param2)
-	if maxp.y < height_min or minp.y > height_max then
-		return
-	end
-	local y_min = math.max(minp.y, height_min)
-	local y_max = math.min(maxp.y, height_max)
-	local volume = (maxp.x-minp.x+1)*(y_max-y_min+1)*(maxp.z-minp.z+1)
-	local pr = PseudoRandom(seed)
-	local num_chunks = math.floor(chunks_per_volume * volume)
-	local chunk_size = 3
-	if ore_per_chunk <= 4 then
-		chunk_size = 2
-	end
-	local inverse_chance = math.floor(chunk_size*chunk_size*chunk_size / ore_per_chunk)
-	--print("generate_ore num_chunks: "..dump(num_chunks))
-	for i=1,num_chunks do
-		local y0 = pr:next(y_min, y_max-chunk_size+1)
-		if y0 >= height_min and y0 <= height_max then
-			local x0 = pr:next(minp.x, maxp.x-chunk_size+1)
-			local z0 = pr:next(minp.z, maxp.z-chunk_size+1)
-			local p0 = {x=x0, y=y0, z=z0}
-			for x1=0,chunk_size-1 do
-			for y1=0,chunk_size-1 do
-			for z1=0,chunk_size-1 do
-				if pr:next(1,inverse_chance) == 1 then
-					local x2 = x0+x1
-					local y2 = y0+y1
-					local z2 = z0+z1
-					local p2 = {x=x2, y=y2, z=z2}
-					if minetest.env:get_node(p2).name == wherein then
-						minetest.env:set_node(p2, {name=name, param2=param2})
-					end
-				end
-			end
-			end
-			end
-		end
-	end
-	--print("generate_ore done")
-end
+minetest.register_ore({
+	ore_type       = "scatter",
+	ore            = "default:stone_with_coal",
+	wherein        = "default:stone",
+	clust_scarcity = 8*8*8,
+	clust_num_ores = 5,
+	clust_size     = 3,
+	height_min     = -31000,
+	height_max     = 64,
+})
+
+minetest.register_ore({
+	ore_type       = "scatter",
+	ore            = "default:stone_with_iron",
+	wherein        = "default:stone",
+	clust_scarcity = 16*16*16,
+	clust_num_ores = 5,
+	clust_size     = 3,
+	height_min     = -5,
+	height_max     = 7,
+})
+
+minetest.register_ore({
+	ore_type       = "scatter",
+	ore            = "default:stone_with_iron",
+	wherein        = "default:stone",
+	clust_scarcity = 12*12*12,
+	clust_num_ores = 5,
+	clust_size     = 3,
+	height_min     = -16,
+	height_max     = -5,
+})
+
+minetest.register_ore({
+	ore_type       = "scatter",
+	ore            = "default:stone_with_iron",
+	wherein        = "default:stone",
+	clust_scarcity = 9*9*9,
+	clust_num_ores = 5,
+	clust_size     = 3,
+	height_min     = -31000,
+	height_max     = -17,
+})
+
+-- for float islands and far scaled
+minetest.register_ore({
+	ore_type       = "scatter",
+	ore            = "default:stone_with_coal",
+	wherein        = "default:stone",
+	clust_scarcity = 8*8*8,
+	clust_num_ores = 5,
+	clust_size     = 3,
+	height_min     = 200,
+	height_max     = 31000,
+})
+
+minetest.register_ore({
+	ore_type       = "scatter",
+	ore            = "default:stone_with_iron",
+	wherein        = "default:stone",
+	clust_scarcity = 9*9*9,
+	clust_num_ores = 5,
+	clust_size     = 3,
+	height_min     = 200,
+	height_max     = 31000,
+})
 
 minetest.register_on_generated(function(minp, maxp, seed)
-	generate_ore("default:stone_with_coal", "default:stone", minp, maxp, seed,   1/8/8/8,    5, -31000,  64)
-	generate_ore("default:stone_with_iron", "default:stone", minp, maxp, seed+1, 1/16/16/16, 5,   -5,   7)
-	generate_ore("default:stone_with_iron", "default:stone", minp, maxp, seed+2, 1/12/12/12, 5, -16,   -5)
-	generate_ore("default:stone_with_iron", "default:stone", minp, maxp, seed+3, 1/9/9/9,    5, -31000, -17)
 	-- Generate clay
 	if maxp.y >= 2 and minp.y <= 0 then
 		-- Assume X and Z lengths are equal
@@ -110,11 +132,5 @@ minetest.register_on_generated(function(minp, maxp, seed)
 		end
 		end
 	end
-	if minetest.setting_get("liquid_finite") then
-		generate_ore("default:water_source", "default:stone", minp, maxp, seed+42, 1/24/24/24, 4,  -100,   -10,  128)
-		generate_ore("default:water_source", "default:stone", minp, maxp, seed+42, 1/28/28/28, 3,  -10000, -101, 128)
-		generate_ore("default:lava_source",  "default:stone", minp, maxp, seed+43, 1/38/38/38, 2,  -500,   -100, 128)
-		generate_ore("default:lava_source",  "default:stone", minp, maxp, seed+43, 1/30/30/30, 4,  -31000, -501, 128)
-	end
 end)
 
diff --git a/minetest.conf.example b/minetest.conf.example
index 1f2a764f28003bb3a4965e55535c3195a9843ed1..838987c33dc99b725c58396881565f68d26f22b4 100644
--- a/minetest.conf.example
+++ b/minetest.conf.example
@@ -80,6 +80,7 @@
 # when set to higher number than 0
 #fsaa = 0
 #vsync = false
+#fov = 72
 # Address to connect to (#blank = start local server)
 #address = 
 # Enable random user input, for testing
@@ -95,9 +96,11 @@
 # Update liquids every .. recommend for finite: 0.2
 #liquid_update = 1.0
 # When finite liquid: relax flowing blocks to source if level near max and N nearby source blocks, more realistic, but not true constant. values: 0,1,2,3,4 : 0 - disable, 1 - most aggresive
-#liquid_relax = 1
-# optimization: faster cave flood (and not true constant)
+#liquid_relax = 2
+# Optimization: faster cave flood (and not true constant)
 #liquid_fast_flood = 1
+# Underground water and lava springs, its infnity sources if liquid_finite enabled
+#underground_springs = 1
 # Enable nice leaves; disable for speed
 #new_style_leaves = true
 # Enable smooth lighting with simple ambient occlusion;
@@ -125,6 +128,10 @@
 #farmesh_distance = 40
 # Enable/disable clouds
 #enable_clouds = true
+#cloud_height = 120
+#enable_3d_clouds = true
+# Use a cloud animation for the main menu background
+#menu_clouds = true
 # Path for screenshots
 #screenshot_path = .
 # Amount of view bobbing (0 = no view bobbing, 1.0 = normal, 2.0 = double)
@@ -304,29 +311,45 @@
 # Mapgen stuff
 #
 
-# Name of map generator to be used.  Currently only v6 is supported.
+# Name of map generator to be used.  Currently v6, indev and singlenode are supported.
 #mg_name = v6
 # Water level of map.
 #water_level = 1
 # Size of chunks to be generated.
 #chunksize = 5
-# Map generation attributes.  Currently supported: trees, caves, flat, v6_biome_blend
+# Map generation attributes.  Currently supported: trees, caves, flat, v6_biome_blend, v6_jungles, dungeons
 #mg_flags = trees, caves, v6_biome_blend
 # How large deserts and beaches are
 #mgv6_freq_desert = 0.45
 #mgv6_freq_beach = 0.15
 # Perlin noise attributes for different map generation parameters
 # Offset, scale, spread factor, seed offset, number of octaves, persistence
-#mgv6_np_terrain_base = -4, 20, (250.0, 250, 250), 82341, 5, 0.6
+#mgv6_np_terrain_base = -4, 20, (250, 250, 250), 82341, 5, 0.6
 #mgv6_np_terrain_higher = 20, 16, (500, 500, 500), 85039, 5, 0.6
 #mgv6_np_steepness = 0.85, 0.5, (125, 125, 125), -932, 5, 0.7
 #mgv6_np_height_select = 0.5, 1, (250, 250, 250), 4213, 5, 0.69
-#mgv6_np_trees = 0, 1, (125, 125, 125), 2, 4, 0.66
 #mgv6_np_mud = 4, 2, (200, 200, 200), 91013, 3, 0.55
 #mgv6_np_beach = 0, 1, (250, 250, 250), 59420, 3, 0.50
 #mgv6_np_biome = 0, 1, (250, 250, 250), 9130, 3, 0.50
 #mgv6_np_cave = 6, 6, (250, 250, 250), 34329, 3, 0.50
+#mgv6_np_humidity = 0.5, 0.5, (500, 500, 500), 72384, 4, 0.66
+#mgv6_np_trees = 0, 1, (125, 125, 125), 2, 4, 0.66
+#mgv6_np_apple_trees = 0, 1, (100, 100, 100), 342902, 3, 0.45
+
 #mgv7_np_terrain = 10, 12, (350, 350, 350), 82341, 5, 0.6
 #mgv7_np_bgroup = 0.5, 0.3125, (350, 350, 350), 5923, 2, 0.6
 #mgv7_np_heat = 25, 50, (500, 500, 500), 35293, 1, 0
 #mgv7_np_humidity = 50, 31.25, (750, 750, 750), 12094, 2, 0.6
+
+# Offset, scale, spread factor, seed offset, number of octaves, persistence, farscale, farspread
+#mgindev_np_terrain_base   = -4,   20,  (250, 250, 250), 82341, 5, 0.6,  10,  10
+#mgindev_np_terrain_higher = 20,   16,  (500, 500, 500), 85039, 5, 0.6,  10,  10
+#mgindev_np_steepness      = 0.85, 0.5, (125, 125, 125), -932,  5, 0.7,  2,   10
+#mgindev_np_mud            = 4,    2,   (200, 200, 200), 91013, 3, 0.55, 1,   1
+#mgindev_np_float_islands1 = 0,    1,   (64,  64,  64 ), 3683,  5, 0.5,  1,   1.5
+#mgindev_np_float_islands2 = 0,    1,   (8,   8,   8  ), 9292,  2, 0.5,  1,   1.5
+#mgindev_np_float_islands3 = 0,    1,   (256, 256, 256), 6412,  2, 0.5,  1,   0.5
+#mgindev_np_biome          = 0,    1,   (250, 250, 250), 9130,  3, 0.50, 1,   10
+
+# Float islands starts from height, 0 to disable
+#mgindev_float_islands = 500
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index d2f080c907d86a8186fe04b5602238a9a314194d..d6182861fa3dc0ee3681c2c021c169777e0cf89c 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -161,7 +161,7 @@ else()
 		endif(APPLE)
 	endif(BUILD_CLIENT)
 	find_package(ZLIB REQUIRED)
-	set(PLATFORM_LIBS -lpthread ${CMAKE_DL_LIBS})
+	set(PLATFORM_LIBS -lpthread -lrt ${CMAKE_DL_LIBS})
 	#set(CLIENT_PLATFORM_LIBS -lXxf86vm)
 	# This way Xxf86vm is found on OpenBSD too
 	find_library(XXF86VM_LIBRARY Xxf86vm)
@@ -172,6 +172,7 @@ endif()
 find_package(Jthread REQUIRED)
 find_package(Sqlite3 REQUIRED)
 find_package(Json REQUIRED)
+find_package(OpenGLES2)
 
 if(USE_FREETYPE)
 	find_package(Freetype REQUIRED)
@@ -205,6 +206,20 @@ set(common_SRCS
 	itemdef.cpp
 	nodedef.cpp
 	object_properties.cpp
+	scriptapi_types.cpp
+	scriptapi_common.cpp
+	scriptapi_content.cpp
+	scriptapi_craft.cpp
+	scriptapi_node.cpp
+	scriptapi_item.cpp
+	scriptapi_env.cpp
+	scriptapi_nodetimer.cpp
+	scriptapi_noise.cpp
+	scriptapi_entity.cpp
+	scriptapi_object.cpp
+	scriptapi_nodemeta.cpp
+	scriptapi_inventory.cpp
+	scriptapi_particles.cpp
 	scriptapi.cpp
 	script.cpp
 	log.cpp
@@ -212,7 +227,10 @@ set(common_SRCS
 	emerge.cpp
 	mapgen.cpp
 	mapgen_v6.cpp
+	mapgen_indev.cpp
+	mapgen_singlenode.cpp
 	treegen.cpp
+	dungeongen.cpp
 	content_nodemeta.cpp
 	content_mapnode.cpp
 	collision.cpp
@@ -371,6 +389,7 @@ if(BUILD_CLIENT)
 		${SQLITE3_LIBRARY}
 		${LUA_LIBRARY}
 		${JSON_LIBRARY}
+		${OPENGLES2_LIBRARIES}
 		${PLATFORM_LIBS}
 		${CLIENT_PLATFORM_LIBS}
 	)
diff --git a/src/activeobject.h b/src/activeobject.h
index e454f2c8c8da0d8c27bd3bf805363c78fa6637ef..1a75fba2efb1f67bbf0ced2541d708b901240ebb 100644
--- a/src/activeobject.h
+++ b/src/activeobject.h
@@ -61,7 +61,7 @@ class ActiveObject
 	}
 
 	virtual u8 getType() const = 0;
-
+	virtual bool getCollisionBox(aabb3f *toset) = 0;
 protected:
 	u16 m_id; // 0 is invalid, "no id"
 };
diff --git a/src/chat.cpp b/src/chat.cpp
index c3509ae49f288c1e6e553dc11cb647de0f18915c..1135ccdf7486be295043e659a1d0685edcd241ea 100644
--- a/src/chat.cpp
+++ b/src/chat.cpp
@@ -117,8 +117,8 @@ void ChatBuffer::deleteOldest(u32 count)
 		--count;
 	}
 
-	m_unformatted.erase(0, del_unformatted);
-	m_formatted.erase(0, del_formatted);
+	m_unformatted.erase(m_unformatted.begin(), m_unformatted.begin() + del_unformatted);
+	m_formatted.erase(m_formatted.begin(), m_formatted.begin() + del_formatted);
 }
 
 void ChatBuffer::deleteByAge(f32 maxAge)
@@ -232,10 +232,10 @@ void ChatBuffer::scrollTop()
 }
 
 u32 ChatBuffer::formatChatLine(const ChatLine& line, u32 cols,
-		core::array<ChatFormattedLine>& destination) const
+		std::vector<ChatFormattedLine>& destination) const
 {
 	u32 num_added = 0;
-	core::array<ChatFormattedFragment> next_frags;
+	std::vector<ChatFormattedFragment> next_frags;
 	ChatFormattedLine next_line;
 	ChatFormattedFragment temp_frag;
 	u32 out_column = 0;
@@ -292,7 +292,7 @@ u32 ChatBuffer::formatChatLine(const ChatLine& line, u32 cols,
 				frag.column = out_column;
 				next_line.fragments.push_back(frag);
 				out_column += frag.text.size();
-				next_frags.erase(0, 1);
+				next_frags.erase(next_frags.begin());
 			}
 			else
 			{
@@ -414,7 +414,7 @@ std::wstring ChatPrompt::submit()
 	if (!line.empty())
 		m_history.push_back(line);
 	if (m_history.size() > m_history_limit)
-		m_history.erase(0);
+		m_history.erase(m_history.begin());
 	m_history_index = m_history.size();
 	m_view = 0;
 	m_cursor = 0;
@@ -464,7 +464,7 @@ void ChatPrompt::historyNext()
 	}
 }
 
-void ChatPrompt::nickCompletion(const core::list<std::wstring>& names, bool backwards)
+void ChatPrompt::nickCompletion(const std::list<std::wstring>& names, bool backwards)
 {
 	// Two cases:
 	// (a) m_nick_completion_start == m_nick_completion_end == 0
@@ -492,10 +492,10 @@ void ChatPrompt::nickCompletion(const core::list<std::wstring>& names, bool back
 	std::wstring prefix = m_line.substr(prefix_start, prefix_end - prefix_start);
 
 	// find all names that start with the selected prefix
-	core::array<std::wstring> completions;
-	for (core::list<std::wstring>::ConstIterator
+	std::vector<std::wstring> completions;
+	for (std::list<std::wstring>::const_iterator
 			i = names.begin();
-			i != names.end(); i++)
+			i != names.end(); ++i)
 	{
 		if (str_starts_with(*i, prefix, true))
 		{
diff --git a/src/chat.h b/src/chat.h
index 27863922cc265a6affce52faa0a32c4c97aa23cd..8a40c7ccf58c37840249f004ddd2166c155132ef 100644
--- a/src/chat.h
+++ b/src/chat.h
@@ -22,6 +22,8 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 
 #include "irrlichttypes_bloated.h"
 #include <string>
+#include <vector>
+#include <list>
 
 // Chat console related classes, only used by the client
 
@@ -55,7 +57,7 @@ struct ChatFormattedFragment
 struct ChatFormattedLine
 {
 	// Array of text fragments
-	core::array<ChatFormattedFragment> fragments;
+	std::vector<ChatFormattedFragment> fragments;
 	// true if first line of one formatted ChatLine
 	bool first;
 };
@@ -110,7 +112,7 @@ class ChatBuffer
 	// Appends the formatted lines to the destination array and
 	// returns the number of formatted lines.
 	u32 formatChatLine(const ChatLine& line, u32 cols,
-			core::array<ChatFormattedLine>& destination) const;
+			std::vector<ChatFormattedLine>& destination) const;
 
 protected:
 	s32 getTopScrollPos() const;
@@ -120,7 +122,7 @@ class ChatBuffer
 	// Scrollback size
 	u32 m_scrollback;
 	// Array of unformatted chat lines
-	core::array<ChatLine> m_unformatted;
+	std::vector<ChatLine> m_unformatted;
 	
 	// Number of character columns in console
 	u32 m_cols;
@@ -129,7 +131,7 @@ class ChatBuffer
 	// Scroll position (console's top line index into m_formatted)
 	s32 m_scroll;
 	// Array of formatted lines
-	core::array<ChatFormattedLine> m_formatted;
+	std::vector<ChatFormattedLine> m_formatted;
 	// Empty formatted line, for error returns
 	ChatFormattedLine m_empty_formatted_line;
 };
@@ -158,7 +160,7 @@ class ChatPrompt
 	void historyNext();
 
 	// Nick completion
-	void nickCompletion(const core::list<std::wstring>& names, bool backwards);
+	void nickCompletion(const std::list<std::wstring>& names, bool backwards);
 
 	// Update console size and reformat the visible portion of the prompt
 	void reformat(u32 cols);
@@ -209,7 +211,7 @@ class ChatPrompt
 	// Currently edited line
 	std::wstring m_line;
 	// History buffer
-	core::array<std::wstring> m_history;
+	std::vector<std::wstring> m_history;
 	// History index (0 <= m_history_index <= m_history.size()) 
 	u32 m_history_index;
 	// Maximum number of history entries
diff --git a/src/client.cpp b/src/client.cpp
index be35db5dead72267a1589a1e95cd801fd0a02172..f27f95d98c2d939d7c8e010e28344f78ec67e90f 100644
--- a/src/client.cpp
+++ b/src/client.cpp
@@ -232,8 +232,8 @@ void * MediaFetchThread::Thread()
 	#if USE_CURL
 	CURL *curl;
 	CURLcode res;
-	for (core::list<MediaRequest>::Iterator i = m_file_requests.begin();
-			i != m_file_requests.end(); i++) {
+	for (std::list<MediaRequest>::iterator i = m_file_requests.begin();
+			i != m_file_requests.end(); ++i) {
 		curl = curl_easy_init();
 		assert(curl);
 		curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1);
@@ -360,8 +360,8 @@ Client::~Client()
 		}
 	}
 
-	for (core::list<MediaFetchThread*>::Iterator i = m_media_fetch_threads.begin();
-			i != m_media_fetch_threads.end(); i++)
+	for (std::list<MediaFetchThread*>::iterator i = m_media_fetch_threads.begin();
+			i != m_media_fetch_threads.end(); ++i)
 		delete *i;
 }
 
@@ -585,7 +585,7 @@ void Client::step(float dtime)
 	if(m_map_timer_and_unload_interval.step(dtime, map_timer_and_unload_dtime))
 	{
 		ScopeProfiler sp(g_profiler, "Client: map timer and unload");
-		core::list<v3s16> deleted_blocks;
+		std::list<v3s16> deleted_blocks;
 		m_env.getMap().timerUpdate(map_timer_and_unload_dtime,
 				g_settings->getFloat("client_unload_unused_data_timeout"),
 				&deleted_blocks);
@@ -599,8 +599,8 @@ void Client::step(float dtime)
 			NOTE: This loop is intentionally iterated the way it is.
 		*/
 
-		core::list<v3s16>::Iterator i = deleted_blocks.begin();
-		core::list<v3s16> sendlist;
+		std::list<v3s16>::iterator i = deleted_blocks.begin();
+		std::list<v3s16> sendlist;
 		for(;;)
 		{
 			if(sendlist.size() == 255 || i == deleted_blocks.end())
@@ -619,9 +619,9 @@ void Client::step(float dtime)
 				writeU16(&reply[0], TOSERVER_DELETEDBLOCKS);
 				reply[2] = sendlist.size();
 				u32 k = 0;
-				for(core::list<v3s16>::Iterator
+				for(std::list<v3s16>::iterator
 						j = sendlist.begin();
-						j != sendlist.end(); j++)
+						j != sendlist.end(); ++j)
 				{
 					writeV3S16(&reply[2+1+6*k], *j);
 					k++;
@@ -635,7 +635,7 @@ void Client::step(float dtime)
 			}
 
 			sendlist.push_back(*i);
-			i++;
+			++i;
 		}
 	}
 
@@ -727,7 +727,7 @@ void Client::step(float dtime)
 				<<std::endl;*/
 		
 		int num_processed_meshes = 0;
-		while(m_mesh_update_thread.m_queue_out.size() > 0)
+		while(!m_mesh_update_thread.m_queue_out.empty())
 		{
 			num_processed_meshes++;
 			MeshUpdateResult r = m_mesh_update_thread.m_queue_out.pop_front();
@@ -779,10 +779,10 @@ void Client::step(float dtime)
 	*/
 	if (m_media_receive_started) {
 		bool all_stopped = true;
-		for (core::list<MediaFetchThread*>::Iterator thread = m_media_fetch_threads.begin();
-				thread != m_media_fetch_threads.end(); thread++) {
+		for (std::list<MediaFetchThread*>::iterator thread = m_media_fetch_threads.begin();
+				thread != m_media_fetch_threads.end(); ++thread) {
 			all_stopped &= !(*thread)->IsRunning();
-			while ((*thread)->m_file_data.size() > 0) {
+			while (!(*thread)->m_file_data.empty()) {
 				std::pair <std::string, std::string> out = (*thread)->m_file_data.pop_front();
 				++m_media_received_count;
 
@@ -803,9 +803,9 @@ void Client::step(float dtime)
 				}
 
 				{
-					core::map<std::string, std::string>::Node *n;
+					std::map<std::string, std::string>::iterator n;
 					n = m_media_name_sha1_map.find(out.first);
-					if(n == NULL)
+					if(n == m_media_name_sha1_map.end())
 						errorstream<<"The server sent a file that has not "
 								<<"been announced."<<std::endl;
 					else
@@ -814,11 +814,11 @@ void Client::step(float dtime)
 			}
 		}
 		if (all_stopped) {
-			core::list<MediaRequest> fetch_failed;
-			for (core::list<MediaFetchThread*>::Iterator thread = m_media_fetch_threads.begin();
-					thread != m_media_fetch_threads.end(); thread++) {
-				for (core::list<MediaRequest>::Iterator request = (*thread)->m_failed.begin();
-						request != (*thread)->m_failed.end(); request++)
+			std::list<MediaRequest> fetch_failed;
+			for (std::list<MediaFetchThread*>::iterator thread = m_media_fetch_threads.begin();
+					thread != m_media_fetch_threads.end(); ++thread) {
+				for (std::list<MediaRequest>::iterator request = (*thread)->m_failed.begin();
+						request != (*thread)->m_failed.end(); ++request)
 					fetch_failed.push_back(*request);
 				(*thread)->m_failed.clear();
 			}
@@ -1015,14 +1015,14 @@ void Client::deletingPeer(con::Peer *peer, bool timeout)
 		string name
 	}
 */
-void Client::request_media(const core::list<MediaRequest> &file_requests)
+void Client::request_media(const std::list<MediaRequest> &file_requests)
 {
 	std::ostringstream os(std::ios_base::binary);
 	writeU16(os, TOSERVER_REQUEST_MEDIA);
 	writeU16(os, file_requests.size());
 
-	for(core::list<MediaRequest>::ConstIterator i = file_requests.begin();
-			i != file_requests.end(); i++) {
+	for(std::list<MediaRequest>::const_iterator i = file_requests.begin();
+			i != file_requests.end(); ++i) {
 		os<<serializeString(i->name);
 	}
 
@@ -1622,7 +1622,7 @@ void Client::ProcessData(u8 *data, u32 datasize, u16 sender_peer_id)
 		infostream<<"Client: Received media announcement: packet size: "
 				<<datasize<<std::endl;
 
-		core::list<MediaRequest> file_requests;
+		std::list<MediaRequest> file_requests;
 
 		for(int i=0; i<num_files; i++)
 		{
@@ -1641,7 +1641,7 @@ void Client::ProcessData(u8 *data, u32 datasize, u16 sender_peer_id)
 			std::string sha1_hex = hex_encode(sha1_raw);
 			std::ostringstream tmp_os(std::ios_base::binary);
 			bool found_in_cache = m_media_cache.load_sha1(sha1_raw, tmp_os);
-			m_media_name_sha1_map.set(name, sha1_raw);
+			m_media_name_sha1_map[name] = sha1_raw;
 
 			// If found in cache, try to load it from there
 			if(found_in_cache)
@@ -1677,16 +1677,16 @@ void Client::ProcessData(u8 *data, u32 datasize, u16 sender_peer_id)
 			request_media(file_requests);
 		} else {
 			#if USE_CURL
-			core::list<MediaFetchThread*>::Iterator cur = m_media_fetch_threads.begin();
-			for(core::list<MediaRequest>::Iterator i = file_requests.begin();
-					i != file_requests.end(); i++) {
+			std::list<MediaFetchThread*>::iterator cur = m_media_fetch_threads.begin();
+			for(std::list<MediaRequest>::iterator i = file_requests.begin();
+					i != file_requests.end(); ++i) {
 				(*cur)->m_file_requests.push_back(*i);
 				cur++;
 				if (cur == m_media_fetch_threads.end())
 					cur = m_media_fetch_threads.begin();
 			}
-			for (core::list<MediaFetchThread*>::Iterator i = m_media_fetch_threads.begin();
-					i != m_media_fetch_threads.end(); i++) {
+			for (std::list<MediaFetchThread*>::iterator i = m_media_fetch_threads.begin();
+					i != m_media_fetch_threads.end(); ++i) {
 				(*i)->m_remote_url = remote_media;
 				(*i)->Start();
 			}
@@ -1762,9 +1762,9 @@ void Client::ProcessData(u8 *data, u32 datasize, u16 sender_peer_id)
 			}
 
 			{
-				core::map<std::string, std::string>::Node *n;
+				std::map<std::string, std::string>::iterator n;
 				n = m_media_name_sha1_map.find(name);
-				if(n == NULL)
+				if(n == m_media_name_sha1_map.end())
 					errorstream<<"The server sent a file that has not "
 							<<"been announced."<<std::endl;
 				else
@@ -1936,6 +1936,89 @@ void Client::ProcessData(u8 *data, u32 datasize, u16 sender_peer_id)
 		event.show_formspec.formname = new std::string(formname);
 		m_client_event_queue.push_back(event);
 	}
+	else if(command == TOCLIENT_SPAWN_PARTICLE)
+	{
+		std::string datastring((char*)&data[2], datasize-2);
+		std::istringstream is(datastring, std::ios_base::binary);
+
+		v3f pos = readV3F1000(is);
+		v3f vel = readV3F1000(is);
+		v3f acc = readV3F1000(is);
+		float expirationtime = readF1000(is);
+		float size = readF1000(is);
+		bool collisiondetection = readU8(is);
+		std::string texture = deSerializeLongString(is);
+
+		ClientEvent event;
+		event.type = CE_SPAWN_PARTICLE;
+		event.spawn_particle.pos = new v3f (pos);
+		event.spawn_particle.vel = new v3f (vel);
+		event.spawn_particle.acc = new v3f (acc);
+
+		event.spawn_particle.expirationtime = expirationtime;
+		event.spawn_particle.size = size;
+		event.add_particlespawner.collisiondetection =
+				collisiondetection;
+		event.spawn_particle.texture = new std::string(texture);
+
+		m_client_event_queue.push_back(event);
+	}
+	else if(command == TOCLIENT_ADD_PARTICLESPAWNER)
+	{
+		std::string datastring((char*)&data[2], datasize-2);
+		std::istringstream is(datastring, std::ios_base::binary);
+
+		u16 amount = readU16(is);
+		float spawntime = readF1000(is);
+		v3f minpos = readV3F1000(is);
+		v3f maxpos = readV3F1000(is);
+		v3f minvel = readV3F1000(is);
+		v3f maxvel = readV3F1000(is);
+		v3f minacc = readV3F1000(is);
+		v3f maxacc = readV3F1000(is);
+		float minexptime = readF1000(is);
+		float maxexptime = readF1000(is);
+		float minsize = readF1000(is);
+		float maxsize = readF1000(is);
+		bool collisiondetection = readU8(is);
+		std::string texture = deSerializeLongString(is);
+		u32 id = readU32(is);
+
+		ClientEvent event;
+		event.type = CE_ADD_PARTICLESPAWNER;
+		event.add_particlespawner.amount = amount;
+		event.add_particlespawner.spawntime = spawntime;
+
+		event.add_particlespawner.minpos = new v3f (minpos);
+		event.add_particlespawner.maxpos = new v3f (maxpos);
+		event.add_particlespawner.minvel = new v3f (minvel);
+		event.add_particlespawner.maxvel = new v3f (maxvel);
+		event.add_particlespawner.minacc = new v3f (minacc);
+		event.add_particlespawner.maxacc = new v3f (maxacc);
+
+		event.add_particlespawner.minexptime = minexptime;
+		event.add_particlespawner.maxexptime = maxexptime;
+		event.add_particlespawner.minsize = minsize;
+		event.add_particlespawner.maxsize = maxsize;
+		event.add_particlespawner.collisiondetection = collisiondetection;
+		event.add_particlespawner.texture = new std::string(texture);
+		event.add_particlespawner.id = id;
+
+		m_client_event_queue.push_back(event);
+	}
+	else if(command == TOCLIENT_DELETE_PARTICLESPAWNER)
+	{
+		std::string datastring((char*)&data[2], datasize-2);
+		std::istringstream is(datastring, std::ios_base::binary);
+
+		u32 id = readU16(is);
+
+		ClientEvent event;
+		event.type = CE_DELETE_PARTICLESPAWNER;
+		event.delete_particlespawner.id = id;
+
+		m_client_event_queue.push_back(event);
+	}
 	else
 	{
 		infostream<<"Client: Ignoring unknown command "
@@ -2231,7 +2314,7 @@ void Client::sendPlayerItem(u16 item)
 
 void Client::removeNode(v3s16 p)
 {
-	core::map<v3s16, MapBlock*> modified_blocks;
+	std::map<v3s16, MapBlock*> modified_blocks;
 
 	try
 	{
@@ -2245,12 +2328,11 @@ void Client::removeNode(v3s16 p)
 	// add urgent task to update the modified node
 	addUpdateMeshTaskForNode(p, false, true);
 
-	for(core::map<v3s16, MapBlock * >::Iterator
-			i = modified_blocks.getIterator();
-			i.atEnd() == false; i++)
+	for(std::map<v3s16, MapBlock * >::iterator
+			i = modified_blocks.begin();
+			i != modified_blocks.end(); ++i)
 	{
-		v3s16 p = i.getNode()->getKey();
-		addUpdateMeshTaskWithEdge(p);
+		addUpdateMeshTaskWithEdge(i->first);
 	}
 }
 
@@ -2258,7 +2340,7 @@ void Client::addNode(v3s16 p, MapNode n)
 {
 	TimeTaker timer1("Client::addNode()");
 
-	core::map<v3s16, MapBlock*> modified_blocks;
+	std::map<v3s16, MapBlock*> modified_blocks;
 
 	try
 	{
@@ -2268,12 +2350,11 @@ void Client::addNode(v3s16 p, MapNode n)
 	catch(InvalidPositionException &e)
 	{}
 	
-	for(core::map<v3s16, MapBlock * >::Iterator
-			i = modified_blocks.getIterator();
-			i.atEnd() == false; i++)
+	for(std::map<v3s16, MapBlock * >::iterator
+			i = modified_blocks.begin();
+			i != modified_blocks.end(); ++i)
 	{
-		v3s16 p = i.getNode()->getKey();
-		addUpdateMeshTaskWithEdge(p);
+		addUpdateMeshTaskWithEdge(i->first);
 	}
 }
 	
@@ -2373,7 +2454,7 @@ ClientActiveObject * Client::getSelectedActiveObject(
 		core::line3d<f32> shootline_on_map
 	)
 {
-	core::array<DistanceSortedActiveObject> objects;
+	std::vector<DistanceSortedActiveObject> objects;
 
 	m_env.getActiveObjects(from_pos_f_on_map, max_d, objects);
 
@@ -2381,7 +2462,7 @@ ClientActiveObject * Client::getSelectedActiveObject(
 	
 	// Sort them.
 	// After this, the closest object is the first in the array.
-	objects.sort();
+	std::sort(objects.begin(), objects.end());
 
 	for(u32 i=0; i<objects.size(); i++)
 	{
@@ -2420,13 +2501,13 @@ void Client::printDebugInfo(std::ostream &os)
 		<<std::endl;*/
 }
 
-core::list<std::wstring> Client::getConnectedPlayerNames()
+std::list<std::wstring> Client::getConnectedPlayerNames()
 {
-	core::list<Player*> players = m_env.getPlayers(true);
-	core::list<std::wstring> playerNames;
-	for(core::list<Player*>::Iterator
+	std::list<Player*> players = m_env.getPlayers(true);
+	std::list<std::wstring> playerNames;
+	for(std::list<Player*>::iterator
 			i = players.begin();
-			i != players.end(); i++)
+			i != players.end(); ++i)
 	{
 		Player *player = *i;
 		playerNames.push_back(narrow_to_wide(player->getName()));
diff --git a/src/client.h b/src/client.h
index 809e98b81c136fffb010542e215e2f95945a7056..d476a1d5189b7174ab9e93f6fc3b5835c0dffd85 100644
--- a/src/client.h
+++ b/src/client.h
@@ -36,6 +36,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include "server.h"
 #include "particles.h"
 #include "util/pointedthing.h"
+#include <algorithm>
 
 struct MeshMakeData;
 class MapBlockMesh;
@@ -142,9 +143,9 @@ class MediaFetchThread : public SimpleThread
 
 	void * Thread();
 
-	core::list<MediaRequest> m_file_requests;
+	std::list<MediaRequest> m_file_requests;
 	MutexedQueue<std::pair<std::string, std::string> > m_file_data;
-	core::list<MediaRequest> m_failed;
+	std::list<MediaRequest> m_failed;
 	std::string m_remote_url;
 	IGameDef *m_gamedef;
 };
@@ -156,7 +157,10 @@ enum ClientEventType
 	CE_PLAYER_FORCE_MOVE,
 	CE_DEATHSCREEN,
 	CE_TEXTURES_UPDATED,
-	CE_SHOW_FORMSPEC
+	CE_SHOW_FORMSPEC,
+	CE_SPAWN_PARTICLE,
+	CE_ADD_PARTICLESPAWNER,
+	CE_DELETE_PARTICLESPAWNER
 };
 
 struct ClientEvent
@@ -184,6 +188,35 @@ struct ClientEvent
 		} show_formspec;
 		struct{
 		} textures_updated;
+		struct{
+			v3f *pos;
+			v3f *vel;
+			v3f *acc;
+			f32 expirationtime;
+			f32 size;
+			bool collisiondetection;
+			std::string *texture;
+		} spawn_particle;
+		struct{
+			u16 amount;
+			f32 spawntime;
+			v3f *minpos;
+			v3f *maxpos;
+			v3f *minvel;
+			v3f *maxvel;
+			v3f *minacc;
+			v3f *maxacc;
+			f32 minexptime;
+			f32 maxexptime;
+			f32 minsize;
+			f32 maxsize;
+			bool collisiondetection;
+			std::string *texture;
+			u32 id;
+		} add_particlespawner;
+		struct{
+			u32 id;
+		} delete_particlespawner;
 	};
 };
 
@@ -282,7 +315,7 @@ class Client : public con::PeerHandler, public InventoryManager, public IGameDef
 	// Prints a line or two of info
 	void printDebugInfo(std::ostream &os);
 
-	core::list<std::wstring> getConnectedPlayerNames();
+	std::list<std::wstring> getConnectedPlayerNames();
 
 	float getAnimationTime();
 
@@ -347,7 +380,7 @@ class Client : public con::PeerHandler, public InventoryManager, public IGameDef
 	// Insert a media file appropriately into the appropriate manager
 	bool loadMedia(const std::string &data, const std::string &filename);
 
-	void request_media(const core::list<MediaRequest> &file_requests);
+	void request_media(const std::list<MediaRequest> &file_requests);
 
 	// Virtual methods from con::PeerHandler
 	void peerAdded(con::Peer *peer);
@@ -377,7 +410,7 @@ class Client : public con::PeerHandler, public InventoryManager, public IGameDef
 	MtEventManager *m_event;
 
 	MeshUpdateThread m_mesh_update_thread;
-	core::list<MediaFetchThread*> m_media_fetch_threads;
+	std::list<MediaFetchThread*> m_media_fetch_threads;
 	ClientEnvironment m_env;
 	con::Connection m_con;
 	IrrlichtDevice *m_device;
@@ -387,7 +420,7 @@ class Client : public con::PeerHandler, public InventoryManager, public IGameDef
 	bool m_inventory_updated;
 	Inventory *m_inventory_from_server;
 	float m_inventory_from_server_age;
-	core::map<v3s16, bool> m_active_blocks;
+	std::set<v3s16> m_active_blocks;
 	PacketCounter m_packetcounter;
 	// Block mesh animation parameters
 	float m_animation_time;
@@ -405,7 +438,7 @@ class Client : public con::PeerHandler, public InventoryManager, public IGameDef
 	Queue<ClientEvent> m_client_event_queue;
 	FileCache m_media_cache;
 	// Mapping from media file name to SHA1 checksum
-	core::map<std::string, std::string> m_media_name_sha1_map;
+	std::map<std::string, std::string> m_media_name_sha1_map;
 	bool m_media_receive_started;
 	u32 m_media_count;
 	u32 m_media_received_count;
diff --git a/src/clientmap.cpp b/src/clientmap.cpp
index aa92dfdee661f2d5c21172fb70793b5eaebbdf82..c0806836704e11d10a483277ea428e5efe55877c 100644
--- a/src/clientmap.cpp
+++ b/src/clientmap.cpp
@@ -30,6 +30,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include "profiler.h"
 #include "settings.h"
 #include "util/mathconstants.h"
+#include <algorithm>
 
 #define PP(x) "("<<(x).X<<","<<(x).Y<<","<<(x).Z<<")"
 
@@ -83,7 +84,7 @@ MapSector * ClientMap::emergeSector(v2s16 p2d)
 	
 	{
 		//JMutexAutoLock lock(m_sector_mutex); // Bulk comment-out
-		m_sectors.insert(p2d, sector);
+		m_sectors[p2d] = sector;
 	}
 	
 	return sector;
@@ -164,11 +165,11 @@ void ClientMap::updateDrawList(video::IVideoDriver* driver)
 
 	INodeDefManager *nodemgr = m_gamedef->ndef();
 
-	for(core::map<v3s16, MapBlock*>::Iterator
-			i = m_drawlist.getIterator();
-			i.atEnd() == false; i++)
+	for(std::map<v3s16, MapBlock*>::iterator
+			i = m_drawlist.begin();
+			i != m_drawlist.end(); ++i)
 	{
-		MapBlock *block = i.getNode()->getValue();
+		MapBlock *block = i->second;
 		block->refDrop();
 	}
 	m_drawlist.clear();
@@ -215,11 +216,11 @@ void ClientMap::updateDrawList(video::IVideoDriver* driver)
 	// Blocks from which stuff was actually drawn
 	//u32 blocks_without_stuff = 0;
 
-	for(core::map<v2s16, MapSector*>::Iterator
-			si = m_sectors.getIterator();
-			si.atEnd() == false; si++)
+	for(std::map<v2s16, MapSector*>::iterator
+			si = m_sectors.begin();
+			si != m_sectors.end(); ++si)
 	{
-		MapSector *sector = si.getNode()->getValue();
+		MapSector *sector = si->second;
 		v2s16 sp = sector->getPos();
 		
 		if(m_control.range_all == false)
@@ -231,7 +232,7 @@ void ClientMap::updateDrawList(video::IVideoDriver* driver)
 				continue;
 		}
 
-		core::list< MapBlock * > sectorblocks;
+		std::list< MapBlock * > sectorblocks;
 		sector->getBlocks(sectorblocks);
 		
 		/*
@@ -240,7 +241,7 @@ void ClientMap::updateDrawList(video::IVideoDriver* driver)
 
 		u32 sector_blocks_drawn = 0;
 		
-		core::list< MapBlock * >::Iterator i;
+		std::list< MapBlock * >::iterator i;
 		for(i=sectorblocks.begin(); i!=sectorblocks.end(); i++)
 		{
 			MapBlock *block = *i;
@@ -350,7 +351,7 @@ void ClientMap::updateDrawList(video::IVideoDriver* driver)
 		} // foreach sectorblocks
 
 		if(sector_blocks_drawn != 0)
-			m_last_drawn_sectors[sp] = true;
+			m_last_drawn_sectors.insert(sp);
 	}
 
 	m_control.blocks_would_have_drawn = blocks_would_have_drawn;
@@ -368,12 +369,12 @@ void ClientMap::updateDrawList(video::IVideoDriver* driver)
 struct MeshBufList
 {
 	video::SMaterial m;
-	core::list<scene::IMeshBuffer*> bufs;
+	std::list<scene::IMeshBuffer*> bufs;
 };
 
 struct MeshBufListList
 {
-	core::list<MeshBufList> lists;
+	std::list<MeshBufList> lists;
 	
 	void clear()
 	{
@@ -382,8 +383,8 @@ struct MeshBufListList
 	
 	void add(scene::IMeshBuffer *buf)
 	{
-		for(core::list<MeshBufList>::Iterator i = lists.begin();
-				i != lists.end(); i++){
+		for(std::list<MeshBufList>::iterator i = lists.begin();
+				i != lists.end(); ++i){
 			MeshBufList &l = *i;
 			if(l.m == buf->getMaterial()){
 				l.bufs.push_back(buf);
@@ -487,11 +488,11 @@ void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass)
 
 	MeshBufListList drawbufs;
 
-	for(core::map<v3s16, MapBlock*>::Iterator
-			i = m_drawlist.getIterator();
-			i.atEnd() == false; i++)
+	for(std::map<v3s16, MapBlock*>::iterator
+			i = m_drawlist.begin();
+			i != m_drawlist.end(); ++i)
 	{
-		MapBlock *block = i.getNode()->getValue();
+		MapBlock *block = i->second;
 
 		// If the mesh of the block happened to get deleted, ignore it
 		if(block->mesh == NULL)
@@ -569,11 +570,11 @@ void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass)
 		}
 	}
 	
-	core::list<MeshBufList> &lists = drawbufs.lists;
+	std::list<MeshBufList> &lists = drawbufs.lists;
 	
 	int timecheck_counter = 0;
-	for(core::list<MeshBufList>::Iterator i = lists.begin();
-			i != lists.end(); i++)
+	for(std::list<MeshBufList>::iterator i = lists.begin();
+			i != lists.end(); ++i)
 	{
 		{
 			timecheck_counter++;
@@ -595,8 +596,8 @@ void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass)
 		
 		driver->setMaterial(list.m);
 		
-		for(core::list<scene::IMeshBuffer*>::Iterator j = list.bufs.begin();
-				j != list.bufs.end(); j++)
+		for(std::list<scene::IMeshBuffer*>::iterator j = list.bufs.begin();
+				j != list.bufs.end(); ++j)
 		{
 			scene::IMeshBuffer *buf = *j;
 			driver->drawMeshBuffer(buf);
@@ -769,7 +770,7 @@ int ClientMap::getBackgroundBrightness(float max_d, u32 daylight_factor,
 	float sunlight_min_d = max_d*0.8;
 	if(sunlight_min_d > 35*BS)
 		sunlight_min_d = 35*BS;
-	core::array<int> values;
+	std::vector<int> values;
 	for(u32 i=0; i<sizeof(z_directions)/sizeof(*z_directions); i++){
 		v3f z_dir = z_directions[i];
 		z_dir.normalize();
@@ -798,7 +799,7 @@ int ClientMap::getBackgroundBrightness(float max_d, u32 daylight_factor,
 	}
 	int brightness_sum = 0;
 	int brightness_count = 0;
-	values.sort();
+	std::sort(values.begin(), values.end());
 	u32 num_values_to_use = values.size();
 	if(num_values_to_use >= 10)
 		num_values_to_use -= num_values_to_use/2;
diff --git a/src/clientmap.h b/src/clientmap.h
index 786f35b77f0a7ecd73dfac6b73e59d6438a27ded..7f63704d3cb9f30188d0ebb18e562df2c7be2143 100644
--- a/src/clientmap.h
+++ b/src/clientmap.h
@@ -22,6 +22,8 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 
 #include "irrlichttypes_extrabloated.h"
 #include "map.h"
+#include <set>
+#include <map>
 
 struct MapDrawControl
 {
@@ -128,7 +130,7 @@ class ClientMap : public Map, public scene::ISceneNode
 	// Check if sector was drawn on last render()
 	bool sectorWasDrawn(v2s16 p)
 	{
-		return (m_last_drawn_sectors.find(p) != NULL);
+		return (m_last_drawn_sectors.find(p) != m_last_drawn_sectors.end());
 	}
 	
 private:
@@ -143,9 +145,9 @@ class ClientMap : public Map, public scene::ISceneNode
 	f32 m_camera_fov;
 	JMutex m_camera_mutex;
 
-	core::map<v3s16, MapBlock*> m_drawlist;
+	std::map<v3s16, MapBlock*> m_drawlist;
 	
-	core::map<v2s16, bool> m_last_drawn_sectors;
+	std::set<v2s16> m_last_drawn_sectors;
 };
 
 #endif
diff --git a/src/clientobject.cpp b/src/clientobject.cpp
index e7c735dac03d2ce95e42296c2b77494cafc2636a..37f693c5e1310d0b4306fa9ff720b684db1e9adf 100644
--- a/src/clientobject.cpp
+++ b/src/clientobject.cpp
@@ -43,9 +43,9 @@ ClientActiveObject* ClientActiveObject::create(u8 type, IGameDef *gamedef,
 		ClientEnvironment *env)
 {
 	// Find factory function
-	core::map<u16, Factory>::Node *n;
+	std::map<u16, Factory>::iterator n;
 	n = m_types.find(type);
-	if(n == NULL)
+	if(n == m_types.end())
 	{
 		// If factory is not found, just return.
 		dstream<<"WARNING: ClientActiveObject: No factory for type="
@@ -53,18 +53,18 @@ ClientActiveObject* ClientActiveObject::create(u8 type, IGameDef *gamedef,
 		return NULL;
 	}
 
-	Factory f = n->getValue();
+	Factory f = n->second;
 	ClientActiveObject *object = (*f)(gamedef, env);
 	return object;
 }
 
 void ClientActiveObject::registerType(u16 type, Factory f)
 {
-	core::map<u16, Factory>::Node *n;
+	std::map<u16, Factory>::iterator n;
 	n = m_types.find(type);
-	if(n)
+	if(n != m_types.end())
 		return;
-	m_types.insert(type, f);
+	m_types[type] = f;
 }
 
 
diff --git a/src/clientobject.h b/src/clientobject.h
index d1ee366cfebaee991a45ddd608280b0e5934384e..8cbf3d60ba7880c9d79723cd3d1741d8c35002d1 100644
--- a/src/clientobject.h
+++ b/src/clientobject.h
@@ -22,6 +22,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 
 #include "irrlichttypes_extrabloated.h"
 #include "activeobject.h"
+#include <map>
 
 /*
 
@@ -96,7 +97,7 @@ class ClientActiveObject : public ActiveObject
 	ClientEnvironment *m_env;
 private:
 	// Used for creating objects based on type
-	static core::map<u16, Factory> m_types;
+	static std::map<u16, Factory> m_types;
 };
 
 struct DistanceSortedActiveObject
@@ -110,7 +111,7 @@ struct DistanceSortedActiveObject
 		d = a_d;
 	}
 
-	bool operator < (DistanceSortedActiveObject &other)
+	bool operator < (const DistanceSortedActiveObject &other) const
 	{
 		return d < other.d;
 	}
diff --git a/src/clientserver.h b/src/clientserver.h
index 769272a68a7db1344b1bef014830de360dfc7cc6..28b57997195a1e79093478fcaf66f189a020f698 100644
--- a/src/clientserver.h
+++ b/src/clientserver.h
@@ -79,9 +79,18 @@ SharedBuffer<u8> makePacket_TOCLIENT_TIME_OF_DAY(u16 time, float time_speed);
 		Serialization format changes
 	PROTOCOL_VERSION 16:
 		TOCLIENT_SHOW_FORMSPEC
+	PROTOCOL_VERSION 17:
+		Serialization format change: include backface_culling flag in TileDef
+		Added rightclickable field in nodedef
+		TOCLIENT_SPAWN_PARTICLE
+		TOCLIENT_ADD_PARTICLESPAWNER
+		TOCLIENT_DELETE_PARTICLESPAWNER
+	PROTOCOL_VERSION 18:
+		damageGroups added to ToolCapabilities
+		sound_place added to ItemDefinition
 */
 
-#define LATEST_PROTOCOL_VERSION 16
+#define LATEST_PROTOCOL_VERSION 18
 
 // Server's supported network protocol range
 #define SERVER_PROTOCOL_VERSION_MIN 13
@@ -356,6 +365,7 @@ enum ToClientCommand
 		u8[len] name
 		[2] serialized inventory
 	*/
+
 	TOCLIENT_SHOW_FORMSPEC = 0x44,
 	/*
 		[0] u16 command
@@ -381,6 +391,46 @@ enum ToClientCommand
 		f1000 movement_liquid_sink
 		f1000 movement_gravity
 	*/
+
+	TOCLIENT_SPAWN_PARTICLE = 0x46,
+	/*
+		u16 command
+		v3f1000 pos
+		v3f1000 velocity
+		v3f1000 acceleration
+		f1000 expirationtime
+		f1000 size
+		u8 bool collisiondetection
+		u32 len
+		u8[len] texture
+	*/
+
+	TOCLIENT_ADD_PARTICLESPAWNER = 0x47,
+	/*
+		u16 command
+		u16 amount
+		f1000 spawntime
+		v3f1000 minpos
+		v3f1000 maxpos
+		v3f1000 minvel
+		v3f1000 maxvel
+		v3f1000 minacc
+		v3f1000 maxacc
+		f1000 minexptime
+		f1000 maxexptime
+		f1000 minsize
+		f1000 maxsize
+		u8 bool collisiondetection
+		u32 len
+		u8[len] texture
+		u32 id
+	*/
+
+	TOCLIENT_DELETE_PARTICLESPAWNER = 0x48,
+	/*
+		u16 command
+		u32 id
+	*/
 };
 
 enum ToServerCommand
diff --git a/src/clouds.cpp b/src/clouds.cpp
index 9f0bc06d8fc22a187ebd5e736f9aad51dcd80c80..55ec8965ad6683b8b146f4ec7e907702c790d66c 100644
--- a/src/clouds.cpp
+++ b/src/clouds.cpp
@@ -29,7 +29,8 @@ Clouds::Clouds(
 		scene::ISceneNode* parent,
 		scene::ISceneManager* mgr,
 		s32 id,
-		u32 seed
+		u32 seed,
+		s16 cloudheight
 ):
 	scene::ISceneNode(parent, mgr, id),
 	m_seed(seed),
@@ -45,7 +46,8 @@ Clouds::Clouds(
 	//m_material.MaterialType = video::EMT_TRANSPARENT_VERTEX_ALPHA;
 	m_material.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL;
 
-	m_cloud_y = BS * g_settings->getS16("cloud_height");
+	m_cloud_y = BS * (cloudheight ? cloudheight :
+				g_settings->getS16("cloud_height"));
 
 	m_box = core::aabbox3d<f32>(-BS*1000000,m_cloud_y-BS,-BS*1000000,
 			BS*1000000,m_cloud_y+BS,BS*1000000);
diff --git a/src/clouds.h b/src/clouds.h
index 72923c2c5e9e19e3f0967039c4a984bbd2f31c03..8f8b19faf77c63bb58a0348f8a22e7bfeb8f384f 100644
--- a/src/clouds.h
+++ b/src/clouds.h
@@ -30,7 +30,8 @@ class Clouds : public scene::ISceneNode
 			scene::ISceneNode* parent,
 			scene::ISceneManager* mgr,
 			s32 id,
-			u32 seed
+			u32 seed,
+			s16 cloudheight=0
 	);
 
 	~Clouds();
diff --git a/src/collision.cpp b/src/collision.cpp
index 58517b779444f6e6ec9a9504ce2af521b8e88553..806a3b7203278cdd43a821c869e8a5848672e622 100644
--- a/src/collision.cpp
+++ b/src/collision.cpp
@@ -23,7 +23,10 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include "nodedef.h"
 #include "gamedef.h"
 #include "log.h"
+#include "environment.h"
+#include "serverobject.h"
 #include <vector>
+#include <set>
 #include "util/timetaker.h"
 #include "main.h" // g_profiler
 #include "profiler.h"
@@ -186,11 +189,12 @@ bool wouldCollideWithCeiling(
 }
 
 
-collisionMoveResult collisionMoveSimple(Map *map, IGameDef *gamedef,
+collisionMoveResult collisionMoveSimple(Environment *env, IGameDef *gamedef,
 		f32 pos_max_d, const aabb3f &box_0,
 		f32 stepheight, f32 dtime,
 		v3f &pos_f, v3f &speed_f, v3f &accel_f)
 {
+	Map *map = &env->getMap();
 	//TimeTaker tt("collisionMoveSimple");
     ScopeProfiler sp(g_profiler, "collisionMoveSimple avg", SPT_AVG);
 
@@ -215,6 +219,7 @@ collisionMoveResult collisionMoveSimple(Map *map, IGameDef *gamedef,
 	std::vector<aabb3f> cboxes;
 	std::vector<bool> is_unloaded;
 	std::vector<bool> is_step_up;
+	std::vector<bool> is_object;
 	std::vector<int> bouncy_values;
 	std::vector<v3s16> node_positions;
 	{
@@ -256,6 +261,7 @@ collisionMoveResult collisionMoveSimple(Map *map, IGameDef *gamedef,
 				is_step_up.push_back(false);
 				bouncy_values.push_back(n_bouncy_value);
 				node_positions.push_back(p);
+				is_object.push_back(false);
 			}
 		}
 		catch(InvalidPositionException &e)
@@ -267,14 +273,72 @@ collisionMoveResult collisionMoveSimple(Map *map, IGameDef *gamedef,
 			is_step_up.push_back(false);
 			bouncy_values.push_back(0);
 			node_positions.push_back(p);
+			is_object.push_back(false);
 		}
 	}
 	} // tt2
 
+	{
+		ScopeProfiler sp(g_profiler, "collisionMoveSimple objects avg", SPT_AVG);
+		//TimeTaker tt3("collisionMoveSimple collect object boxes");
+
+		/* add object boxes to cboxes */
+
+
+		std::list<ActiveObject*> objects;
+#ifndef SERVER
+		ClientEnvironment *c_env = dynamic_cast<ClientEnvironment*>(env);
+		if (c_env != 0)
+		{
+			f32 distance = speed_f.getLength();
+			std::vector<DistanceSortedActiveObject> clientobjects;
+			c_env->getActiveObjects(pos_f,distance * 1.5,clientobjects);
+			for (int i=0; i < clientobjects.size(); i++)
+			{
+				objects.push_back((ActiveObject*)clientobjects[i].obj);
+			}
+		}
+		else
+#endif
+		{
+			ServerEnvironment *s_env = dynamic_cast<ServerEnvironment*>(env);
+			if (s_env != 0)
+			{
+				f32 distance = speed_f.getLength();
+				std::set<u16> s_objects = s_env->getObjectsInsideRadius(pos_f,distance * 1.5);
+				for (std::set<u16>::iterator iter = s_objects.begin(); iter != s_objects.end(); iter++)
+				{
+					ServerActiveObject *current = s_env->getActiveObject(*iter);
+					objects.push_back((ActiveObject*)current);
+				}
+			}
+		}
+
+		for (std::list<ActiveObject*>::const_iterator iter = objects.begin();iter != objects.end(); ++iter)
+		{
+			ActiveObject *object = *iter;
+
+			if (object != NULL)
+			{
+				aabb3f object_collisionbox;
+				if (object->getCollisionBox(&object_collisionbox))
+				{
+					cboxes.push_back(object_collisionbox);
+					is_unloaded.push_back(false);
+					is_step_up.push_back(false);
+					bouncy_values.push_back(0);
+					node_positions.push_back(v3s16(0,0,0));
+					is_object.push_back(true);
+				}
+			}
+		}
+	} //tt3
+
 	assert(cboxes.size() == is_unloaded.size());
 	assert(cboxes.size() == is_step_up.size());
 	assert(cboxes.size() == bouncy_values.size());
 	assert(cboxes.size() == node_positions.size());
+	assert(cboxes.size() == is_object.size());
 
 	/*
 		Collision detection
@@ -386,7 +450,11 @@ collisionMoveResult collisionMoveSimple(Map *map, IGameDef *gamedef,
 				is_collision = false;
 
 			CollisionInfo info;
-			info.type = COLLISION_NODE;
+			if (is_object[nearest_boxindex]) {
+				info.type = COLLISION_OBJECT;
+			}
+			else
+				info.type = COLLISION_NODE;
 			info.node_p = node_positions[nearest_boxindex];
 			info.bouncy = bouncy;
 			info.old_speed = speed_f;
diff --git a/src/collision.h b/src/collision.h
index 38cc3efb39c00149772f1b077ad7aabe28b18b02..1178184567a3821f8a3d84397000fc8f908b0f85 100644
--- a/src/collision.h
+++ b/src/collision.h
@@ -25,10 +25,12 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 
 class Map;
 class IGameDef;
+class Environment;
 
 enum CollisionType
 {
-	COLLISION_NODE
+	COLLISION_NODE,
+	COLLISION_OBJECT,
 };
 
 struct CollisionInfo
@@ -65,7 +67,7 @@ struct collisionMoveResult
 };
 
 // Moves using a single iteration; speed should not exceed pos_max_d/dtime
-collisionMoveResult collisionMoveSimple(Map *map, IGameDef *gamedef,
+collisionMoveResult collisionMoveSimple(Environment *env,IGameDef *gamedef,
 		f32 pos_max_d, const aabb3f &box_0,
 		f32 stepheight, f32 dtime,
 		v3f &pos_f, v3f &speed_f, v3f &accel_f);
diff --git a/src/connection.cpp b/src/connection.cpp
index 7a3018bfdb3a19c311217dccb8bd633acea715da..7bff5b113ede227a81c2b1cf89866c2772351fd8 100644
--- a/src/connection.cpp
+++ b/src/connection.cpp
@@ -76,19 +76,20 @@ SharedBuffer<u8> makeOriginalPacket(
 	return b;
 }
 
-core::list<SharedBuffer<u8> > makeSplitPacket(
+std::list<SharedBuffer<u8> > makeSplitPacket(
 		SharedBuffer<u8> data,
 		u32 chunksize_max,
 		u16 seqnum)
 {
 	// Chunk packets, containing the TYPE_SPLIT header
-	core::list<SharedBuffer<u8> > chunks;
+	std::list<SharedBuffer<u8> > chunks;
 	
 	u32 chunk_header_size = 7;
 	u32 maximum_data_size = chunksize_max - chunk_header_size;
 	u32 start = 0;
 	u32 end = 0;
 	u32 chunk_num = 0;
+	u16 chunk_count = 0;
 	do{
 		end = start + maximum_data_size - 1;
 		if(end > data.getSize() - 1)
@@ -106,16 +107,15 @@ core::list<SharedBuffer<u8> > makeSplitPacket(
 		memcpy(&chunk[chunk_header_size], &data[start], payload_size);
 
 		chunks.push_back(chunk);
+		chunk_count++;
 		
 		start = end + 1;
 		chunk_num++;
 	}
 	while(end != data.getSize() - 1);
 
-	u16 chunk_count = chunks.getSize();
-
-	core::list<SharedBuffer<u8> >::Iterator i = chunks.begin();
-	for(; i != chunks.end(); i++)
+	for(std::list<SharedBuffer<u8> >::iterator i = chunks.begin();
+		i != chunks.end(); ++i)
 	{
 		// Write chunk_count
 		writeU16(&((*i)[3]), chunk_count);
@@ -124,13 +124,13 @@ core::list<SharedBuffer<u8> > makeSplitPacket(
 	return chunks;
 }
 
-core::list<SharedBuffer<u8> > makeAutoSplitPacket(
+std::list<SharedBuffer<u8> > makeAutoSplitPacket(
 		SharedBuffer<u8> data,
 		u32 chunksize_max,
 		u16 &split_seqnum)
 {
 	u32 original_header_size = 1;
-	core::list<SharedBuffer<u8> > list;
+	std::list<SharedBuffer<u8> > list;
 	if(data.getSize() + original_header_size > chunksize_max)
 	{
 		list = makeSplitPacket(data, chunksize_max, split_seqnum);
@@ -170,11 +170,13 @@ SharedBuffer<u8> makeReliablePacket(
 	ReliablePacketBuffer
 */
 
+ReliablePacketBuffer::ReliablePacketBuffer(): m_list_size(0) {}
+
 void ReliablePacketBuffer::print()
 {
-	core::list<BufferedPacket>::Iterator i;
-	i = m_list.begin();
-	for(; i != m_list.end(); i++)
+	for(std::list<BufferedPacket>::iterator i = m_list.begin();
+		i != m_list.end();
+		++i)
 	{
 		u16 s = readU16(&(i->data[BASE_HEADER_SIZE+1]));
 		dout_con<<s<<" ";
@@ -186,13 +188,12 @@ bool ReliablePacketBuffer::empty()
 }
 u32 ReliablePacketBuffer::size()
 {
-	return m_list.getSize();
+	return m_list_size;
 }
 RPBSearchResult ReliablePacketBuffer::findPacket(u16 seqnum)
 {
-	core::list<BufferedPacket>::Iterator i;
-	i = m_list.begin();
-	for(; i != m_list.end(); i++)
+	std::list<BufferedPacket>::iterator i = m_list.begin();
+	for(; i != m_list.end(); ++i)
 	{
 		u16 s = readU16(&(i->data[BASE_HEADER_SIZE+1]));
 		/*dout_con<<"findPacket(): finding seqnum="<<seqnum
@@ -218,8 +219,8 @@ BufferedPacket ReliablePacketBuffer::popFirst()
 	if(empty())
 		throw NotFoundException("Buffer is empty");
 	BufferedPacket p = *m_list.begin();
-	core::list<BufferedPacket>::Iterator i = m_list.begin();
-	m_list.erase(i);
+	m_list.erase(m_list.begin());
+	--m_list_size;
 	return p;
 }
 BufferedPacket ReliablePacketBuffer::popSeqnum(u16 seqnum)
@@ -231,6 +232,7 @@ BufferedPacket ReliablePacketBuffer::popSeqnum(u16 seqnum)
 	}
 	BufferedPacket p = *r;
 	m_list.erase(r);
+	--m_list_size;
 	return p;
 }
 void ReliablePacketBuffer::insert(BufferedPacket &p)
@@ -240,6 +242,7 @@ void ReliablePacketBuffer::insert(BufferedPacket &p)
 	assert(type == TYPE_RELIABLE);
 	u16 seqnum = readU16(&p.data[BASE_HEADER_SIZE+1]);
 
+	++m_list_size;
 	// Find the right place for the packet and insert it there
 
 	// If list is empty, just add it
@@ -250,12 +253,12 @@ void ReliablePacketBuffer::insert(BufferedPacket &p)
 		return;
 	}
 	// Otherwise find the right place
-	core::list<BufferedPacket>::Iterator i;
-	i = m_list.begin();
+	std::list<BufferedPacket>::iterator i = m_list.begin();
 	// Find the first packet in the list which has a higher seqnum
-	for(; i != m_list.end(); i++){
+	for(; i != m_list.end(); ++i){
 		u16 s = readU16(&(i->data[BASE_HEADER_SIZE+1]));
 		if(s == seqnum){
+			--m_list_size;
 			throw AlreadyExistsException("Same seqnum in list");
 		}
 		if(seqnum_higher(s, seqnum)){
@@ -271,14 +274,14 @@ void ReliablePacketBuffer::insert(BufferedPacket &p)
 		return;
 	}
 	// Insert before i
-	m_list.insert_before(i, p);
+	m_list.insert(i, p);
 }
 
 void ReliablePacketBuffer::incrementTimeouts(float dtime)
 {
-	core::list<BufferedPacket>::Iterator i;
-	i = m_list.begin();
-	for(; i != m_list.end(); i++){
+	for(std::list<BufferedPacket>::iterator i = m_list.begin();
+		i != m_list.end(); ++i)
+	{
 		i->time += dtime;
 		i->totaltime += dtime;
 	}
@@ -286,9 +289,9 @@ void ReliablePacketBuffer::incrementTimeouts(float dtime)
 
 void ReliablePacketBuffer::resetTimedOuts(float timeout)
 {
-	core::list<BufferedPacket>::Iterator i;
-	i = m_list.begin();
-	for(; i != m_list.end(); i++){
+	for(std::list<BufferedPacket>::iterator i = m_list.begin();
+		i != m_list.end(); ++i)
+	{
 		if(i->time >= timeout)
 			i->time = 0.0;
 	}
@@ -296,21 +299,20 @@ void ReliablePacketBuffer::resetTimedOuts(float timeout)
 
 bool ReliablePacketBuffer::anyTotaltimeReached(float timeout)
 {
-	core::list<BufferedPacket>::Iterator i;
-	i = m_list.begin();
-	for(; i != m_list.end(); i++){
+	for(std::list<BufferedPacket>::iterator i = m_list.begin();
+		i != m_list.end(); ++i)
+	{
 		if(i->totaltime >= timeout)
 			return true;
 	}
 	return false;
 }
 
-core::list<BufferedPacket> ReliablePacketBuffer::getTimedOuts(float timeout)
+std::list<BufferedPacket> ReliablePacketBuffer::getTimedOuts(float timeout)
 {
-	core::list<BufferedPacket> timed_outs;
-	core::list<BufferedPacket>::Iterator i;
-	i = m_list.begin();
-	for(; i != m_list.end(); i++)
+	std::list<BufferedPacket> timed_outs;
+	for(std::list<BufferedPacket>::iterator i = m_list.begin();
+		i != m_list.end(); ++i)
 	{
 		if(i->time >= timeout)
 			timed_outs.push_back(*i);
@@ -324,11 +326,10 @@ core::list<BufferedPacket> ReliablePacketBuffer::getTimedOuts(float timeout)
 
 IncomingSplitBuffer::~IncomingSplitBuffer()
 {
-	core::map<u16, IncomingSplitPacket*>::Iterator i;
-	i = m_buf.getIterator();
-	for(; i.atEnd() == false; i++)
+	for(std::map<u16, IncomingSplitPacket*>::iterator i = m_buf.begin();
+		i != m_buf.end(); ++i)
 	{
-		delete i.getNode()->getValue();
+		delete i->second;
 	}
 }
 /*
@@ -346,7 +347,7 @@ SharedBuffer<u8> IncomingSplitBuffer::insert(BufferedPacket &p, bool reliable)
 	u16 chunk_num = readU16(&p.data[BASE_HEADER_SIZE+5]);
 
 	// Add if doesn't exist
-	if(m_buf.find(seqnum) == NULL)
+	if(m_buf.find(seqnum) == m_buf.end())
 	{
 		IncomingSplitPacket *sp = new IncomingSplitPacket();
 		sp->chunk_count = chunk_count;
@@ -369,7 +370,7 @@ SharedBuffer<u8> IncomingSplitBuffer::insert(BufferedPacket &p, bool reliable)
 	// If chunk already exists, ignore it.
 	// Sometimes two identical packets may arrive when there is network
 	// lag and the server re-sends stuff.
-	if(sp->chunks.find(chunk_num) != NULL)
+	if(sp->chunks.find(chunk_num) != sp->chunks.end())
 		return SharedBuffer<u8>();
 	
 	// Cut chunk data out of packet
@@ -386,11 +387,10 @@ SharedBuffer<u8> IncomingSplitBuffer::insert(BufferedPacket &p, bool reliable)
 
 	// Calculate total size
 	u32 totalsize = 0;
-	core::map<u16, SharedBuffer<u8> >::Iterator i;
-	i = sp->chunks.getIterator();
-	for(; i.atEnd() == false; i++)
+	for(std::map<u16, SharedBuffer<u8> >::iterator i = sp->chunks.begin();
+		i != sp->chunks.end(); ++i)
 	{
-		totalsize += i.getNode()->getValue().getSize();
+		totalsize += i->second.getSize();
 	}
 	
 	SharedBuffer<u8> fulldata(totalsize);
@@ -407,34 +407,32 @@ SharedBuffer<u8> IncomingSplitBuffer::insert(BufferedPacket &p, bool reliable)
 	}
 
 	// Remove sp from buffer
-	m_buf.remove(seqnum);
+	m_buf.erase(seqnum);
 	delete sp;
 
 	return fulldata;
 }
 void IncomingSplitBuffer::removeUnreliableTimedOuts(float dtime, float timeout)
 {
-	core::list<u16> remove_queue;
-	core::map<u16, IncomingSplitPacket*>::Iterator i;
-	i = m_buf.getIterator();
-	for(; i.atEnd() == false; i++)
+	std::list<u16> remove_queue;
+	for(std::map<u16, IncomingSplitPacket*>::iterator i = m_buf.begin();
+		i != m_buf.end(); ++i)
 	{
-		IncomingSplitPacket *p = i.getNode()->getValue();
+		IncomingSplitPacket *p = i->second;
 		// Reliable ones are not removed by timeout
 		if(p->reliable == true)
 			continue;
 		p->time += dtime;
 		if(p->time >= timeout)
-			remove_queue.push_back(i.getNode()->getKey());
+			remove_queue.push_back(i->first);
 	}
-	core::list<u16>::Iterator j;
-	j = remove_queue.begin();
-	for(; j != remove_queue.end(); j++)
+	for(std::list<u16>::iterator j = remove_queue.begin();
+		j != remove_queue.end(); ++j)
 	{
 		dout_con<<"NOTE: Removing timed out unreliable split packet"
 				<<std::endl;
 		delete m_buf[*j];
-		m_buf.remove(*j);
+		m_buf.erase(*j);
 	}
 }
 
@@ -556,12 +554,11 @@ Connection::~Connection()
 {
 	stop();
 	// Delete peers
-	for(core::map<u16, Peer*>::Iterator
-			j = m_peers.getIterator();
-			j.atEnd() == false; j++)
+	for(std::map<u16, Peer*>::iterator
+			j = m_peers.begin();
+			j != m_peers.end(); ++j)
 	{
-		Peer *peer = j.getNode()->getValue();
-		delete peer;
+		delete j->second;
 	}
 }
 
@@ -591,7 +588,7 @@ void * Connection::Thread()
 		
 		runTimeouts(dtime);
 
-		while(m_command_queue.size() != 0){
+		while(!m_command_queue.empty()){
 			ConnectionCommand c = m_command_queue.pop_front();
 			processCommand(c);
 		}
@@ -648,18 +645,18 @@ void Connection::processCommand(ConnectionCommand &c)
 
 void Connection::send(float dtime)
 {
-	for(core::map<u16, Peer*>::Iterator
-			j = m_peers.getIterator();
-			j.atEnd() == false; j++)
+	for(std::map<u16, Peer*>::iterator
+			j = m_peers.begin();
+			j != m_peers.end(); ++j)
 	{
-		Peer *peer = j.getNode()->getValue();
+		Peer *peer = j->second;
 		peer->m_sendtime_accu += dtime;
 		peer->m_num_sent = 0;
 		peer->m_max_num_sent = peer->m_sendtime_accu *
 				peer->m_max_packets_per_second;
 	}
 	Queue<OutgoingPacket> postponed_packets;
-	while(m_outgoing_queue.size() != 0){
+	while(!m_outgoing_queue.empty()){
 		OutgoingPacket packet = m_outgoing_queue.pop_front();
 		Peer *peer = getPeerNoEx(packet.peer_id);
 		if(!peer)
@@ -674,14 +671,14 @@ void Connection::send(float dtime)
 			postponed_packets.push_back(packet);
 		}
 	}
-	while(postponed_packets.size() != 0){
+	while(!postponed_packets.empty()){
 		m_outgoing_queue.push_back(postponed_packets.pop_front());
 	}
-	for(core::map<u16, Peer*>::Iterator
-			j = m_peers.getIterator();
-			j.atEnd() == false; j++)
+	for(std::map<u16, Peer*>::iterator
+			j = m_peers.begin();
+			j != m_peers.end(); ++j)
 	{
-		Peer *peer = j.getNode()->getValue();
+		Peer *peer = j->second;
 		peer->m_sendtime_accu -= (float)peer->m_num_sent /
 				peer->m_max_packets_per_second;
 		if(peer->m_sendtime_accu > 10. / peer->m_max_packets_per_second)
@@ -751,11 +748,11 @@ void Connection::receive()
 				Allow only entries that have has_sent_with_id==false.
 			*/
 
-			core::map<u16, Peer*>::Iterator j;
-			j = m_peers.getIterator();
-			for(; j.atEnd() == false; j++)
+			std::map<u16, Peer*>::iterator j;
+			j = m_peers.begin();
+			for(; j != m_peers.end(); ++j)
 			{
-				Peer *peer = j.getNode()->getValue();
+				Peer *peer = j->second;
 				if(peer->has_sent_with_id)
 					continue;
 				if(peer->address == sender)
@@ -766,14 +763,14 @@ void Connection::receive()
 				If no peer was found with the same address and port,
 				we shall assume it is a new peer and create an entry.
 			*/
-			if(j.atEnd())
+			if(j == m_peers.end())
 			{
 				// Pass on to adding the peer
 			}
 			// Else: A peer was found.
 			else
 			{
-				Peer *peer = j.getNode()->getValue();
+				Peer *peer = j->second;
 				peer_id = peer->id;
 				PrintInfo(derr_con);
 				derr_con<<"WARNING: Assuming unknown peer to be "
@@ -797,7 +794,7 @@ void Connection::receive()
 			for(;;)
 			{
 				// Check if exists
-				if(m_peers.find(peer_id_new) == NULL)
+				if(m_peers.find(peer_id_new) == m_peers.end())
 					break;
 				// Check for overflow
 				if(peer_id_new == 65535){
@@ -817,7 +814,7 @@ void Connection::receive()
 
 			// Create a peer
 			Peer *peer = new Peer(peer_id_new, sender);
-			m_peers.insert(peer->id, peer);
+			m_peers[peer->id] = peer;
 			
 			// Create peer addition event
 			ConnectionEvent e;
@@ -837,9 +834,9 @@ void Connection::receive()
 			// Go on and process whatever it sent
 		}
 
-		core::map<u16, Peer*>::Node *node = m_peers.find(peer_id);
+		std::map<u16, Peer*>::iterator node = m_peers.find(peer_id);
 
-		if(node == NULL)
+		if(node == m_peers.end())
 		{
 			// Peer not found
 			// This means that the peer id of the sender is not PEER_ID_INEXISTENT
@@ -849,7 +846,7 @@ void Connection::receive()
 			throw InvalidIncomingDataException("Peer not found (possible timeout)");
 		}
 
-		Peer *peer = node->getValue();
+		Peer *peer = node->second;
 
 		// Validate peer address
 		if(peer->address != sender)
@@ -902,12 +899,11 @@ void Connection::runTimeouts(float dtime)
 	float congestion_control_min_rate
 			= g_settings->getFloat("congestion_control_min_rate");
 
-	core::list<u16> timeouted_peers;
-	core::map<u16, Peer*>::Iterator j;
-	j = m_peers.getIterator();
-	for(; j.atEnd() == false; j++)
+	std::list<u16> timeouted_peers;
+	for(std::map<u16, Peer*>::iterator j = m_peers.begin();
+		j != m_peers.end(); ++j)
 	{
-		Peer *peer = j.getNode()->getValue();
+		Peer *peer = j->second;
 
 		// Update congestion control values
 		peer->congestion_control_aim_rtt = congestion_control_aim_rtt;
@@ -934,8 +930,7 @@ void Connection::runTimeouts(float dtime)
 		float resend_timeout = peer->resend_timeout;
 		for(u16 i=0; i<CHANNEL_COUNT; i++)
 		{
-			core::list<BufferedPacket> timed_outs;
-			core::list<BufferedPacket>::Iterator j;
+			std::list<BufferedPacket> timed_outs;
 			
 			Channel *channel = &peer->channels[i];
 
@@ -966,8 +961,8 @@ void Connection::runTimeouts(float dtime)
 
 			channel->outgoing_reliables.resetTimedOuts(resend_timeout);
 
-			j = timed_outs.begin();
-			for(; j != timed_outs.end(); j++)
+			for(std::list<BufferedPacket>::iterator j = timed_outs.begin();
+				j != timed_outs.end(); ++j)
 			{
 				u16 peer_id = readPeerId(*(j->data));
 				u8 channel = readChannel(*(j->data));
@@ -1012,8 +1007,8 @@ void Connection::runTimeouts(float dtime)
 	}
 
 	// Remove timed out peers
-	core::list<u16>::Iterator i = timeouted_peers.begin();
-	for(; i != timeouted_peers.end(); i++)
+	for(std::list<u16>::iterator i = timeouted_peers.begin();
+		i != timeouted_peers.end(); ++i)
 	{
 		PrintInfo(derr_con);
 		derr_con<<"RunTimeouts(): Removing peer "<<(*i)<<std::endl;
@@ -1041,13 +1036,13 @@ void Connection::connect(Address address)
 	dout_con<<getDesc()<<" connecting to "<<address.serializeString()
 			<<":"<<address.getPort()<<std::endl;
 
-	core::map<u16, Peer*>::Node *node = m_peers.find(PEER_ID_SERVER);
-	if(node != NULL){
+	std::map<u16, Peer*>::iterator node = m_peers.find(PEER_ID_SERVER);
+	if(node != m_peers.end()){
 		throw ConnectionException("Already connected to a server");
 	}
 
 	Peer *peer = new Peer(PEER_ID_SERVER, address);
-	m_peers.insert(peer->id, peer);
+	m_peers[peer->id] = peer;
 
 	// Create event
 	ConnectionEvent e;
@@ -1072,22 +1067,20 @@ void Connection::disconnect()
 	writeU8(&data[1], CONTROLTYPE_DISCO);
 	
 	// Send to all
-	core::map<u16, Peer*>::Iterator j;
-	j = m_peers.getIterator();
-	for(; j.atEnd() == false; j++)
+	for(std::map<u16, Peer*>::iterator j = m_peers.begin();
+		j != m_peers.end(); ++j)
 	{
-		Peer *peer = j.getNode()->getValue();
+		Peer *peer = j->second;
 		rawSendAsPacket(peer->id, 0, data, false);
 	}
 }
 
 void Connection::sendToAll(u8 channelnum, SharedBuffer<u8> data, bool reliable)
 {
-	core::map<u16, Peer*>::Iterator j;
-	j = m_peers.getIterator();
-	for(; j.atEnd() == false; j++)
+	for(std::map<u16, Peer*>::iterator j = m_peers.begin();
+		j != m_peers.end(); ++j)
 	{
-		Peer *peer = j.getNode()->getValue();
+		Peer *peer = j->second;
 		send(peer->id, channelnum, data, reliable);
 	}
 }
@@ -1108,13 +1101,12 @@ void Connection::send(u16 peer_id, u8 channelnum,
 	if(reliable)
 		chunksize_max -= RELIABLE_HEADER_SIZE;
 
-	core::list<SharedBuffer<u8> > originals;
+	std::list<SharedBuffer<u8> > originals;
 	originals = makeAutoSplitPacket(data, chunksize_max,
 			channel->next_outgoing_split_seqnum);
 	
-	core::list<SharedBuffer<u8> >::Iterator i;
-	i = originals.begin();
-	for(; i != originals.end(); i++)
+	for(std::list<SharedBuffer<u8> >::iterator i = originals.begin();
+		i != originals.end(); ++i)
 	{
 		SharedBuffer<u8> original = *i;
 		
@@ -1187,40 +1179,39 @@ void Connection::rawSend(const BufferedPacket &packet)
 
 Peer* Connection::getPeer(u16 peer_id)
 {
-	core::map<u16, Peer*>::Node *node = m_peers.find(peer_id);
+	std::map<u16, Peer*>::iterator node = m_peers.find(peer_id);
 
-	if(node == NULL){
+	if(node == m_peers.end()){
 		throw PeerNotFoundException("GetPeer: Peer not found (possible timeout)");
 	}
 
 	// Error checking
-	assert(node->getValue()->id == peer_id);
+	assert(node->second->id == peer_id);
 
-	return node->getValue();
+	return node->second;
 }
 
 Peer* Connection::getPeerNoEx(u16 peer_id)
 {
-	core::map<u16, Peer*>::Node *node = m_peers.find(peer_id);
+	std::map<u16, Peer*>::iterator node = m_peers.find(peer_id);
 
-	if(node == NULL){
+	if(node == m_peers.end()){
 		return NULL;
 	}
 
 	// Error checking
-	assert(node->getValue()->id == peer_id);
+	assert(node->second->id == peer_id);
 
-	return node->getValue();
+	return node->second;
 }
 
-core::list<Peer*> Connection::getPeers()
+std::list<Peer*> Connection::getPeers()
 {
-	core::list<Peer*> list;
-	core::map<u16, Peer*>::Iterator j;
-	j = m_peers.getIterator();
-	for(; j.atEnd() == false; j++)
+	std::list<Peer*> list;
+	for(std::map<u16, Peer*>::iterator j = m_peers.begin();
+		j != m_peers.end(); ++j)
 	{
-		Peer *peer = j.getNode()->getValue();
+		Peer *peer = j->second;
 		list.push_back(peer);
 	}
 	return list;
@@ -1228,11 +1219,10 @@ core::list<Peer*> Connection::getPeers()
 
 bool Connection::getFromBuffers(u16 &peer_id, SharedBuffer<u8> &dst)
 {
-	core::map<u16, Peer*>::Iterator j;
-	j = m_peers.getIterator();
-	for(; j.atEnd() == false; j++)
+	for(std::map<u16, Peer*>::iterator j = m_peers.begin();
+		j != m_peers.end(); ++j)
 	{
-		Peer *peer = j.getNode()->getValue();
+		Peer *peer = j->second;
 		for(u16 i=0; i<CHANNEL_COUNT; i++)
 		{
 			Channel *channel = &peer->channels[i];
@@ -1538,7 +1528,7 @@ SharedBuffer<u8> Connection::processPacket(Channel *channel,
 
 bool Connection::deletePeer(u16 peer_id, bool timeout)
 {
-	if(m_peers.find(peer_id) == NULL)
+	if(m_peers.find(peer_id) == m_peers.end())
 		return false;
 	
 	Peer *peer = m_peers[peer_id];
@@ -1549,7 +1539,7 @@ bool Connection::deletePeer(u16 peer_id, bool timeout)
 	putEvent(e);
 
 	delete m_peers[peer_id];
-	m_peers.remove(peer_id);
+	m_peers.erase(peer_id);
 	return true;
 }
 
@@ -1557,7 +1547,7 @@ bool Connection::deletePeer(u16 peer_id, bool timeout)
 
 ConnectionEvent Connection::getEvent()
 {
-	if(m_event_queue.size() == 0){
+	if(m_event_queue.empty()){
 		ConnectionEvent e;
 		e.type = CONNEVENT_NONE;
 		return e;
@@ -1602,8 +1592,8 @@ bool Connection::Connected()
 	if(m_peers.size() != 1)
 		return false;
 		
-	core::map<u16, Peer*>::Node *node = m_peers.find(PEER_ID_SERVER);
-	if(node == NULL)
+	std::map<u16, Peer*>::iterator node = m_peers.find(PEER_ID_SERVER);
+	if(node == m_peers.end())
 		return false;
 	
 	if(m_peer_id == PEER_ID_INEXISTENT)
diff --git a/src/connection.h b/src/connection.h
index 05b1ca2e860c0b3543b391d1b5fce72d6ddeb35d..486cf331fa755a9efe42242230ac4fb401ed6670 100644
--- a/src/connection.h
+++ b/src/connection.h
@@ -29,6 +29,8 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include "util/thread.h"
 #include <iostream>
 #include <fstream>
+#include <list>
+#include <map>
 
 namespace con
 {
@@ -142,14 +144,14 @@ SharedBuffer<u8> makeOriginalPacket(
 		SharedBuffer<u8> data);
 
 // Split data in chunks and add TYPE_SPLIT headers to them
-core::list<SharedBuffer<u8> > makeSplitPacket(
+std::list<SharedBuffer<u8> > makeSplitPacket(
 		SharedBuffer<u8> data,
 		u32 chunksize_max,
 		u16 seqnum);
 
 // Depending on size, make a TYPE_ORIGINAL or TYPE_SPLIT packet
 // Increments split_seqnum if a split packet is made
-core::list<SharedBuffer<u8> > makeAutoSplitPacket(
+std::list<SharedBuffer<u8> > makeAutoSplitPacket(
 		SharedBuffer<u8> data,
 		u32 chunksize_max,
 		u16 &split_seqnum);
@@ -167,7 +169,7 @@ struct IncomingSplitPacket
 		reliable = false;
 	}
 	// Key is chunk number, value is data without headers
-	core::map<u16, SharedBuffer<u8> > chunks;
+	std::map<u16, SharedBuffer<u8> > chunks;
 	u32 chunk_count;
 	float time; // Seconds from adding
 	bool reliable; // If true, isn't deleted on timeout
@@ -268,12 +270,12 @@ with a buffer in the receiving and transmitting end.
 	for fast access to the smallest one.
 */
 
-typedef core::list<BufferedPacket>::Iterator RPBSearchResult;
+typedef std::list<BufferedPacket>::iterator RPBSearchResult;
 
 class ReliablePacketBuffer
 {
 public:
-	
+	ReliablePacketBuffer();
 	void print();
 	bool empty();
 	u32 size();
@@ -286,10 +288,11 @@ class ReliablePacketBuffer
 	void incrementTimeouts(float dtime);
 	void resetTimedOuts(float timeout);
 	bool anyTotaltimeReached(float timeout);
-	core::list<BufferedPacket> getTimedOuts(float timeout);
+	std::list<BufferedPacket> getTimedOuts(float timeout);
 
 private:
-	core::list<BufferedPacket> m_list;
+	std::list<BufferedPacket> m_list;
+	u16 m_list_size;
 };
 
 /*
@@ -310,7 +313,7 @@ class IncomingSplitBuffer
 	
 private:
 	// Key is seqnum
-	core::map<u16, IncomingSplitPacket*> m_buf;
+	std::map<u16, IncomingSplitPacket*> m_buf;
 };
 
 class Connection;
@@ -589,7 +592,7 @@ class Connection: public SimpleThread
 	void rawSend(const BufferedPacket &packet);
 	Peer* getPeer(u16 peer_id);
 	Peer* getPeerNoEx(u16 peer_id);
-	core::list<Peer*> getPeers();
+	std::list<Peer*> getPeers();
 	bool getFromBuffers(u16 &peer_id, SharedBuffer<u8> &dst);
 	// Returns next data from a buffer if possible
 	// If found, returns true; if not, false.
@@ -619,7 +622,7 @@ class Connection: public SimpleThread
 	UDPSocket m_socket;
 	u16 m_peer_id;
 	
-	core::map<u16, Peer*> m_peers;
+	std::map<u16, Peer*> m_peers;
 	JMutex m_peers_mutex;
 
 	// Backwards compatibility
diff --git a/src/content_abm.cpp b/src/content_abm.cpp
index 03fc82ed4d8396f4710b84c1c6e489e9f90eb65f..ccd9ca19ce326884d10f46b4ac45d4fd39ba514c 100644
--- a/src/content_abm.cpp
+++ b/src/content_abm.cpp
@@ -94,11 +94,22 @@ class RemoveGrassABM : public ActiveBlockModifier
 class MakeTreesFromSaplingsABM : public ActiveBlockModifier
 {
 private:
+	content_t c_junglesapling;
+	content_t c_dirt;
+	content_t c_dirt_with_grass;
+	
 public:
+	MakeTreesFromSaplingsABM(ServerEnvironment *env, INodeDefManager *nodemgr) {
+		c_junglesapling   = nodemgr->getId("junglesapling");
+		c_dirt            = nodemgr->getId("mapgen_dirt");
+		c_dirt_with_grass = nodemgr->getId("mapgen_dirt_with_grass");
+	}
+
 	virtual std::set<std::string> getTriggerContents()
 	{
 		std::set<std::string> s;
 		s.insert("sapling");
+		s.insert("junglesapling");
 		return s;
 	}
 	virtual float getTriggerInterval()
@@ -111,37 +122,46 @@ class MakeTreesFromSaplingsABM : public ActiveBlockModifier
 		INodeDefManager *ndef = env->getGameDef()->ndef();
 		ServerMap *map = &env->getServerMap();
 		
-		actionstream<<"A sapling grows into a tree at "
-				<<PP(p)<<std::endl;
+		MapNode n_below = map->getNodeNoEx(p - v3s16(0, 1, 0));
+		if (n_below.getContent() != c_dirt &&
+			n_below.getContent() != c_dirt_with_grass)
+			return;
+			
+		bool is_jungle_tree = n.getContent() == c_junglesapling;
+		
+		actionstream <<"A " << (is_jungle_tree ? "jungle " : "")
+				<< "sapling grows into a tree at "
+				<< PP(p) << std::endl;
 
-		core::map<v3s16, MapBlock*> modified_blocks;
+		std::map<v3s16, MapBlock*> modified_blocks;
 		v3s16 tree_p = p;
 		ManualMapVoxelManipulator vmanip(map);
 		v3s16 tree_blockp = getNodeBlockPos(tree_p);
 		vmanip.initialEmerge(tree_blockp - v3s16(1,1,1), tree_blockp + v3s16(1,1,1));
-		bool is_apple_tree = myrand()%4 == 0;
-		treegen::make_tree(vmanip, tree_p, is_apple_tree, ndef, myrand());
+		
+		if (is_jungle_tree) {
+			treegen::make_jungletree(vmanip, tree_p, ndef, myrand());
+		} else {
+			bool is_apple_tree = myrand() % 4 == 0;
+			treegen::make_tree(vmanip, tree_p, is_apple_tree, ndef, myrand());
+		}
+		
 		vmanip.blitBackAll(&modified_blocks);
 
 		// update lighting
-		core::map<v3s16, MapBlock*> lighting_modified_blocks;
-		for(core::map<v3s16, MapBlock*>::Iterator
-			i = modified_blocks.getIterator();
-			i.atEnd() == false; i++)
-		{
-			lighting_modified_blocks.insert(i.getNode()->getKey(), i.getNode()->getValue());
-		}
+		std::map<v3s16, MapBlock*> lighting_modified_blocks;
+		lighting_modified_blocks.insert(modified_blocks.begin(), modified_blocks.end());
 		map->updateLighting(lighting_modified_blocks, modified_blocks);
 
 		// Send a MEET_OTHER event
 		MapEditEvent event;
 		event.type = MEET_OTHER;
-		for(core::map<v3s16, MapBlock*>::Iterator
-			i = modified_blocks.getIterator();
-			i.atEnd() == false; i++)
+//		event.modified_blocks.insert(modified_blocks.begin(), modified_blocks.end());
+		for(std::map<v3s16, MapBlock*>::iterator
+			i = modified_blocks.begin();
+			i != modified_blocks.end(); ++i)
 		{
-			v3s16 p = i.getNode()->getKey();
-			event.modified_blocks.insert(p, true);
+			event.modified_blocks.insert(i->first);
 		}
 		map->dispatchEvent(&event);
 	}
@@ -182,7 +202,7 @@ void add_legacy_abms(ServerEnvironment *env, INodeDefManager *nodedef)
 {
 	env->addActiveBlockModifier(new GrowGrassABM());
 	env->addActiveBlockModifier(new RemoveGrassABM());
-	env->addActiveBlockModifier(new MakeTreesFromSaplingsABM());
+	env->addActiveBlockModifier(new MakeTreesFromSaplingsABM(env, nodedef));
 	if (g_settings->getBool("liquid_finite"))
 		env->addActiveBlockModifier(new LiquidFlowABM(env, nodedef));
 }
diff --git a/src/content_cao.cpp b/src/content_cao.cpp
index 5e5cb38ae7bbb3578e9f15ad55a9777d6708d707..ee1009b6cb6e7cfcbdd00762a8232fe88de67373 100644
--- a/src/content_cao.cpp
+++ b/src/content_cao.cpp
@@ -50,7 +50,7 @@ struct ToolCapabilities;
 
 #define PP(x) "("<<(x).X<<","<<(x).Y<<","<<(x).Z<<")"
 
-core::map<u16, ClientActiveObject::Factory> ClientActiveObject::m_types;
+std::map<u16, ClientActiveObject::Factory> ClientActiveObject::m_types;
 
 /*
 	SmoothTranslator
@@ -174,6 +174,7 @@ class TestCAO : public ClientActiveObject
 
 	void processMessage(const std::string &data);
 
+	bool getCollisionBox(aabb3f *toset) { return false; }
 private:
 	scene::IMeshSceneNode *m_node;
 	v3f m_position;
@@ -329,6 +330,7 @@ class ItemCAO : public ClientActiveObject
 	std::string infoText()
 		{return m_infotext;}
 
+	bool getCollisionBox(aabb3f *toset) { return false; }
 private:
 	core::aabbox3d<f32> m_selection_box;
 	scene::IMeshSceneNode *m_node;
@@ -643,6 +645,22 @@ class GenericCAO : public ClientActiveObject
 			ClientActiveObject::registerType(getType(), create);
 	}
 
+	bool getCollisionBox(aabb3f *toset) {
+		if (m_prop.physical) {
+			aabb3f retval;
+			//update collision box
+			toset->MinEdge = m_prop.collisionbox.MinEdge * BS;
+			toset->MaxEdge = m_prop.collisionbox.MaxEdge * BS;
+
+			toset->MinEdge += m_position;
+			toset->MaxEdge += m_position;
+
+			return true;
+		}
+
+		return false;
+	}
+
 	void initialize(const std::string &data)
 	{
 		infostream<<"GenericCAO: Got init data"<<std::endl;
@@ -1127,8 +1145,7 @@ class GenericCAO : public ClientActiveObject
 				v3f p_pos = m_position;
 				v3f p_velocity = m_velocity;
 				v3f p_acceleration = m_acceleration;
-				IGameDef *gamedef = env->getGameDef();
-				moveresult = collisionMoveSimple(&env->getMap(), gamedef,
+				moveresult = collisionMoveSimple(env,env->getGameDef(),
 						pos_max_d, box, stepheight, dtime,
 						p_pos, p_velocity, p_acceleration);
 				// Apply results
diff --git a/src/content_mapblock.cpp b/src/content_mapblock.cpp
index 0d80dc17339d59073f8b3c7702a6b9e037f2ff98..84d5f067c90704c928b8e7985b44b346e8676839 100644
--- a/src/content_mapblock.cpp
+++ b/src/content_mapblock.cpp
@@ -42,14 +42,16 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 //              (compatible with ContentFeatures). If you specified 0,0,1,1
 //              for each face, that would be the same as passing NULL.
 void makeCuboid(MeshCollector *collector, const aabb3f &box,
-	const TileSpec *tiles, int tilecount,
+	TileSpec *tiles, int tilecount,
 	video::SColor &c, const f32* txc)
 {
 	assert(tilecount >= 1 && tilecount <= 6);
 
 	v3f min = box.MinEdge;
 	v3f max = box.MaxEdge;
-
+ 
+ 
+ 
 	if(txc == NULL)
 	{
 		static const f32 txc_default[24] = {
@@ -97,15 +99,70 @@ void makeCuboid(MeshCollector *collector, const aabb3f &box,
 		video::S3DVertex(min.X,min.Y,min.Z, 0,0,-1, c, txc[20],txc[23]),
 	};
 
-	for(s32 j=0; j<24; j++)
+	v2f t;
+	for(int i = 0; i < tilecount; i++)
+				{
+				switch (tiles[i].rotation)
+				{
+				case 0:
+					break;
+				case 1: //R90
+					for (int x = 0; x < 4; x++)
+						vertices[i*4+x].TCoords.rotateBy(90,irr::core::vector2df(0, 0));
+					break;
+				case 2: //R180
+					for (int x = 0; x < 4; x++)
+						vertices[i*4+x].TCoords.rotateBy(180,irr::core::vector2df(0, 0));
+					break;
+				case 3: //R270
+					for (int x = 0; x < 4; x++)
+						vertices[i*4+x].TCoords.rotateBy(270,irr::core::vector2df(0, 0));
+					break;
+				case 4: //FXR90
+					for (int x = 0; x < 4; x++)
+						vertices[i*4+x].TCoords.rotateBy(90,irr::core::vector2df(0, 0));
+
+					tiles[i].texture.pos.Y += tiles[i].texture.size.Y;
+					tiles[i].texture.size.Y *= -1;
+					break;
+				case 5: //FXR270
+					for (int x = 0; x < 4; x++)
+						vertices[i*4+x].TCoords.rotateBy(270,irr::core::vector2df(0, 0));
+					t=vertices[i*4].TCoords;
+					tiles[i].texture.pos.Y += tiles[i].texture.size.Y;
+					tiles[i].texture.size.Y *= -1;
+					break;
+				case 6: //FYR90
+					for (int x = 0; x < 4; x++)
+						vertices[i*4+x].TCoords.rotateBy(90,irr::core::vector2df(0, 0));
+					tiles[i].texture.pos.X += tiles[i].texture.size.X;
+					tiles[i].texture.size.X *= -1;
+					break;
+				case 7: //FYR270
+					for (int x = 0; x < 4; x++)
+						vertices[i*4+x].TCoords.rotateBy(270,irr::core::vector2df(0, 0));
+					tiles[i].texture.pos.X += tiles[i].texture.size.X;
+					tiles[i].texture.size.X *= -1;
+					break;
+				case 8: //FX
+					tiles[i].texture.pos.Y += tiles[i].texture.size.Y;
+					tiles[i].texture.size.Y *= -1;
+					break;
+				case 9: //FY
+					tiles[i].texture.pos.X += tiles[i].texture.size.X;
+					tiles[i].texture.size.X *= -1;
+					break;
+				default:
+					break;
+				}
+			}
+		for(s32 j=0; j<24; j++)
 	{
 		int tileindex = MYMIN(j/4, tilecount-1);
 		vertices[j].TCoords *= tiles[tileindex].texture.size;
 		vertices[j].TCoords += tiles[tileindex].texture.pos;
 	}
-
 	u16 indices[] = {0,1,2,2,3,0};
-
 	// Add to mesh collector
 	for(s32 j=0; j<24; j+=4)
 	{
@@ -160,18 +217,145 @@ void mapblock_mesh_generate_special(MeshMakeData *data,
 				Add water sources to mesh if using new style
 			*/
 			TileSpec tile_liquid = f.special_tiles[0];
+			TileSpec tile_liquid_bfculled = getNodeTile(n, p, v3s16(0,0,0), data);
 			AtlasPointer &pa_liquid = tile_liquid.texture;
 
-			bool top_is_air = false;
-			MapNode n = data->m_vmanip.getNodeNoEx(blockpos_nodes + v3s16(x,y+1,z));
-			if(n.getContent() == CONTENT_AIR)
-				top_is_air = true;
-			
-			if(top_is_air == false)
-				continue;
+			bool top_is_same_liquid = false;
+			MapNode ntop = data->m_vmanip.getNodeNoEx(blockpos_nodes + v3s16(x,y+1,z));
+			content_t c_flowing = nodedef->getId(f.liquid_alternative_flowing);
+			content_t c_source = nodedef->getId(f.liquid_alternative_source);
+			if(ntop.getContent() == c_flowing || ntop.getContent() == c_source)
+				top_is_same_liquid = true;
 
 			u16 l = getInteriorLight(n, 0, data);
 			video::SColor c = MapBlock_LightColor(f.alpha, l, decode_light(f.light_source));
+
+			/*
+				Generate sides
+			 */
+			v3s16 side_dirs[4] = {
+				v3s16(1,0,0),
+				v3s16(-1,0,0),
+				v3s16(0,0,1),
+				v3s16(0,0,-1),
+			};
+			for(u32 i=0; i<4; i++)
+			{
+				v3s16 dir = side_dirs[i];
+
+				MapNode neighbor = data->m_vmanip.getNodeNoEx(blockpos_nodes + p + dir);
+				content_t neighbor_content = neighbor.getContent();
+				const ContentFeatures &n_feat = nodedef->get(neighbor_content);
+				MapNode n_top = data->m_vmanip.getNodeNoEx(blockpos_nodes + p + dir+ v3s16(0,1,0));
+				content_t n_top_c = n_top.getContent();
+
+				if(neighbor_content == CONTENT_IGNORE)
+					continue;
+
+				/*
+					If our topside is liquid and neighbor's topside
+					is liquid, don't draw side face
+				*/
+				if(top_is_same_liquid && (n_top_c == c_flowing ||
+						n_top_c == c_source || n_top_c == CONTENT_IGNORE))
+					continue;
+
+				// Don't draw face if neighbor is blocking the view
+				if(n_feat.solidness == 2)
+					continue;
+
+				bool neighbor_is_same_liquid = (neighbor_content == c_source
+						|| neighbor_content == c_flowing);
+
+				// Don't draw any faces if neighbor same is liquid and top is
+				// same liquid
+				if(neighbor_is_same_liquid && !top_is_same_liquid)
+					continue;
+
+				// Use backface culled material if neighbor doesn't have a
+				// solidness of 0
+				const TileSpec *current_tile = &tile_liquid;
+				if(n_feat.solidness != 0 || n_feat.visual_solidness != 0)
+					current_tile = &tile_liquid_bfculled;
+
+				video::S3DVertex vertices[4] =
+				{
+					video::S3DVertex(-BS/2,0,BS/2,0,0,0, c,
+							pa_liquid.x0(), pa_liquid.y1()),
+					video::S3DVertex(BS/2,0,BS/2,0,0,0, c,
+							pa_liquid.x1(), pa_liquid.y1()),
+					video::S3DVertex(BS/2,0,BS/2, 0,0,0, c,
+							pa_liquid.x1(), pa_liquid.y0()),
+					video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c,
+							pa_liquid.x0(), pa_liquid.y0()),
+				};
+
+				/*
+					If our topside is liquid, set upper border of face
+					at upper border of node
+				*/
+				if(top_is_same_liquid)
+				{
+					vertices[2].Pos.Y = 0.5*BS;
+					vertices[3].Pos.Y = 0.5*BS;
+				}
+				/*
+					Otherwise upper position of face is liquid level
+				*/
+				else
+				{
+					vertices[2].Pos.Y = (node_liquid_level-0.5)*BS;
+					vertices[3].Pos.Y = (node_liquid_level-0.5)*BS;
+				}
+				/*
+					If neighbor is liquid, lower border of face is liquid level
+				*/
+				if(neighbor_is_same_liquid)
+				{
+					vertices[0].Pos.Y = (node_liquid_level-0.5)*BS;
+					vertices[1].Pos.Y = (node_liquid_level-0.5)*BS;
+				}
+				/*
+					If neighbor is not liquid, lower border of face is
+					lower border of node
+				*/
+				else
+				{
+					vertices[0].Pos.Y = -0.5*BS;
+					vertices[1].Pos.Y = -0.5*BS;
+				}
+
+				for(s32 j=0; j<4; j++)
+				{
+					if(dir == v3s16(0,0,1))
+						vertices[j].Pos.rotateXZBy(0);
+					if(dir == v3s16(0,0,-1))
+						vertices[j].Pos.rotateXZBy(180);
+					if(dir == v3s16(-1,0,0))
+						vertices[j].Pos.rotateXZBy(90);
+					if(dir == v3s16(1,0,-0))
+						vertices[j].Pos.rotateXZBy(-90);
+
+					// Do this to not cause glitches when two liquids are
+					// side-by-side
+					/*if(neighbor_is_same_liquid == false){
+						vertices[j].Pos.X *= 0.98;
+						vertices[j].Pos.Z *= 0.98;
+					}*/
+
+					vertices[j].Pos += intToFloat(p, BS);
+				}
+
+				u16 indices[] = {0,1,2,2,3,0};
+				// Add to mesh collector
+				collector.append(*current_tile, vertices, 4, indices, 6);
+			}
+
+			/*
+				Generate top
+			 */
+			if(top_is_same_liquid)
+				continue;
 			
 			video::S3DVertex vertices[4] =
 			{
@@ -185,7 +369,7 @@ void mapblock_mesh_generate_special(MeshMakeData *data,
 						pa_liquid.x0(), pa_liquid.y0()),
 			};
 
-			v3f offset(p.X, p.Y + (-0.5+node_liquid_level)*BS, p.Z);
+			v3f offset(p.X*BS, p.Y*BS + (-0.5+node_liquid_level)*BS, p.Z*BS);
 			for(s32 i=0; i<4; i++)
 			{
 				vertices[i].Pos += offset;
@@ -230,9 +414,9 @@ void mapblock_mesh_generate_special(MeshMakeData *data,
 			
 			// Neighbor liquid levels (key = relative position)
 			// Includes current node
-			core::map<v3s16, f32> neighbor_levels;
-			core::map<v3s16, content_t> neighbor_contents;
-			core::map<v3s16, u8> neighbor_flags;
+			std::map<v3s16, f32> neighbor_levels;
+			std::map<v3s16, content_t> neighbor_contents;
+			std::map<v3s16, u8> neighbor_flags;
 			const u8 neighborflag_top_is_same_liquid = 0x01;
 			v3s16 neighbor_dirs[9] = {
 				v3s16(0,0,0),
@@ -273,9 +457,9 @@ void mapblock_mesh_generate_special(MeshMakeData *data,
 						flags |= neighborflag_top_is_same_liquid;
 				}
 				
-				neighbor_levels.insert(neighbor_dirs[i], level);
-				neighbor_contents.insert(neighbor_dirs[i], content);
-				neighbor_flags.insert(neighbor_dirs[i], flags);
+				neighbor_levels[neighbor_dirs[i]] = level;
+				neighbor_contents[neighbor_dirs[i]] = content;
+				neighbor_flags[neighbor_dirs[i]] = flags;
 			}
 
 			// Corner heights (average between four liquids)
@@ -324,7 +508,7 @@ void mapblock_mesh_generate_special(MeshMakeData *data,
 					}
 				}
 				if(air_count >= 2)
-					cornerlevel = -0.5*BS+0.1;
+					cornerlevel = -0.5*BS+0.2;
 				else if(valid_count > 0)
 					cornerlevel /= valid_count;
 				corner_levels[i] = cornerlevel;
@@ -1027,14 +1211,8 @@ void mapblock_mesh_generate_special(MeshMakeData *data,
 				v3s16(0, 0, 1),
 				v3s16(0, 0, -1)
 			};
-
 			TileSpec tiles[6];
-			for(int i = 0; i < 6; i++)
-			{
-				// Handles facedir rotation for textures
-				tiles[i] = getNodeTile(n, p, tile_dirs[i], data);
-			}
-
+			
 			u16 l = getInteriorLight(n, 0, data);
 			video::SColor c = MapBlock_LightColor(255, l, decode_light(f.light_source));
 
@@ -1045,17 +1223,43 @@ void mapblock_mesh_generate_special(MeshMakeData *data,
 					i = boxes.begin();
 					i != boxes.end(); i++)
 			{
+			for(int j = 0; j < 6; j++)
+				{
+				// Handles facedir rotation for textures
+				tiles[j] = getNodeTile(n, p, tile_dirs[j], data);
+				}
 				aabb3f box = *i;
 				box.MinEdge += pos;
 				box.MaxEdge += pos;
+				
+				f32 temp;
+				if (box.MinEdge.X > box.MaxEdge.X)
+				{
+					temp=box.MinEdge.X;
+					box.MinEdge.X=box.MaxEdge.X;
+					box.MaxEdge.X=temp;
+				}
+				if (box.MinEdge.Y > box.MaxEdge.Y)
+				{
+					temp=box.MinEdge.Y;
+					box.MinEdge.Y=box.MaxEdge.Y;
+					box.MaxEdge.Y=temp;
+				}
+				if (box.MinEdge.Z > box.MaxEdge.Z)
+				{
+					temp=box.MinEdge.Z;
+					box.MinEdge.Z=box.MaxEdge.Z;
+					box.MaxEdge.Z=temp;
+				}
 
+				//
 				// Compute texture coords
-				f32 tx1 = (i->MinEdge.X/BS)+0.5;
-				f32 ty1 = (i->MinEdge.Y/BS)+0.5;
-				f32 tz1 = (i->MinEdge.Z/BS)+0.5;
-				f32 tx2 = (i->MaxEdge.X/BS)+0.5;
-				f32 ty2 = (i->MaxEdge.Y/BS)+0.5;
-				f32 tz2 = (i->MaxEdge.Z/BS)+0.5;
+				f32 tx1 = (box.MinEdge.X/BS)+0.5;
+				f32 ty1 = (box.MinEdge.Y/BS)+0.5;
+				f32 tz1 = (box.MinEdge.Z/BS)+0.5;
+				f32 tx2 = (box.MaxEdge.X/BS)+0.5;
+				f32 ty2 = (box.MaxEdge.Y/BS)+0.5;
+				f32 tz2 = (box.MaxEdge.Z/BS)+0.5;
 				f32 txc[24] = {
 					// up
 					tx1, 1-tz2, tx2, 1-tz1,
@@ -1070,7 +1274,6 @@ void mapblock_mesh_generate_special(MeshMakeData *data,
 					// front
 					tx1, 1-ty2, tx2, 1-ty1,
 				};
-
 				makeCuboid(&collector, box, tiles, 6, c, txc);
 			}
 		break;}
diff --git a/src/content_sao.cpp b/src/content_sao.cpp
index 718a42dffcff479eea9eb3fa06cd737789a6b85d..ae08b4260385fb9b4f253b7ff81fc2dd9482094f 100644
--- a/src/content_sao.cpp
+++ b/src/content_sao.cpp
@@ -31,7 +31,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include "genericobject.h"
 #include "util/serialize.h"
 
-core::map<u16, ServerActiveObject::Factory> ServerActiveObject::m_types;
+std::map<u16, ServerActiveObject::Factory> ServerActiveObject::m_types;
 
 /*
 	DummyLoadSAO
@@ -64,6 +64,10 @@ class DummyLoadSAO : public ServerActiveObject
 		infostream<<"DummyLoadSAO step"<<std::endl;
 	}
 
+	bool getCollisionBox(aabb3f *toset) {
+		return false;
+	}
+
 private:
 };
 
@@ -132,6 +136,10 @@ class TestSAO : public ServerActiveObject
 		}
 	}
 
+	bool getCollisionBox(aabb3f *toset) {
+		return false;
+	}
+
 private:
 	float m_timer1;
 	float m_age;
@@ -208,8 +216,7 @@ class ItemSAO : public ServerActiveObject
 		v3f pos_f_old = pos_f;
 		v3f accel_f = v3f(0,0,0);
 		f32 stepheight = 0;
-		IGameDef *gamedef = m_env->getGameDef();
-		moveresult = collisionMoveSimple(&m_env->getMap(), gamedef,
+		moveresult = collisionMoveSimple(m_env,m_env->getGameDef(),
 				pos_max_d, box, stepheight, dtime,
 				pos_f, m_speed_f, accel_f);
 		
@@ -314,6 +321,10 @@ class ItemSAO : public ServerActiveObject
 		return 0;
 	}
 
+	bool getCollisionBox(aabb3f *toset) {
+		return false;
+	}
+
 
 private:
 	std::string m_itemstring;
@@ -370,8 +381,7 @@ LuaEntitySAO::LuaEntitySAO(ServerEnvironment *env, v3f pos,
 	}
 	
 	// Initialize something to armor groups
-	m_armor_groups["fleshy"] = 3;
-	m_armor_groups["snappy"] = 2;
+	m_armor_groups["fleshy"] = 100;
 }
 
 LuaEntitySAO::~LuaEntitySAO()
@@ -490,8 +500,7 @@ void LuaEntitySAO::step(float dtime, bool send_recommended)
 			v3f p_pos = m_base_position;
 			v3f p_velocity = m_velocity;
 			v3f p_acceleration = m_acceleration;
-			IGameDef *gamedef = m_env->getGameDef();
-			moveresult = collisionMoveSimple(&m_env->getMap(), gamedef,
+			moveresult = collisionMoveSimple(m_env,m_env->getGameDef(),
 					pos_max_d, box, stepheight, dtime,
 					p_pos, p_velocity, p_acceleration);
 			// Apply results
@@ -880,6 +889,22 @@ void LuaEntitySAO::sendPosition(bool do_interpolate, bool is_movement_end)
 	m_messages_out.push_back(aom);
 }
 
+bool LuaEntitySAO::getCollisionBox(aabb3f *toset) {
+	if (m_prop.physical)
+	{
+		//update collision box
+		toset->MinEdge = m_prop.collisionbox.MinEdge * BS;
+		toset->MaxEdge = m_prop.collisionbox.MaxEdge * BS;
+
+		toset->MinEdge += m_base_position;
+		toset->MaxEdge += m_base_position;
+
+		return true;
+	}
+
+	return false;
+}
+
 /*
 	PlayerSAO
 */
@@ -916,8 +941,7 @@ PlayerSAO::PlayerSAO(ServerEnvironment *env_, Player *player_, u16 peer_id_,
 	assert(m_peer_id != 0);
 	setBasePosition(m_player->getPosition());
 	m_inventory = &m_player->inventory;
-	m_armor_groups["choppy"] = 2;
-	m_armor_groups["fleshy"] = 3;
+	m_armor_groups["fleshy"] = 100;
 
 	m_prop.hp_max = PLAYER_MAX_HP;
 	m_prop.physical = false;
@@ -1230,6 +1254,20 @@ void PlayerSAO::moveTo(v3f pos, bool continuous)
 	m_moved = true;
 }
 
+void PlayerSAO::setYaw(float yaw)
+{
+	m_player->setYaw(yaw);
+	// Force change on client
+	m_moved = true;
+}
+
+void PlayerSAO::setPitch(float pitch)
+{
+	m_player->setPitch(pitch);
+	// Force change on client
+	m_moved = true;
+}
+
 int PlayerSAO::punch(v3f dir,
 	const ToolCapabilities *toolcap,
 	ServerActiveObject *puncher,
@@ -1420,3 +1458,7 @@ std::string PlayerSAO::getPropertyPacket()
 	return gob_cmd_set_properties(m_prop);
 }
 
+bool PlayerSAO::getCollisionBox(aabb3f *toset) {
+	//player collision handling is already done clientside no need to do it twice
+	return false;
+}
diff --git a/src/content_sao.h b/src/content_sao.h
index 2fd1034ebd73a8380c20c14e13ef59129e990101..60ca8f319fd88abf852be59a21a3d6e8c58a93d3 100644
--- a/src/content_sao.h
+++ b/src/content_sao.h
@@ -78,6 +78,7 @@ class LuaEntitySAO : public ServerActiveObject
 	void setSprite(v2s16 p, int num_frames, float framelength,
 			bool select_horiz_by_yawpitch);
 	std::string getName();
+	bool getCollisionBox(aabb3f *toset);
 private:
 	std::string getPropertyPacket();
 	void sendPosition(bool do_interpolate, bool is_movement_end);
@@ -147,6 +148,8 @@ class PlayerSAO : public ServerActiveObject
 	void setBasePosition(const v3f &position);
 	void setPos(v3f pos);
 	void moveTo(v3f pos, bool continuous);
+	void setYaw(float);
+	void setPitch(float);
 
 	/*
 		Interaction interface
@@ -233,6 +236,8 @@ class PlayerSAO : public ServerActiveObject
 		m_is_singleplayer = is_singleplayer;
 	}
 
+	bool getCollisionBox(aabb3f *toset);
+
 private:
 	std::string getPropertyPacket();
 	
diff --git a/src/craftdef.cpp b/src/craftdef.cpp
index 99e3fcc3d93c2d22581962e471c13e673e0b067d..c79408f9986b177aa0933f7aa777e6ef38aea78f 100644
--- a/src/craftdef.cpp
+++ b/src/craftdef.cpp
@@ -987,6 +987,43 @@ class CCraftDefManager: public IWritableCraftDefManager
 		}
 		return false;
 	}
+	virtual std::vector<CraftDefinition*> getCraftRecipes(CraftOutput &output,
+			IGameDef *gamedef) const
+	{
+		std::vector<CraftDefinition*> recipes_list;
+		CraftInput input;
+		CraftOutput tmpout;
+		tmpout.item = "";
+		tmpout.time = 0;
+
+		for(std::vector<CraftDefinition*>::const_reverse_iterator
+				i = m_craft_definitions.rbegin();
+				i != m_craft_definitions.rend(); i++)
+		{
+			CraftDefinition *def = *i;
+
+			/*infostream<<"Checking "<<input.dump()<<std::endl
+					<<" against "<<def->dump()<<std::endl;*/
+
+			try {
+				tmpout = def->getOutput(input, gamedef);
+				if(tmpout.item.substr(0,output.item.length()) == output.item)
+				{
+					// Get output, then decrement input (if requested)
+					input = def->getInput(output, gamedef);
+					recipes_list.push_back(*i);
+				}
+			}
+			catch(SerializationError &e)
+			{
+				errorstream<<"getCraftResult: ERROR: "
+						<<"Serialization error in recipe "
+						<<def->dump()<<std::endl;
+				// then go on with the next craft definition
+			}
+		}
+		return recipes_list;
+	}
 	virtual std::string dump() const
 	{
 		std::ostringstream os(std::ios::binary);
diff --git a/src/craftdef.h b/src/craftdef.h
index eb3cd7e391486e8a9530d2043ed1c5bf270ba5ce..14dc530031ba4eaa834eb49ccfc08e112ae3cd63 100644
--- a/src/craftdef.h
+++ b/src/craftdef.h
@@ -358,6 +358,8 @@ class ICraftDefManager
 			bool decrementInput, IGameDef *gamedef) const=0;
 	virtual bool getCraftRecipe(CraftInput &input, CraftOutput &output,
 			IGameDef *gamedef) const=0;
+	virtual std::vector<CraftDefinition*> getCraftRecipes(CraftOutput &output,
+			IGameDef *gamedef) const=0;
 	
 	// Print crafting recipes for debugging
 	virtual std::string dump() const=0;
@@ -376,6 +378,8 @@ class IWritableCraftDefManager : public ICraftDefManager
 			bool decrementInput, IGameDef *gamedef) const=0;
 	virtual bool getCraftRecipe(CraftInput &input, CraftOutput &output,
 			IGameDef *gamedef) const=0;
+	virtual std::vector<CraftDefinition*> getCraftRecipes(CraftOutput &output, 
+			IGameDef *gamedef) const=0;
 
 	// Print crafting recipes for debugging
 	virtual std::string dump() const=0;
diff --git a/src/debug.cpp b/src/debug.cpp
index e32cceb8606c2dfc5fd3a2f6e197a2e315b29f0d..2e4992a78013f752ae51211db4d5d018cda11498 100644
--- a/src/debug.cpp
+++ b/src/debug.cpp
@@ -130,7 +130,7 @@ void DebugStack::print(std::ostream &os, bool everything)
 		os<<"Probably overflown."<<std::endl;
 }
 
-core::map<threadid_t, DebugStack*> g_debug_stacks;
+std::map<threadid_t, DebugStack*> g_debug_stacks;
 JMutex g_debug_stacks_mutex;
 
 void debug_stacks_init()
@@ -144,12 +144,11 @@ void debug_stacks_print_to(std::ostream &os)
 
 	os<<"Debug stacks:"<<std::endl;
 
-	for(core::map<threadid_t, DebugStack*>::Iterator
-			i = g_debug_stacks.getIterator();
-			i.atEnd() == false; i++)
+	for(std::map<threadid_t, DebugStack*>::iterator
+			i = g_debug_stacks.begin();
+			i != g_debug_stacks.end(); ++i)
 	{
-		DebugStack *stack = i.getNode()->getValue();
-		stack->print(os, false);
+		i->second->print(os, false);
 	}
 }
 
@@ -159,11 +158,11 @@ void debug_stacks_print()
 
 	DEBUGPRINT("Debug stacks:\n");
 
-	for(core::map<threadid_t, DebugStack*>::Iterator
-			i = g_debug_stacks.getIterator();
-			i.atEnd() == false; i++)
+	for(std::map<threadid_t, DebugStack*>::iterator
+			i = g_debug_stacks.begin();
+			i != g_debug_stacks.end(); ++i)
 	{
-		DebugStack *stack = i.getNode()->getValue();
+		DebugStack *stack = i->second;
 
 		for(int i=0; i<DEBUGSTREAM_COUNT; i++)
 		{
@@ -179,18 +178,18 @@ DebugStacker::DebugStacker(const char *text)
 
 	JMutexAutoLock lock(g_debug_stacks_mutex);
 
-	core::map<threadid_t, DebugStack*>::Node *n;
+	std::map<threadid_t, DebugStack*>::iterator n;
 	n = g_debug_stacks.find(threadid);
-	if(n != NULL)
+	if(n != g_debug_stacks.end())
 	{
-		m_stack = n->getValue();
+		m_stack = n->second;
 	}
 	else
 	{
 		/*DEBUGPRINT("Creating new debug stack for thread %x\n",
 				(unsigned int)threadid);*/
 		m_stack = new DebugStack(threadid);
-		g_debug_stacks.insert(threadid, m_stack);
+		g_debug_stacks[threadid] = m_stack;
 	}
 
 	if(m_stack->stack_i >= DEBUG_STACK_SIZE)
@@ -224,7 +223,7 @@ DebugStacker::~DebugStacker()
 		/*DEBUGPRINT("Deleting debug stack for thread %x\n",
 				(unsigned int)threadid);*/
 		delete m_stack;
-		g_debug_stacks.remove(threadid);
+		g_debug_stacks.erase(threadid);
 	}
 }
 
diff --git a/src/debug.h b/src/debug.h
index 56952427cfd2542789eba39fe191ac81f99a77c4..1b14c4e0a325c2b2d1fbf50f6bbee19c356e91a7 100644
--- a/src/debug.h
+++ b/src/debug.h
@@ -29,6 +29,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include "threads.h"
 #include "gettime.h"
 #include "exceptions.h"
+#include <map>
 
 #ifdef _WIN32
 	#define WIN32_LEAN_AND_MEAN
@@ -165,7 +166,7 @@ struct DebugStack
 	int stack_max_i; // Highest i that was seen
 };
 
-extern core::map<threadid_t, DebugStack*> g_debug_stacks;
+extern std::map<threadid_t, DebugStack*> g_debug_stacks;
 extern JMutex g_debug_stacks_mutex;
 
 extern void debug_stacks_init();
@@ -205,42 +206,42 @@ class PacketCounter
 
 	void add(u16 command)
 	{
-		core::map<u16, u16>::Node *n = m_packets.find(command);
-		if(n == NULL)
+		std::map<u16, u16>::iterator n = m_packets.find(command);
+		if(n == m_packets.end())
 		{
 			m_packets[command] = 1;
 		}
 		else
 		{
-			n->setValue(n->getValue()+1);
+			n->second++;
 		}
 	}
 
 	void clear()
 	{
-		for(core::map<u16, u16>::Iterator
-				i = m_packets.getIterator();
-				i.atEnd() == false; i++)
+		for(std::map<u16, u16>::iterator
+				i = m_packets.begin();
+				i != m_packets.end(); ++i)
 		{
-			i.getNode()->setValue(0);
+			i->second = 0;
 		}
 	}
 
 	void print(std::ostream &o)
 	{
-		for(core::map<u16, u16>::Iterator
-				i = m_packets.getIterator();
-				i.atEnd() == false; i++)
+		for(std::map<u16, u16>::iterator
+				i = m_packets.begin();
+				i != m_packets.end(); ++i)
 		{
-			o<<"cmd "<<i.getNode()->getKey()
-					<<" count "<<i.getNode()->getValue()
+			o<<"cmd "<<i->first
+					<<" count "<<i->second
 					<<std::endl;
 		}
 	}
 
 private:
 	// command, count
-	core::map<u16, u16> m_packets;
+	std::map<u16, u16> m_packets;
 };
 
 /*
diff --git a/src/defaultsettings.cpp b/src/defaultsettings.cpp
index a8954be724c204f54d362dc4bcdfeef90ceadc48..592c6bccae7587270a2c9c765a8cdc8cea7db803 100644
--- a/src/defaultsettings.cpp
+++ b/src/defaultsettings.cpp
@@ -109,6 +109,7 @@ void set_default_settings(Settings *settings)
 	settings->setDefault("view_bobbing_amount", "1.0");
 	settings->setDefault("enable_3d_clouds", "true");
 	settings->setDefault("cloud_height", "120");
+	settings->setDefault("menu_clouds", "true");
 	settings->setDefault("opaque_water", "false");
 	settings->setDefault("console_color", "(0,0,0)");
 	settings->setDefault("console_alpha", "200");
@@ -202,34 +203,57 @@ void set_default_settings(Settings *settings)
 	settings->setDefault("movement_liquid_fluidity_smooth", "0.5");
 	settings->setDefault("movement_liquid_sink", "10");
 	settings->setDefault("movement_gravity", "9.81");
-	
+
 	//liquid stuff
 	settings->setDefault("liquid_finite", "false");
 	settings->setDefault("liquid_update", "1.0");
-	settings->setDefault("liquid_relax", "1");
+	settings->setDefault("liquid_relax", "2");
 	settings->setDefault("liquid_fast_flood", "1");
+	settings->setDefault("underground_springs", "1");
 
 	//mapgen stuff
 	settings->setDefault("mg_name", "v6");
-	settings->setDefault("water_level", "1");	
+	settings->setDefault("water_level", "1");
 	settings->setDefault("chunksize", "5");
 	settings->setDefault("mg_flags", "trees, caves, v6_biome_blend");
 	settings->setDefault("mgv6_freq_desert", "0.45");
 	settings->setDefault("mgv6_freq_beach", "0.15");
 
-	settings->setDefault("mgv6_np_terrain_base",   "-4, 20, (250.0, 250, 250), 82341, 5, 0.6");
+	settings->setDefault("mgv6_np_terrain_base",   "-4, 20, (250, 250, 250), 82341, 5, 0.6");
 	settings->setDefault("mgv6_np_terrain_higher", "20, 16, (500, 500, 500), 85039, 5, 0.6");
 	settings->setDefault("mgv6_np_steepness",      "0.85, 0.5, (125, 125, 125), -932, 5, 0.7");
 	settings->setDefault("mgv6_np_height_select",  "0.5, 1, (250, 250, 250), 4213, 5, 0.69");
-	settings->setDefault("mgv6_np_trees",          "0, 1, (125, 125, 125), 2, 4, 0.66");
 	settings->setDefault("mgv6_np_mud",            "4, 2, (200, 200, 200), 91013, 3, 0.55");
 	settings->setDefault("mgv6_np_beach",          "0, 1, (250, 250, 250), 59420, 3, 0.50");
 	settings->setDefault("mgv6_np_biome",          "0, 1, (250, 250, 250), 9130, 3, 0.50");
 	settings->setDefault("mgv6_np_cave",           "6, 6, (250, 250, 250), 34329, 3, 0.50");
+	settings->setDefault("mgv6_np_humidity",       "0.5, 0.5, (500, 500, 500), 72384, 4, 0.66");
+	settings->setDefault("mgv6_np_trees",          "0, 1, (125, 125, 125), 2, 4, 0.66");
+	settings->setDefault("mgv6_np_apple_trees",    "0, 1, (100, 100, 100), 342902, 3, 0.45");
 
 	settings->setDefault("mgv7_np_terrain",  "10, 12, (350, 350, 350), 82341, 5, 0.6");
 	settings->setDefault("mgv7_np_bgroup",   "0.5, 0.3125, (350, 350, 350), 5923, 2, 0.6");
 	settings->setDefault("mgv7_np_heat",     "25, 50, (500, 500, 500), 35293, 1, 0");
 	settings->setDefault("mgv7_np_humidity", "50, 31.25, (750, 750, 750), 12094, 2, 0.6");
+
+	settings->setDefault("mgindev_np_terrain_base",   "-4,   20,  (250, 250, 250), 82341, 5, 0.6,  10,  10");
+	settings->setDefault("mgindev_np_terrain_higher", "20,   16,  (500, 500, 500), 85039, 5, 0.6,  10,  10");
+	settings->setDefault("mgindev_np_steepness",      "0.85, 0.5, (125, 125, 125), -932,  5, 0.7,  2,   10");
+	settings->setDefault("mgindev_np_mud",            "4,    2,   (200, 200, 200), 91013, 3, 0.55, 1,   1");
+	settings->setDefault("mgindev_np_float_islands1", "0,    1,   (64,  64,  64 ), 3683,  5, 0.5,  1,   1.5");
+	settings->setDefault("mgindev_np_float_islands2", "0,    1,   (8,   8,   8  ), 9292,  2, 0.5,  1,   1.5");
+	settings->setDefault("mgindev_np_float_islands3", "0,    1,   (256, 256, 256), 6412,  2, 0.5,  1,   0.5");
+	settings->setDefault("mgindev_np_biome",          "0,    1,   (250, 250, 250), 9130,  3, 0.50, 1,   10");
+	settings->setDefault("mgindev_float_islands", "500");
+
+}
+
+void override_default_settings(Settings *settings, Settings *from)
+{
+	std::vector<std::string> names = from->getNames();
+	for(size_t i=0; i<names.size(); i++){
+		const std::string &name = names[i];
+		settings->setDefault(name, from->get(name));
+	}
 }
 
diff --git a/src/defaultsettings.h b/src/defaultsettings.h
index 37e3f717f11232a675644815d8da944770f1bb3e..00aacad87f2d4dd142d163f7db8b1fcb55415936 100644
--- a/src/defaultsettings.h
+++ b/src/defaultsettings.h
@@ -23,6 +23,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 class Settings;
 
 void set_default_settings(Settings *settings);
+void override_default_settings(Settings *settings, Settings *from);
 
 #endif
 
diff --git a/src/dungeongen.cpp b/src/dungeongen.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..9528b4132b74434cbe9f989b71c82c3a9d446001
--- /dev/null
+++ b/src/dungeongen.cpp
@@ -0,0 +1,634 @@
+/*
+Minetest
+Copyright (C) 2010-2013 celeron55, Perttu Ahola <celeron55@gmail.com>
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU Lesser General Public License as published by
+the Free Software Foundation; either version 2.1 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public License along
+with this program; if not, write to the Free Software Foundation, Inc.,
+51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+
+#include "dungeongen.h"
+#include "mapgen.h"
+#include "voxel.h"
+#include "noise.h"
+#include "mapblock.h"
+#include "mapnode.h"
+#include "map.h"
+#include "nodedef.h"
+#include "profiler.h"
+#include "settings.h" // For g_settings
+#include "main.h" // For g_profiler
+
+NoiseParams nparams_dungeon_rarity = 
+	{0.0, 1.0, v3f(500.0, 500.0, 500.0), 0, 2, 0.8};
+NoiseParams nparams_dungeon_wetness =
+	{0.0, 1.0, v3f(40.0, 40.0, 40.0), 32474, 4, 1.1};
+NoiseParams nparams_dungeon_density =
+	{0.0, 1.0, v3f(2.5, 2.5, 2.5), 0, 2, 1.4};
+
+
+///////////////////////////////////////////////////////////////////////////////
+
+
+DungeonGen::DungeonGen(INodeDefManager *ndef, u64 seed, s16 waterlevel) {
+	this->ndef        = ndef;
+	this->mapseed     = seed;
+	this->water_level = waterlevel;
+	
+	np_rarity  = &nparams_dungeon_rarity;
+	np_wetness = &nparams_dungeon_wetness;
+	np_density = &nparams_dungeon_density;
+	/*
+	cid_water_source = ndef->getId("mapgen_water_source");
+	cid_cobble       = ndef->getId("mapgen_cobble");
+	cid_mossycobble  = ndef->getId("mapgen_mossycobble");
+	cid_torch        = ndef->getId("default:torch");
+	*/
+}
+
+
+void DungeonGen::generate(ManualMapVoxelManipulator *vm, u32 bseed,
+						 v3s16 nmin, v3s16 nmax) {
+	//TimeTaker t("gen dungeons");
+	int approx_groundlevel = 10 + water_level;
+
+	if ((nmin.Y + nmax.Y) / 2 >= approx_groundlevel ||
+		NoisePerlin3D(np_rarity, nmin.X, nmin.Y, nmin.Z, mapseed) < 0.2)
+		return;
+		
+	this->vmanip    = vm;
+	this->blockseed = bseed;
+	random.seed(bseed + 2);
+
+	cid_water_source = ndef->getId("mapgen_water_source");
+	cid_cobble       = ndef->getId("mapgen_cobble");
+	cid_mossycobble  = ndef->getId("mapgen_mossycobble");
+	//cid_torch        = ndef->getId("default:torch");
+	cid_cobblestair  = ndef->getId("stairs:stair_cobble");
+
+	// Dungeon generator doesn't modify places which have this set
+	vmanip->clearFlag(VMANIP_FLAG_DUNGEON_INSIDE | VMANIP_FLAG_DUNGEON_PRESERVE);
+
+	// Set all air and water to be untouchable to make dungeons open
+	// to caves and open air
+	for (s16 z = nmin.Z; z <= nmax.Z; z++) {
+		for (s16 y = nmin.Y; y <= nmax.Y; y++) {
+			u32 i = vmanip->m_area.index(nmin.X, y, z);
+			for (s16 x = nmin.X; x <= nmax.X; x++) {
+				content_t c = vmanip->m_data[i].getContent();
+				if (c == CONTENT_AIR || c == cid_water_source)
+					vmanip->m_flags[i] |= VMANIP_FLAG_DUNGEON_PRESERVE;
+				i++;
+			}
+		}
+	}
+	
+	// Add it
+	makeDungeon(v3s16(1,1,1) * MAP_BLOCKSIZE);
+	
+	// Convert some cobble to mossy cobble
+	for (s16 z = nmin.Z; z <= nmax.Z; z++) {
+		for (s16 y = nmin.Y; y <= nmax.Y; y++) {
+			u32 i = vmanip->m_area.index(nmin.X, y, z);
+			for (s16 x = nmin.X; x <= nmax.X; x++) {
+				if (vmanip->m_data[i].getContent() == cid_cobble) {
+					float wetness = NoisePerlin3D(np_wetness, x, y, z, mapseed);
+					float density = NoisePerlin3D(np_density, x, y, z, blockseed);
+					if (density < wetness / 3.0)
+						vmanip->m_data[i].setContent(cid_mossycobble);
+				}
+				i++;
+			}
+		}
+	}
+	
+	//printf("== gen dungeons: %dms\n", t.stop());
+}
+
+
+void DungeonGen::makeDungeon(v3s16 start_padding)
+{
+	v3s16 areasize = vmanip->m_area.getExtent();
+	v3s16 roomsize;
+	v3s16 roomplace;
+
+	/*
+		Find place for first room
+	*/
+	bool fits = false;
+	for (u32 i = 0; i < 100; i++)
+	{
+		bool is_large_room = ((random.next() & 3) == 1);
+		roomsize = is_large_room ?
+			v3s16(random.range(8, 16),random.range(8, 16),random.range(8, 16)) :
+			v3s16(random.range(4,  8),random.range(4,  6),random.range(4, 8));
+		
+		// start_padding is used to disallow starting the generation of
+		// a dungeon in a neighboring generation chunk
+		roomplace = vmanip->m_area.MinEdge + start_padding + v3s16(
+			random.range(0,areasize.X-roomsize.X-1-start_padding.X),
+			random.range(0,areasize.Y-roomsize.Y-1-start_padding.Y),
+			random.range(0,areasize.Z-roomsize.Z-1-start_padding.Z));
+			
+		/*
+			Check that we're not putting the room to an unknown place,
+			otherwise it might end up floating in the air
+		*/
+		fits = true;
+		for (s16 z = 1; z < roomsize.Z - 1; z++)
+		for (s16 y = 1; y < roomsize.Y - 1; y++)
+		for (s16 x = 1; x < roomsize.X - 1; x++)
+		{
+			v3s16 p = roomplace + v3s16(x, y, z);
+			u32 vi = vmanip->m_area.index(p);
+			if (vmanip->m_flags[vi] & VMANIP_FLAG_DUNGEON_INSIDE)
+			{
+				fits = false;
+				break;
+			}
+			if (vmanip->m_data[vi].getContent() == CONTENT_IGNORE)
+			{
+				fits = false;
+				break;
+			}
+		}
+		if (fits)
+			break;
+	}
+	// No place found
+	if (fits == false)
+		return;
+
+	/*
+		Stores the center position of the last room made, so that
+		a new corridor can be started from the last room instead of
+		the new room, if chosen so.
+	*/
+	v3s16 last_room_center = roomplace + v3s16(roomsize.X / 2, 1, roomsize.Z / 2);
+
+	u32 room_count = random.range(2, 16);
+	for (u32 i = 0; i < room_count; i++)
+	{
+		// Make a room to the determined place
+		makeRoom(roomsize, roomplace);
+
+		v3s16 room_center = roomplace + v3s16(roomsize.X / 2, 1, roomsize.Z / 2);
+
+		// Place torch at room center (for testing)
+		//vmanip->m_data[vmanip->m_area.index(room_center)] = MapNode(cid_torch);
+
+		// Quit if last room
+		if (i == room_count - 1)
+			break;
+
+		// Determine walker start position
+
+		bool start_in_last_room = (random.range(0, 2) != 0);
+
+		v3s16 walker_start_place;
+
+		if(start_in_last_room)
+		{
+			walker_start_place = last_room_center;
+		}
+		else
+		{
+			walker_start_place = room_center;
+			// Store center of current room as the last one
+			last_room_center = room_center;
+		}
+
+		// Create walker and find a place for a door
+		v3s16 doorplace;
+		v3s16 doordir;
+		
+		m_pos = walker_start_place;
+		bool r = findPlaceForDoor(doorplace, doordir);
+		if (r == false)
+			return;
+
+		if (random.range(0,1) == 0)
+			// Make the door
+			makeDoor(doorplace, doordir);
+		else
+			// Don't actually make a door
+			doorplace -= doordir;
+
+		// Make a random corridor starting from the door
+		v3s16 corridor_end;
+		v3s16 corridor_end_dir;
+		makeCorridor(doorplace, doordir, corridor_end, corridor_end_dir);
+
+		// Find a place for a random sized room
+		roomsize = v3s16(random.range(4,8),random.range(4,6),random.range(4,8));
+		m_pos = corridor_end;
+		m_dir = corridor_end_dir;
+		r = findPlaceForRoomDoor(roomsize, doorplace, doordir, roomplace);
+		if (r == false)
+			return;
+
+		if (random.range(0,1) == 0)
+			// Make the door
+			makeDoor(doorplace, doordir);
+		else
+			// Don't actually make a door
+			roomplace -= doordir;
+
+	}
+}
+
+
+void DungeonGen::makeRoom(v3s16 roomsize, v3s16 roomplace)
+{
+	MapNode n_cobble(cid_cobble);
+	MapNode n_air(CONTENT_AIR);
+	
+	// Make +-X walls
+	for (s16 z = 0; z < roomsize.Z; z++)
+	for (s16 y = 0; y < roomsize.Y; y++)
+	{
+		{
+			v3s16 p = roomplace + v3s16(0, y, z);
+			if (vmanip->m_area.contains(p) == false)
+				continue;
+			u32 vi = vmanip->m_area.index(p);
+			if (vmanip->m_flags[vi] & VMANIP_FLAG_DUNGEON_UNTOUCHABLE)
+				continue;
+			vmanip->m_data[vi] = n_cobble;
+		}
+		{
+			v3s16 p = roomplace + v3s16(roomsize.X - 1, y, z);
+			if (vmanip->m_area.contains(p) == false)
+				continue;
+			u32 vi = vmanip->m_area.index(p);
+			if (vmanip->m_flags[vi] & VMANIP_FLAG_DUNGEON_UNTOUCHABLE)
+				continue;
+			vmanip->m_data[vi] = n_cobble;
+		}
+	}
+
+	// Make +-Z walls
+	for (s16 x = 0; x < roomsize.X; x++)
+	for (s16 y = 0; y < roomsize.Y; y++)
+	{
+		{
+			v3s16 p = roomplace + v3s16(x, y, 0);
+			if (vmanip->m_area.contains(p) == false)
+				continue;
+			u32 vi = vmanip->m_area.index(p);
+			if (vmanip->m_flags[vi] & VMANIP_FLAG_DUNGEON_UNTOUCHABLE)
+				continue;
+			vmanip->m_data[vi] = n_cobble;
+		}
+		{
+			v3s16 p = roomplace + v3s16(x, y, roomsize.Z - 1);
+			if (vmanip->m_area.contains(p) == false)
+				continue;
+			u32 vi = vmanip->m_area.index(p);
+			if (vmanip->m_flags[vi] & VMANIP_FLAG_DUNGEON_UNTOUCHABLE)
+				continue;
+			vmanip->m_data[vi] = n_cobble;
+		}
+	}
+
+	// Make +-Y walls (floor and ceiling)
+	for (s16 z = 0; z < roomsize.Z; z++)
+	for (s16 x = 0; x < roomsize.X; x++)
+	{
+		{
+			v3s16 p = roomplace + v3s16(x, 0, z);
+			if (vmanip->m_area.contains(p) == false)
+				continue;
+			u32 vi = vmanip->m_area.index(p);
+			if (vmanip->m_flags[vi] & VMANIP_FLAG_DUNGEON_UNTOUCHABLE)
+				continue;
+			vmanip->m_data[vi] = n_cobble;
+		}
+		{
+			v3s16 p = roomplace + v3s16(x,roomsize. Y - 1, z);
+			if (vmanip->m_area.contains(p) == false)
+				continue;
+			u32 vi = vmanip->m_area.index(p);
+			if (vmanip->m_flags[vi] & VMANIP_FLAG_DUNGEON_UNTOUCHABLE)
+				continue;
+			vmanip->m_data[vi] = n_cobble;
+		}
+	}
+
+	// Fill with air
+	for (s16 z = 1; z < roomsize.Z - 1; z++)
+	for (s16 y = 1; y < roomsize.Y - 1; y++)
+	for (s16 x = 1; x < roomsize.X - 1; x++)
+	{
+		v3s16 p = roomplace + v3s16(x, y, z);
+		if (vmanip->m_area.contains(p) == false)
+			continue;
+		u32 vi = vmanip->m_area.index(p);
+		vmanip->m_flags[vi] |= VMANIP_FLAG_DUNGEON_UNTOUCHABLE;
+		vmanip->m_data[vi]   = n_air;
+	}
+}
+
+
+void DungeonGen::makeFill(v3s16 place, v3s16 size,
+		u8 avoid_flags, MapNode n, u8 or_flags)
+{
+	for (s16 z = 0; z < size.Z; z++)
+	for (s16 y = 0; y < size.Y; y++)
+	for (s16 x = 0; x < size.X; x++)
+	{
+		v3s16 p = place + v3s16(x, y, z);
+		if (vmanip->m_area.contains(p) == false)
+			continue;
+		u32 vi = vmanip->m_area.index(p);
+		if (vmanip->m_flags[vi] & avoid_flags)
+			continue;
+		vmanip->m_flags[vi] |= or_flags;
+		vmanip->m_data[vi]   = n;
+	}
+}
+
+
+void DungeonGen::makeHole(v3s16 place)
+{
+	makeFill(place, v3s16(1, 2, 1), 0, MapNode(CONTENT_AIR),
+			VMANIP_FLAG_DUNGEON_INSIDE);
+}
+
+
+void DungeonGen::makeDoor(v3s16 doorplace, v3s16 doordir)
+{
+	makeHole(doorplace);
+	// Place torch (for testing)
+	//vmanip->m_data[vmanip->m_area.index(doorplace)] = MapNode(cid_torch);
+}
+
+
+void DungeonGen::makeCorridor(v3s16 doorplace,
+		v3s16 doordir, v3s16 &result_place, v3s16 &result_dir)
+{
+	makeHole(doorplace);
+	v3s16 p0 = doorplace;
+	v3s16 dir = doordir;
+	u32 length;
+	/*if (random.next() % 2)
+		length = random.range(1, 13);
+	else
+		length = random.range(1, 6);*/
+	length = random.range(1, 13);
+	u32 partlength = random.range(1, 13);
+	u32 partcount = 0;
+	s16 make_stairs = 0;
+	
+	if (random.next() % 2 == 0 && partlength >= 3)
+		make_stairs = random.next() % 2 ? 1 : -1;
+	
+	for (u32 i = 0; i < length; i++) {
+		v3s16 p = p0 + dir;
+		if (partcount != 0)
+			p.Y += make_stairs;
+
+		if (vmanip->m_area.contains(p) == true &&
+			vmanip->m_area.contains(p + v3s16(0, 1, 0)) == true) {
+			if (make_stairs) {
+				makeFill(p + v3s16(-1, -1, -1), v3s16(3, 5, 3),
+						VMANIP_FLAG_DUNGEON_UNTOUCHABLE, MapNode(cid_cobble), 0);
+				makeHole(p);
+				makeHole(p - dir);
+				
+				// TODO: fix stairs code so it works 100% (quite difficult)
+
+				// exclude stairs from the bottom step
+				if (((make_stairs ==  1) && i != 0) ||
+					((make_stairs == -1) && i != length - 1)) {
+					// rotate face 180 deg if making stairs backwards
+					int facedir = dir_to_facedir(dir * make_stairs);
+					
+					u32 vi = vmanip->m_area.index(p.X - dir.X, p.Y - 1, p.Z - dir.Z);
+					if (vmanip->m_data[vi].getContent() == cid_cobble)
+						vmanip->m_data[vi] = MapNode(cid_cobblestair, 0, facedir);
+					
+					vi = vmanip->m_area.index(p.X, p.Y, p.Z);
+					if (vmanip->m_data[vi].getContent() == cid_cobble)
+						vmanip->m_data[vi] = MapNode(cid_cobblestair, 0, facedir);
+				}
+			} else {
+				makeFill(p + v3s16(-1, -1, -1), v3s16(3, 4, 3),
+						VMANIP_FLAG_DUNGEON_UNTOUCHABLE, MapNode(cid_cobble), 0);
+				makeHole(p);
+			}
+
+			p0 = p;
+		} else {
+			// Can't go here, turn away
+			dir = turn_xz(dir, random.range(0, 1));
+			make_stairs = -make_stairs;
+			partcount = 0;
+			partlength = random.range(1, length);
+			continue;
+		}
+
+		partcount++;
+		if (partcount >= partlength) {
+			partcount = 0;
+
+			dir = random_turn(random, dir);
+
+			partlength = random.range(1,length);
+
+			make_stairs = 0;
+			if (random.next() % 2 == 0 && partlength >= 3)
+				make_stairs = random.next() % 2 ? 1 : -1;
+		}
+	}
+	result_place = p0;
+	result_dir = dir;
+}
+
+
+bool DungeonGen::findPlaceForDoor(v3s16 &result_place, v3s16 &result_dir)
+{
+	for (u32 i = 0; i < 100; i++)
+	{
+		v3s16 p = m_pos + m_dir;
+		v3s16 p1 = p + v3s16(0, 1, 0);
+		if (vmanip->m_area.contains(p) == false
+		 || vmanip->m_area.contains(p1) == false
+		 || i % 4 == 0)
+		{
+			randomizeDir();
+			continue;
+		}
+		if (vmanip->getNodeNoExNoEmerge(p).getContent()  == cid_cobble
+		 && vmanip->getNodeNoExNoEmerge(p1).getContent() == cid_cobble)
+		{
+			// Found wall, this is a good place!
+			result_place = p;
+			result_dir = m_dir;
+			// Randomize next direction
+			randomizeDir();
+			return true;
+		}
+		/*
+			Determine where to move next
+		*/
+		// Jump one up if the actual space is there
+		if (vmanip->getNodeNoExNoEmerge(p+v3s16(0,0,0)).getContent() == cid_cobble
+		 && vmanip->getNodeNoExNoEmerge(p+v3s16(0,1,0)).getContent() == CONTENT_AIR
+		 && vmanip->getNodeNoExNoEmerge(p+v3s16(0,2,0)).getContent() == CONTENT_AIR)
+			p += v3s16(0,1,0);
+		// Jump one down if the actual space is there
+		if (vmanip->getNodeNoExNoEmerge(p+v3s16(0,1,0)).getContent() == cid_cobble
+		 && vmanip->getNodeNoExNoEmerge(p+v3s16(0,0,0)).getContent() == CONTENT_AIR
+		 && vmanip->getNodeNoExNoEmerge(p+v3s16(0,-1,0)).getContent() == CONTENT_AIR)
+			p += v3s16(0,-1,0);
+		// Check if walking is now possible
+		if (vmanip->getNodeNoExNoEmerge(p).getContent() != CONTENT_AIR
+		 || vmanip->getNodeNoExNoEmerge(p+v3s16(0,1,0)).getContent() != CONTENT_AIR)
+		{
+			// Cannot continue walking here
+			randomizeDir();
+			continue;
+		}
+		// Move there
+		m_pos = p;
+	}
+	return false;
+}
+
+
+bool DungeonGen::findPlaceForRoomDoor(v3s16 roomsize, v3s16 &result_doorplace,
+		v3s16 &result_doordir, v3s16 &result_roomplace)
+{
+	for (s16 trycount = 0; trycount < 30; trycount++)
+	{
+		v3s16 doorplace;
+		v3s16 doordir;
+		bool r = findPlaceForDoor(doorplace, doordir);
+		if (r == false)
+			continue;
+		v3s16 roomplace;
+		// X east, Z north, Y up
+#if 1
+		if (doordir == v3s16(1, 0, 0)) // X+
+			roomplace = doorplace +
+					v3s16(0, -1, random.range(-roomsize.Z + 2, -2));
+		if (doordir == v3s16(-1, 0, 0)) // X-
+			roomplace = doorplace +
+					v3s16(-roomsize.X + 1, -1, random.range(-roomsize.Z + 2, -2));
+		if (doordir == v3s16(0, 0, 1)) // Z+
+			roomplace = doorplace +
+					v3s16(random.range(-roomsize.X + 2, -2), -1, 0);
+		if (doordir == v3s16(0, 0, -1)) // Z-
+			roomplace = doorplace +
+					v3s16(random.range(-roomsize.X + 2, -2), -1, -roomsize.Z + 1);
+#endif
+#if 0
+		if (doordir == v3s16(1, 0, 0)) // X+
+			roomplace = doorplace + v3s16(0, -1, -roomsize.Z / 2);
+		if (doordir == v3s16(-1, 0, 0)) // X-
+			roomplace = doorplace + v3s16(-roomsize.X+1,-1,-roomsize.Z / 2);
+		if (doordir == v3s16(0, 0, 1)) // Z+
+			roomplace = doorplace + v3s16(-roomsize.X / 2, -1, 0);
+		if (doordir == v3s16(0, 0, -1)) // Z-
+			roomplace = doorplace + v3s16(-roomsize.X / 2, -1, -roomsize.Z + 1);
+#endif
+
+		// Check fit
+		bool fits = true;
+		for (s16 z = 1; z < roomsize.Z - 1; z++)
+		for (s16 y = 1; y < roomsize.Y - 1; y++)
+		for (s16 x = 1; x < roomsize.X - 1; x++)
+		{
+			v3s16 p = roomplace + v3s16(x, y, z);
+			if (vmanip->m_area.contains(p) == false)
+			{
+				fits = false;
+				break;
+			}
+			if (vmanip->m_flags[vmanip->m_area.index(p)]
+					& VMANIP_FLAG_DUNGEON_INSIDE)
+			{
+				fits = false;
+				break;
+			}
+		}
+		if(fits == false)
+		{
+			// Find new place
+			continue;
+		}
+		result_doorplace = doorplace;
+		result_doordir   = doordir;
+		result_roomplace = roomplace;
+		return true;
+	}
+	return false;
+}
+
+
+v3s16 rand_ortho_dir(PseudoRandom &random)
+{
+	if (random.next() % 2 == 0)
+		return random.next() % 2 ? v3s16(-1, 0, 0) : v3s16(1, 0, 0);
+	else
+		return random.next() % 2 ? v3s16(0, 0, -1) : v3s16(0, 0, 1);
+}
+
+
+v3s16 turn_xz(v3s16 olddir, int t)
+{
+	v3s16 dir;
+	if (t == 0)
+	{
+		// Turn right
+		dir.X = olddir.Z;
+		dir.Z = -olddir.X;
+		dir.Y = olddir.Y;
+	}
+	else
+	{
+		// Turn left
+		dir.X = -olddir.Z;
+		dir.Z = olddir.X;
+		dir.Y = olddir.Y;
+	}
+	return dir;
+}
+
+
+v3s16 random_turn(PseudoRandom &random, v3s16 olddir)
+{
+	int turn = random.range(0, 2);
+	v3s16 dir;
+	if (turn == 0)
+	{
+		// Go straight
+		dir = olddir;
+	}
+	else if (turn == 1)
+		// Turn right
+		dir = turn_xz(olddir, 0);
+	else
+		// Turn left
+		dir = turn_xz(olddir, 1);
+	return dir;
+}
+
+
+int dir_to_facedir(v3s16 d) {
+	if (abs(d.X) > abs(d.Z))
+		return d.X < 0 ? 3 : 1;
+	else
+		return d.Z < 0 ? 2 : 0;
+}
diff --git a/src/dungeongen.h b/src/dungeongen.h
new file mode 100644
index 0000000000000000000000000000000000000000..4be3df4aa8f05455401cb247b54d3bf1366e948d
--- /dev/null
+++ b/src/dungeongen.h
@@ -0,0 +1,128 @@
+/*
+Minetest
+Copyright (C) 2010-2013 celeron55, Perttu Ahola <celeron55@gmail.com>
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU Lesser General Public License as published by
+the Free Software Foundation; either version 2.1 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public License along
+with this program; if not, write to the Free Software Foundation, Inc.,
+51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+
+#ifndef DUNGEONGEN_HEADER
+#define DUNGEONGEN_HEADER
+
+#include "voxel.h"
+#include "noise.h"
+
+#define VMANIP_FLAG_DUNGEON_INSIDE VOXELFLAG_CHECKED1
+#define VMANIP_FLAG_DUNGEON_PRESERVE VOXELFLAG_CHECKED2
+#define VMANIP_FLAG_DUNGEON_UNTOUCHABLE (\
+		VMANIP_FLAG_DUNGEON_INSIDE|VMANIP_FLAG_DUNGEON_PRESERVE)
+
+class ManualMapVoxelManipulator;
+class INodeDefManager;
+
+v3s16 rand_ortho_dir(PseudoRandom &random);
+v3s16 turn_xz(v3s16 olddir, int t);
+v3s16 random_turn(PseudoRandom &random, v3s16 olddir);
+int dir_to_facedir(v3s16 d);
+
+class DungeonGen {
+public:
+	u32 blockseed;
+	u64 mapseed;
+	ManualMapVoxelManipulator *vmanip;
+	INodeDefManager *ndef;
+	PseudoRandom random;
+	v3s16 csize;
+	s16 water_level;
+	
+	NoiseParams *np_rarity;
+	NoiseParams *np_wetness;
+	NoiseParams *np_density;
+	
+	content_t cid_water_source;
+	content_t cid_cobble;
+	content_t cid_mossycobble;
+	content_t cid_torch;
+	content_t cid_cobblestair;
+	
+	//RoomWalker
+	v3s16 m_pos;
+	v3s16 m_dir;
+
+	DungeonGen(INodeDefManager *ndef, u64 seed, s16 waterlevel);
+	void generate(ManualMapVoxelManipulator *vm, u32 bseed,
+				  v3s16 full_node_min, v3s16 full_node_max);
+	//void generate(v3s16 full_node_min, v3s16 full_node_max, u32 bseed);
+	
+	void makeDungeon(v3s16 start_padding);
+	void makeRoom(v3s16 roomsize, v3s16 roomplace);
+	void makeCorridor(v3s16 doorplace, v3s16 doordir,
+					  v3s16 &result_place, v3s16 &result_dir);
+	void makeDoor(v3s16 doorplace, v3s16 doordir);
+	void makeFill(v3s16 place, v3s16 size, u8 avoid_flags, MapNode n, u8 or_flags);
+	void makeHole(v3s16 place);
+
+	bool findPlaceForDoor(v3s16 &result_place, v3s16 &result_dir);
+	bool findPlaceForRoomDoor(v3s16 roomsize, v3s16 &result_doorplace,
+			v3s16 &result_doordir, v3s16 &result_roomplace);
+			
+	void randomizeDir()
+	{
+		m_dir = rand_ortho_dir(random);
+	}
+};
+
+class RoomWalker
+{
+public:
+
+	RoomWalker(VoxelManipulator &vmanip_, v3s16 pos, PseudoRandom &random,
+			INodeDefManager *ndef):
+			vmanip(vmanip_),
+			m_pos(pos),
+			m_random(random),
+			m_ndef(ndef)
+	{
+		randomizeDir();
+	}
+
+	void randomizeDir()
+	{
+		m_dir = rand_ortho_dir(m_random);
+	}
+
+	void setPos(v3s16 pos)
+	{
+		m_pos = pos;
+	}
+
+	void setDir(v3s16 dir)
+	{
+		m_dir = dir;
+	}
+
+	//bool findPlaceForDoor(v3s16 &result_place, v3s16 &result_dir);
+	//bool findPlaceForRoomDoor(v3s16 roomsize, v3s16 &result_doorplace,
+	//		v3s16 &result_doordir, v3s16 &result_roomplace);
+
+private:
+	VoxelManipulator &vmanip;
+	v3s16 m_pos;
+	v3s16 m_dir;
+	PseudoRandom &m_random;
+	INodeDefManager *m_ndef;
+};
+
+
+#endif
diff --git a/src/emerge.cpp b/src/emerge.cpp
index 6c6863eff1829a16862a3068a2385bc423df9296..e4bd997cbe2149df6f6427044ce442b11f599ce3 100644
--- a/src/emerge.cpp
+++ b/src/emerge.cpp
@@ -39,6 +39,8 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include "biome.h"
 #include "emerge.h"
 #include "mapgen_v6.h"
+#include "mapgen_indev.h"
+#include "mapgen_singlenode.h"
 
 
 /////////////////////////////// Emerge Manager ////////////////////////////////
@@ -46,6 +48,8 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 EmergeManager::EmergeManager(IGameDef *gamedef, BiomeDefManager *bdef) {
 	//register built-in mapgens
 	registerMapgen("v6", new MapgenFactoryV6());
+	registerMapgen("indev", new MapgenFactoryIndev());
+	registerMapgen("singlenode", new MapgenFactorySinglenode());
 
 	this->biomedef = bdef ? bdef : new BiomeDefManager(gamedef);
 	this->params   = NULL;
@@ -359,7 +363,7 @@ void *EmergeThread::Thread() {
 		*/
 		BlockMakeData data;
 		MapBlock *block = NULL;
-		core::map<v3s16, MapBlock *> modified_blocks;
+		std::map<v3s16, MapBlock *> modified_blocks;
 		
 		if (getBlockOrStartGen(p, &block, &data, allow_generate)) {
 			{
@@ -415,13 +419,13 @@ void *EmergeThread::Thread() {
 		JMutexAutoLock lock(m_server->m_con_mutex);
 		// Add the originally fetched block to the modified list
 		if (block)
-			modified_blocks.insert(p, block);
+			modified_blocks[p] = block;
 
 		// Set the modified blocks unsent for all the clients
-		for (core::map<u16, RemoteClient*>::Iterator
-			 i = m_server->m_clients.getIterator();
-			 i.atEnd() == false; i++) {
-			RemoteClient *client = i.getNode()->getValue();
+		for (std::map<u16, RemoteClient*>::iterator
+			 i = m_server->m_clients.begin();
+			 i != m_server->m_clients.end(); ++i) {
+			RemoteClient *client = i->second;
 			if (modified_blocks.size() > 0) {
 				// Remove block from sent history
 				client->SetBlocksNotSent(modified_blocks);
diff --git a/src/emerge.h b/src/emerge.h
index 70b67e731f05360a3fc44198eaa5fd43a3d0bc9d..3d717bce3fd9f19a93746c3b8d7e67dcfabad97d 100644
--- a/src/emerge.h
+++ b/src/emerge.h
@@ -63,8 +63,9 @@ class EmergeManager {
 	std::map<v3s16, BlockEmergeData *> blocks_enqueued;
 	std::map<u16, u16> peer_queue_count;
 
-	//biome manager
+	//Mapgen-related structures
 	BiomeDefManager *biomedef;
+	std::vector<Ore *> ores;
 
 	EmergeManager(IGameDef *gamedef, BiomeDefManager *bdef);
 	~EmergeManager();
diff --git a/src/environment.cpp b/src/environment.cpp
index e939672e7b0b1e4894197a26b73fa88a58aa8934..07cdb24d10ec887b378e087702868328e0427108 100644
--- a/src/environment.cpp
+++ b/src/environment.cpp
@@ -58,8 +58,8 @@ Environment::Environment():
 Environment::~Environment()
 {
 	// Deallocate players
-	for(core::list<Player*>::Iterator i = m_players.begin();
-			i != m_players.end(); i++)
+	for(std::list<Player*>::iterator i = m_players.begin();
+			i != m_players.end(); ++i)
 	{
 		delete (*i);
 	}
@@ -86,8 +86,8 @@ void Environment::removePlayer(u16 peer_id)
 {
 	DSTACK(__FUNCTION_NAME);
 re_search:
-	for(core::list<Player*>::Iterator i = m_players.begin();
-			i != m_players.end(); i++)
+	for(std::list<Player*>::iterator i = m_players.begin();
+			i != m_players.end(); ++i)
 	{
 		Player *player = *i;
 		if(player->peer_id != peer_id)
@@ -103,8 +103,8 @@ void Environment::removePlayer(u16 peer_id)
 
 Player * Environment::getPlayer(u16 peer_id)
 {
-	for(core::list<Player*>::Iterator i = m_players.begin();
-			i != m_players.end(); i++)
+	for(std::list<Player*>::iterator i = m_players.begin();
+			i != m_players.end(); ++i)
 	{
 		Player *player = *i;
 		if(player->peer_id == peer_id)
@@ -115,8 +115,8 @@ Player * Environment::getPlayer(u16 peer_id)
 
 Player * Environment::getPlayer(const char *name)
 {
-	for(core::list<Player*>::Iterator i = m_players.begin();
-			i != m_players.end(); i++)
+	for(std::list<Player*>::iterator i = m_players.begin();
+			i != m_players.end(); ++i)
 	{
 		Player *player = *i;
 		if(strcmp(player->getName(), name) == 0)
@@ -127,12 +127,12 @@ Player * Environment::getPlayer(const char *name)
 
 Player * Environment::getRandomConnectedPlayer()
 {
-	core::list<Player*> connected_players = getPlayers(true);
+	std::list<Player*> connected_players = getPlayers(true);
 	u32 chosen_one = myrand() % connected_players.size();
 	u32 j = 0;
-	for(core::list<Player*>::Iterator
+	for(std::list<Player*>::iterator
 			i = connected_players.begin();
-			i != connected_players.end(); i++)
+			i != connected_players.end(); ++i)
 	{
 		if(j == chosen_one)
 		{
@@ -146,12 +146,12 @@ Player * Environment::getRandomConnectedPlayer()
 
 Player * Environment::getNearestConnectedPlayer(v3f pos)
 {
-	core::list<Player*> connected_players = getPlayers(true);
+	std::list<Player*> connected_players = getPlayers(true);
 	f32 nearest_d = 0;
 	Player *nearest_player = NULL;
-	for(core::list<Player*>::Iterator
+	for(std::list<Player*>::iterator
 			i = connected_players.begin();
-			i != connected_players.end(); i++)
+			i != connected_players.end(); ++i)
 	{
 		Player *player = *i;
 		f32 d = player->getPosition().getDistanceFrom(pos);
@@ -164,17 +164,17 @@ Player * Environment::getNearestConnectedPlayer(v3f pos)
 	return nearest_player;
 }
 
-core::list<Player*> Environment::getPlayers()
+std::list<Player*> Environment::getPlayers()
 {
 	return m_players;
 }
 
-core::list<Player*> Environment::getPlayers(bool ignore_disconnected)
+std::list<Player*> Environment::getPlayers(bool ignore_disconnected)
 {
-	core::list<Player*> newlist;
-	for(core::list<Player*>::Iterator
+	std::list<Player*> newlist;
+	for(std::list<Player*>::iterator
 			i = m_players.begin();
-			i != m_players.end(); i++)
+			i != m_players.end(); ++i)
 	{
 		Player *player = *i;
 		
@@ -193,7 +193,7 @@ core::list<Player*> Environment::getPlayers(bool ignore_disconnected)
 void Environment::printPlayers(std::ostream &o)
 {
 	o<<"Players in environment:"<<std::endl;
-	for(core::list<Player*>::Iterator i = m_players.begin();
+	for(std::list<Player*>::iterator i = m_players.begin();
 			i != m_players.end(); i++)
 	{
 		Player *player = *i;
@@ -251,7 +251,7 @@ ABMWithState::ABMWithState(ActiveBlockModifier *abm_):
 	ActiveBlockList
 */
 
-void fillRadiusBlock(v3s16 p0, s16 r, core::map<v3s16, bool> &list)
+void fillRadiusBlock(v3s16 p0, s16 r, std::set<v3s16> &list)
 {
 	v3s16 p;
 	for(p.X=p0.X-r; p.X<=p0.X+r; p.X++)
@@ -259,21 +259,21 @@ void fillRadiusBlock(v3s16 p0, s16 r, core::map<v3s16, bool> &list)
 	for(p.Z=p0.Z-r; p.Z<=p0.Z+r; p.Z++)
 	{
 		// Set in list
-		list[p] = true;
+		list.insert(p);
 	}
 }
 
-void ActiveBlockList::update(core::list<v3s16> &active_positions,
+void ActiveBlockList::update(std::list<v3s16> &active_positions,
 		s16 radius,
-		core::map<v3s16, bool> &blocks_removed,
-		core::map<v3s16, bool> &blocks_added)
+		std::set<v3s16> &blocks_removed,
+		std::set<v3s16> &blocks_added)
 {
 	/*
 		Create the new list
 	*/
-	core::map<v3s16, bool> newlist;
-	for(core::list<v3s16>::Iterator i = active_positions.begin();
-			i != active_positions.end(); i++)
+	std::set<v3s16> newlist;
+	for(std::list<v3s16>::iterator i = active_positions.begin();
+			i != active_positions.end(); ++i)
 	{
 		fillRadiusBlock(*i, radius, newlist);
 	}
@@ -282,37 +282,37 @@ void ActiveBlockList::update(core::list<v3s16> &active_positions,
 		Find out which blocks on the old list are not on the new list
 	*/
 	// Go through old list
-	for(core::map<v3s16, bool>::Iterator i = m_list.getIterator();
-			i.atEnd()==false; i++)
+	for(std::set<v3s16>::iterator i = m_list.begin();
+			i != m_list.end(); ++i)
 	{
-		v3s16 p = i.getNode()->getKey();
+		v3s16 p = *i;
 		// If not on new list, it's been removed
-		if(newlist.find(p) == NULL)
-			blocks_removed.insert(p, true);
+		if(newlist.find(p) == newlist.end())
+			blocks_removed.insert(p);
 	}
 
 	/*
 		Find out which blocks on the new list are not on the old list
 	*/
 	// Go through new list
-	for(core::map<v3s16, bool>::Iterator i = newlist.getIterator();
-			i.atEnd()==false; i++)
+	for(std::set<v3s16>::iterator i = newlist.begin();
+			i != newlist.end(); ++i)
 	{
-		v3s16 p = i.getNode()->getKey();
+		v3s16 p = *i;
 		// If not on old list, it's been added
-		if(m_list.find(p) == NULL)
-			blocks_added.insert(p, true);
+		if(m_list.find(p) == m_list.end())
+			blocks_added.insert(p);
 	}
 
 	/*
 		Update m_list
 	*/
 	m_list.clear();
-	for(core::map<v3s16, bool>::Iterator i = newlist.getIterator();
-			i.atEnd()==false; i++)
+	for(std::set<v3s16>::iterator i = newlist.begin();
+			i != newlist.end(); ++i)
 	{
-		v3s16 p = i.getNode()->getKey();
-		m_list.insert(p, true);
+		v3s16 p = *i;
+		m_list.insert(p);
 	}
 }
 
@@ -348,8 +348,8 @@ ServerEnvironment::~ServerEnvironment()
 	m_map->drop();
 
 	// Delete ActiveBlockModifiers
-	for(core::list<ABMWithState>::Iterator
-			i = m_abms.begin(); i != m_abms.end(); i++){
+	for(std::list<ABMWithState>::iterator
+			i = m_abms.begin(); i != m_abms.end(); ++i){
 		delete i->abm;
 	}
 }
@@ -370,7 +370,7 @@ void ServerEnvironment::serializePlayers(const std::string &savedir)
 	std::string players_path = savedir + "/players";
 	fs::CreateDir(players_path);
 
-	core::map<Player*, bool> saved_players;
+	std::set<Player*> saved_players;
 
 	std::vector<fs::DirListNode> player_files = fs::GetDirListing(players_path);
 	for(u32 i=0; i<player_files.size(); i++)
@@ -419,15 +419,15 @@ void ServerEnvironment::serializePlayers(const std::string &savedir)
 				continue;
 			}
 			player->serialize(os);
-			saved_players.insert(player, true);
+			saved_players.insert(player);
 		}
 	}
 
-	for(core::list<Player*>::Iterator i = m_players.begin();
-			i != m_players.end(); i++)
+	for(std::list<Player*>::iterator i = m_players.begin();
+			i != m_players.end(); ++i)
 	{
 		Player *player = *i;
-		if(saved_players.find(player) != NULL)
+		if(saved_players.find(player) != saved_players.end())
 		{
 			/*infostream<<"Player "<<player->getName()
 					<<" was already saved."<<std::endl;*/
@@ -473,7 +473,7 @@ void ServerEnvironment::serializePlayers(const std::string &savedir)
 				continue;
 			}
 			player->serialize(os);
-			saved_players.insert(player, true);
+			saved_players.insert(player);
 		}
 	}
 
@@ -484,8 +484,6 @@ void ServerEnvironment::deSerializePlayers(const std::string &savedir)
 {
 	std::string players_path = savedir + "/players";
 
-	core::map<Player*, bool> saved_players;
-
 	std::vector<fs::DirListNode> player_files = fs::GetDirListing(players_path);
 	for(u32 i=0; i<player_files.size(); i++)
 	{
@@ -627,7 +625,7 @@ class ABMHandler
 	ServerEnvironment *m_env;
 	std::map<content_t, std::list<ActiveABM> > m_aabms;
 public:
-	ABMHandler(core::list<ABMWithState> &abms,
+	ABMHandler(std::list<ABMWithState> &abms,
 			float dtime_s, ServerEnvironment *env,
 			bool use_timers):
 		m_env(env)
@@ -635,8 +633,8 @@ class ABMHandler
 		if(dtime_s < 0.001)
 			return;
 		INodeDefManager *ndef = env->getGameDef()->ndef();
-		for(core::list<ABMWithState>::Iterator
-				i = abms.begin(); i != abms.end(); i++){
+		for(std::list<ABMWithState>::iterator
+				i = abms.begin(); i != abms.end(); ++i){
 			ActiveBlockModifier *abm = i->abm;
 			float trigger_interval = abm->getTriggerInterval();
 			if(trigger_interval < 0.001)
@@ -862,12 +860,12 @@ bool ServerEnvironment::removeNode(v3s16 p)
 std::set<u16> ServerEnvironment::getObjectsInsideRadius(v3f pos, float radius)
 {
 	std::set<u16> objects;
-	for(core::map<u16, ServerActiveObject*>::Iterator
-			i = m_active_objects.getIterator();
-			i.atEnd()==false; i++)
+	for(std::map<u16, ServerActiveObject*>::iterator
+			i = m_active_objects.begin();
+			i != m_active_objects.end(); ++i)
 	{
-		ServerActiveObject* obj = i.getNode()->getValue();
-		u16 id = i.getNode()->getKey();
+		ServerActiveObject* obj = i->second;
+		u16 id = i->first;
 		v3f objectpos = obj->getBasePosition();
 		if(objectpos.getDistanceFrom(pos) > radius)
 			continue;
@@ -880,16 +878,16 @@ void ServerEnvironment::clearAllObjects()
 {
 	infostream<<"ServerEnvironment::clearAllObjects(): "
 			<<"Removing all active objects"<<std::endl;
-	core::list<u16> objects_to_remove;
-	for(core::map<u16, ServerActiveObject*>::Iterator
-			i = m_active_objects.getIterator();
-			i.atEnd()==false; i++)
+	std::list<u16> objects_to_remove;
+	for(std::map<u16, ServerActiveObject*>::iterator
+			i = m_active_objects.begin();
+			i != m_active_objects.end(); ++i)
 	{
-		ServerActiveObject* obj = i.getNode()->getValue();
+		ServerActiveObject* obj = i->second;
 		if(obj->getType() == ACTIVEOBJECT_TYPE_PLAYER)
 			continue;
-		u16 id = i.getNode()->getKey();		
-		v3f objectpos = obj->getBasePosition();	
+		u16 id = i->first;
+		v3f objectpos = obj->getBasePosition();
 		// Delete static object if block is loaded
 		if(obj->m_static_exists){
 			MapBlock *block = m_map->getBlockNoCreateNoEx(obj->m_static_block);
@@ -919,13 +917,13 @@ void ServerEnvironment::clearAllObjects()
 		objects_to_remove.push_back(id);
 	}
 	// Remove references from m_active_objects
-	for(core::list<u16>::Iterator i = objects_to_remove.begin();
-			i != objects_to_remove.end(); i++)
+	for(std::list<u16>::iterator i = objects_to_remove.begin();
+			i != objects_to_remove.end(); ++i)
 	{
-		m_active_objects.remove(*i);
+		m_active_objects.erase(*i);
 	}
 
-	core::list<v3s16> loadable_blocks;
+	std::list<v3s16> loadable_blocks;
 	infostream<<"ServerEnvironment::clearAllObjects(): "
 			<<"Listing all loadable blocks"<<std::endl;
 	m_map->listAllLoadableBlocks(loadable_blocks);
@@ -937,8 +935,8 @@ void ServerEnvironment::clearAllObjects()
 	u32 num_blocks_checked = 0;
 	u32 num_blocks_cleared = 0;
 	u32 num_objs_cleared = 0;
-	for(core::list<v3s16>::Iterator i = loadable_blocks.begin();
-			i != loadable_blocks.end(); i++)
+	for(std::list<v3s16>::iterator i = loadable_blocks.begin();
+			i != loadable_blocks.end(); ++i)
 	{
 		v3s16 p = *i;
 		MapBlock *block = m_map->emergeBlock(p, false);
@@ -1002,8 +1000,8 @@ void ServerEnvironment::step(float dtime)
 	*/
 	{
 		ScopeProfiler sp(g_profiler, "SEnv: handle players avg", SPT_AVG);
-		for(core::list<Player*>::Iterator i = m_players.begin();
-				i != m_players.end(); i++)
+		for(std::list<Player*>::iterator i = m_players.begin();
+				i != m_players.end(); ++i)
 		{
 			Player *player = *i;
 			
@@ -1027,10 +1025,10 @@ void ServerEnvironment::step(float dtime)
 		/*
 			Get player block positions
 		*/
-		core::list<v3s16> players_blockpos;
-		for(core::list<Player*>::Iterator
+		std::list<v3s16> players_blockpos;
+		for(std::list<Player*>::iterator
 				i = m_players.begin();
-				i != m_players.end(); i++)
+				i != m_players.end(); ++i)
 		{
 			Player *player = *i;
 			// Ignore disconnected players
@@ -1045,8 +1043,8 @@ void ServerEnvironment::step(float dtime)
 			Update list of active blocks, collecting changes
 		*/
 		const s16 active_block_range = g_settings->getS16("active_block_range");
-		core::map<v3s16, bool> blocks_removed;
-		core::map<v3s16, bool> blocks_added;
+		std::set<v3s16> blocks_removed;
+		std::set<v3s16> blocks_added;
 		m_active_blocks.update(players_blockpos, active_block_range,
 				blocks_removed, blocks_added);
 
@@ -1057,11 +1055,11 @@ void ServerEnvironment::step(float dtime)
 		// Convert active objects that are no more in active blocks to static
 		deactivateFarObjects(false);
 		
-		for(core::map<v3s16, bool>::Iterator
-				i = blocks_removed.getIterator();
-				i.atEnd()==false; i++)
+		for(std::set<v3s16>::iterator
+				i = blocks_removed.begin();
+				i != blocks_removed.end(); ++i)
 		{
-			v3s16 p = i.getNode()->getKey();
+			v3s16 p = *i;
 
 			/*infostream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
 					<<") became inactive"<<std::endl;*/
@@ -1078,11 +1076,11 @@ void ServerEnvironment::step(float dtime)
 			Handle added blocks
 		*/
 
-		for(core::map<v3s16, bool>::Iterator
-				i = blocks_added.getIterator();
-				i.atEnd()==false; i++)
+		for(std::set<v3s16>::iterator
+				i = blocks_added.begin();
+				i != blocks_added.end(); ++i)
 		{
-			v3s16 p = i.getNode()->getKey();
+			v3s16 p = *i;
 			
 			/*infostream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
 					<<") became active"<<std::endl;*/
@@ -1091,7 +1089,7 @@ void ServerEnvironment::step(float dtime)
 			if(block==NULL){
 				// Block needs to be fetched first
 				m_emerger->queueBlockEmerge(p, false);
-				m_active_blocks.m_list.remove(p);
+				m_active_blocks.m_list.erase(p);
 				continue;
 			}
 
@@ -1108,11 +1106,11 @@ void ServerEnvironment::step(float dtime)
 		
 		float dtime = 1.0;
 
-		for(core::map<v3s16, bool>::Iterator
-				i = m_active_blocks.m_list.getIterator();
-				i.atEnd()==false; i++)
+		for(std::set<v3s16>::iterator
+				i = m_active_blocks.m_list.begin();
+				i != m_active_blocks.m_list.end(); ++i)
 		{
-			v3s16 p = i.getNode()->getKey();
+			v3s16 p = *i;
 			
 			/*infostream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
 					<<") being handled"<<std::endl;*/
@@ -1163,11 +1161,11 @@ void ServerEnvironment::step(float dtime)
 		// Initialize handling of ActiveBlockModifiers
 		ABMHandler abmhandler(m_abms, abm_interval, this, true);
 
-		for(core::map<v3s16, bool>::Iterator
-				i = m_active_blocks.m_list.getIterator();
-				i.atEnd()==false; i++)
+		for(std::set<v3s16>::iterator
+				i = m_active_blocks.m_list.begin();
+				i != m_active_blocks.m_list.end(); ++i)
 		{
-			v3s16 p = i.getNode()->getKey();
+			v3s16 p = *i;
 			
 			/*infostream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
 					<<") being handled"<<std::endl;*/
@@ -1216,11 +1214,11 @@ void ServerEnvironment::step(float dtime)
 			send_recommended = true;
 		}
 
-		for(core::map<u16, ServerActiveObject*>::Iterator
-				i = m_active_objects.getIterator();
-				i.atEnd()==false; i++)
+		for(std::map<u16, ServerActiveObject*>::iterator
+				i = m_active_objects.begin();
+				i != m_active_objects.end(); ++i)
 		{
-			ServerActiveObject* obj = i.getNode()->getValue();
+			ServerActiveObject* obj = i->second;
 			// Remove non-peaceful mobs on peaceful mode
 			if(g_settings->getBool("only_peaceful_mobs")){
 				if(!obj->isPeaceful())
@@ -1232,7 +1230,7 @@ void ServerEnvironment::step(float dtime)
 			// Step object
 			obj->step(dtime, send_recommended);
 			// Read messages from object
-			while(obj->m_messages_out.size() > 0)
+			while(!obj->m_messages_out.empty())
 			{
 				m_active_object_messages.push_back(
 						obj->m_messages_out.pop_front());
@@ -1255,31 +1253,24 @@ void ServerEnvironment::step(float dtime)
 
 ServerActiveObject* ServerEnvironment::getActiveObject(u16 id)
 {
-	core::map<u16, ServerActiveObject*>::Node *n;
+	std::map<u16, ServerActiveObject*>::iterator n;
 	n = m_active_objects.find(id);
-	if(n == NULL)
+	if(n == m_active_objects.end())
 		return NULL;
-	return n->getValue();
+	return n->second;
 }
 
 bool isFreeServerActiveObjectId(u16 id,
-		core::map<u16, ServerActiveObject*> &objects)
+		std::map<u16, ServerActiveObject*> &objects)
 {
 	if(id == 0)
 		return false;
-	
-	for(core::map<u16, ServerActiveObject*>::Iterator
-			i = objects.getIterator();
-			i.atEnd()==false; i++)
-	{
-		if(i.getNode()->getKey() == id)
-			return false;
-	}
-	return true;
+
+	return objects.find(id) == objects.end();
 }
 
 u16 getFreeServerActiveObjectId(
-		core::map<u16, ServerActiveObject*> &objects)
+		std::map<u16, ServerActiveObject*> &objects)
 {
 	u16 new_id = 1;
 	for(;;)
@@ -1351,8 +1342,8 @@ bool ServerEnvironment::addActiveObjectAsStatic(ServerActiveObject *obj)
 	inside a radius around a position
 */
 void ServerEnvironment::getAddedActiveObjects(v3s16 pos, s16 radius,
-		core::map<u16, bool> &current_objects,
-		core::map<u16, bool> &added_objects)
+		std::set<u16> &current_objects,
+		std::set<u16> &added_objects)
 {
 	v3f pos_f = intToFloat(pos, BS);
 	f32 radius_f = radius * BS;
@@ -1363,13 +1354,13 @@ void ServerEnvironment::getAddedActiveObjects(v3s16 pos, s16 radius,
 		- discard objects that are found in current_objects.
 		- add remaining objects to added_objects
 	*/
-	for(core::map<u16, ServerActiveObject*>::Iterator
-			i = m_active_objects.getIterator();
-			i.atEnd()==false; i++)
+	for(std::map<u16, ServerActiveObject*>::iterator
+			i = m_active_objects.begin();
+			i != m_active_objects.end(); ++i)
 	{
-		u16 id = i.getNode()->getKey();
+		u16 id = i->first;
 		// Get object
-		ServerActiveObject *object = i.getNode()->getValue();
+		ServerActiveObject *object = i->second;
 		if(object == NULL)
 			continue;
 		// Discard if removed
@@ -1382,12 +1373,12 @@ void ServerEnvironment::getAddedActiveObjects(v3s16 pos, s16 radius,
 				continue;
 		}
 		// Discard if already on current_objects
-		core::map<u16, bool>::Node *n;
+		std::set<u16>::iterator n;
 		n = current_objects.find(id);
-		if(n != NULL)
+		if(n != current_objects.end())
 			continue;
 		// Add to added_objects
-		added_objects.insert(id, false);
+		added_objects.insert(id);
 	}
 }
 
@@ -1396,8 +1387,8 @@ void ServerEnvironment::getAddedActiveObjects(v3s16 pos, s16 radius,
 	inside a radius around a position
 */
 void ServerEnvironment::getRemovedActiveObjects(v3s16 pos, s16 radius,
-		core::map<u16, bool> &current_objects,
-		core::map<u16, bool> &removed_objects)
+		std::set<u16> &current_objects,
+		std::set<u16> &removed_objects)
 {
 	v3f pos_f = intToFloat(pos, BS);
 	f32 radius_f = radius * BS;
@@ -1409,23 +1400,23 @@ void ServerEnvironment::getRemovedActiveObjects(v3s16 pos, s16 radius,
 		- object has m_removed=true, or
 		- object is too far away
 	*/
-	for(core::map<u16, bool>::Iterator
-			i = current_objects.getIterator();
-			i.atEnd()==false; i++)
+	for(std::set<u16>::iterator
+			i = current_objects.begin();
+			i != current_objects.end(); ++i)
 	{
-		u16 id = i.getNode()->getKey();
+		u16 id = *i;
 		ServerActiveObject *object = getActiveObject(id);
 
 		if(object == NULL){
 			infostream<<"ServerEnvironment::getRemovedActiveObjects():"
 					<<" object in current_objects is NULL"<<std::endl;
-			removed_objects.insert(id, false);
+			removed_objects.insert(id);
 			continue;
 		}
 
 		if(object->m_removed)
 		{
-			removed_objects.insert(id, false);
+			removed_objects.insert(id);
 			continue;
 		}
 		
@@ -1437,7 +1428,7 @@ void ServerEnvironment::getRemovedActiveObjects(v3s16 pos, s16 radius,
 
 		if(distance_f >= radius_f)
 		{
-			removed_objects.insert(id, false);
+			removed_objects.insert(id);
 			continue;
 		}
 		
@@ -1447,7 +1438,7 @@ void ServerEnvironment::getRemovedActiveObjects(v3s16 pos, s16 radius,
 
 ActiveObjectMessage ServerEnvironment::getActiveObjectMessage()
 {
-	if(m_active_object_messages.size() == 0)
+	if(m_active_object_messages.empty())
 		return ActiveObjectMessage(0);
 	
 	return m_active_object_messages.pop_front();
@@ -1488,7 +1479,7 @@ u16 ServerEnvironment::addActiveObjectRaw(ServerActiveObject *object,
 	/*infostream<<"ServerEnvironment::addActiveObjectRaw(): "
 			<<"added (id="<<object->getId()<<")"<<std::endl;*/
 			
-	m_active_objects.insert(object->getId(), object);
+	m_active_objects[object->getId()] = object;
   
 	verbosestream<<"ServerEnvironment::addActiveObjectRaw(): "
 			<<"Added id="<<object->getId()<<"; there are now "
@@ -1512,7 +1503,7 @@ u16 ServerEnvironment::addActiveObjectRaw(ServerActiveObject *object,
 		MapBlock *block = m_map->getBlockNoCreateNoEx(blockpos);
 		if(block)
 		{
-			block->m_static_objects.m_active.insert(object->getId(), s_obj);
+			block->m_static_objects.m_active[object->getId()] = s_obj;
 			object->m_static_exists = true;
 			object->m_static_block = blockpos;
 
@@ -1536,13 +1527,13 @@ u16 ServerEnvironment::addActiveObjectRaw(ServerActiveObject *object,
 */
 void ServerEnvironment::removeRemovedObjects()
 {
-	core::list<u16> objects_to_remove;
-	for(core::map<u16, ServerActiveObject*>::Iterator
-			i = m_active_objects.getIterator();
-			i.atEnd()==false; i++)
+	std::list<u16> objects_to_remove;
+	for(std::map<u16, ServerActiveObject*>::iterator
+			i = m_active_objects.begin();
+			i != m_active_objects.end(); ++i)
 	{
-		u16 id = i.getNode()->getKey();
-		ServerActiveObject* obj = i.getNode()->getValue();
+		u16 id = i->first;
+		ServerActiveObject* obj = i->second;
 		// This shouldn't happen but check it
 		if(obj == NULL)
 		{
@@ -1593,10 +1584,10 @@ void ServerEnvironment::removeRemovedObjects()
 		objects_to_remove.push_back(id);
 	}
 	// Remove references from m_active_objects
-	for(core::list<u16>::Iterator i = objects_to_remove.begin();
-			i != objects_to_remove.end(); i++)
+	for(std::list<u16>::iterator i = objects_to_remove.begin();
+			i != objects_to_remove.end(); ++i)
 	{
-		m_active_objects.remove(*i);
+		m_active_objects.erase(*i);
 	}
 }
 
@@ -1663,11 +1654,11 @@ void ServerEnvironment::activateObjects(MapBlock *block, u32 dtime_s)
 	}
 	// A list for objects that couldn't be converted to active for some
 	// reason. They will be stored back.
-	core::list<StaticObject> new_stored;
+	std::list<StaticObject> new_stored;
 	// Loop through stored static objects
-	for(core::list<StaticObject>::Iterator
+	for(std::list<StaticObject>::iterator
 			i = block->m_static_objects.m_stored.begin();
-			i != block->m_static_objects.m_stored.end(); i++)
+			i != block->m_static_objects.m_stored.end(); ++i)
 	{
 		/*infostream<<"Server: Creating an active object from "
 				<<"static data"<<std::endl;*/
@@ -1696,9 +1687,9 @@ void ServerEnvironment::activateObjects(MapBlock *block, u32 dtime_s)
 	// Clear stored list
 	block->m_static_objects.m_stored.clear();
 	// Add leftover failed stuff to stored list
-	for(core::list<StaticObject>::Iterator
+	for(std::list<StaticObject>::iterator
 			i = new_stored.begin();
-			i != new_stored.end(); i++)
+			i != new_stored.end(); ++i)
 	{
 		StaticObject &s_obj = *i;
 		block->m_static_objects.m_stored.push_back(s_obj);
@@ -1726,12 +1717,12 @@ void ServerEnvironment::activateObjects(MapBlock *block, u32 dtime_s)
 */
 void ServerEnvironment::deactivateFarObjects(bool force_delete)
 {
-	core::list<u16> objects_to_remove;
-	for(core::map<u16, ServerActiveObject*>::Iterator
-			i = m_active_objects.getIterator();
-			i.atEnd()==false; i++)
+	std::list<u16> objects_to_remove;
+	for(std::map<u16, ServerActiveObject*>::iterator
+			i = m_active_objects.begin();
+			i != m_active_objects.end(); ++i)
 	{
-		ServerActiveObject* obj = i.getNode()->getValue();
+		ServerActiveObject* obj = i->second;
 		assert(obj);
 		
 		// Do not deactivate if static data creation not allowed
@@ -1742,7 +1733,7 @@ void ServerEnvironment::deactivateFarObjects(bool force_delete)
 		if(!force_delete && obj->m_pending_deactivation)
 			continue;
 
-		u16 id = i.getNode()->getKey();		
+		u16 id = i->first;
 		v3f objectpos = obj->getBasePosition();	
 
 		// The block in which the object resides in
@@ -1778,10 +1769,10 @@ void ServerEnvironment::deactivateFarObjects(bool force_delete)
 
 				MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
 				
-				core::map<u16, StaticObject>::Node *n =
+				std::map<u16, StaticObject>::iterator n =
 						block->m_static_objects.m_active.find(id);
-				if(n){
-					StaticObject static_old = n->getValue();
+				if(n != block->m_static_objects.m_active.end()){
+					StaticObject static_old = n->second;
 
 					float save_movem = obj->getMinimumSavedMovement();
 
@@ -1840,7 +1831,7 @@ void ServerEnvironment::deactivateFarObjects(bool force_delete)
 					// This shouldn't happen, but happens rarely for some
 					// unknown reason. Unsuccessful attempts have been made to
 					// find said reason.
-					if(new_id && block->m_static_objects.m_active.find(new_id)){
+					if(new_id && block->m_static_objects.m_active.find(new_id) != block->m_static_objects.m_active.end()){
 						infostream<<"ServerEnv: WARNING: Performing hack #83274"
 								<<std::endl;
 						block->m_static_objects.remove(new_id);
@@ -1900,10 +1891,10 @@ void ServerEnvironment::deactivateFarObjects(bool force_delete)
 	}
 
 	// Remove references from m_active_objects
-	for(core::list<u16>::Iterator i = objects_to_remove.begin();
-			i != objects_to_remove.end(); i++)
+	for(std::list<u16>::iterator i = objects_to_remove.begin();
+			i != objects_to_remove.end(); ++i)
 	{
-		m_active_objects.remove(*i);
+		m_active_objects.erase(*i);
 	}
 }
 
@@ -1930,15 +1921,15 @@ ClientEnvironment::ClientEnvironment(ClientMap *map, scene::ISceneManager *smgr,
 ClientEnvironment::~ClientEnvironment()
 {
 	// delete active objects
-	for(core::map<u16, ClientActiveObject*>::Iterator
-			i = m_active_objects.getIterator();
-			i.atEnd()==false; i++)
+	for(std::map<u16, ClientActiveObject*>::iterator
+			i = m_active_objects.begin();
+			i != m_active_objects.end(); ++i)
 	{
-		delete i.getNode()->getValue();
+		delete i->second;
 	}
 
-	for(core::list<ClientSimpleObject*>::Iterator
-			i = m_simple_objects.begin(); i != m_simple_objects.end(); i++)
+	for(std::list<ClientSimpleObject*>::iterator
+			i = m_simple_objects.begin(); i != m_simple_objects.end(); ++i)
 	{
 		delete *i;
 	}
@@ -1971,8 +1962,8 @@ void ClientEnvironment::addPlayer(Player *player)
 
 LocalPlayer * ClientEnvironment::getLocalPlayer()
 {
-	for(core::list<Player*>::Iterator i = m_players.begin();
-			i != m_players.end(); i++)
+	for(std::list<Player*>::iterator i = m_players.begin();
+			i != m_players.end(); ++i)
 	{
 		Player *player = *i;
 		if(player->isLocal())
@@ -1996,7 +1987,7 @@ void ClientEnvironment::step(float dtime)
 	LocalPlayer *lplayer = getLocalPlayer();
 	assert(lplayer);
 	// collision info queue
-	core::list<CollisionInfo> player_collisions;
+	std::list<CollisionInfo> player_collisions;
 	
 	/*
 		Get the speed the player is going
@@ -2105,7 +2096,7 @@ void ClientEnvironment::step(float dtime)
 				Move the lplayer.
 				This also does collision detection.
 			*/
-			lplayer->move(dtime_part, *m_map, position_max_increment,
+			lplayer->move(dtime_part, this, position_max_increment,
 					&player_collisions);
 		}
 	}
@@ -2113,9 +2104,9 @@ void ClientEnvironment::step(float dtime)
 		
 	//std::cout<<"Looped "<<loopcount<<" times."<<std::endl;
 	
-	for(core::list<CollisionInfo>::Iterator
+	for(std::list<CollisionInfo>::iterator
 			i = player_collisions.begin();
-			i != player_collisions.end(); i++)
+			i != player_collisions.end(); ++i)
 	{
 		CollisionInfo &info = *i;
 		v3f speed_diff = info.new_speed - info.old_speed;;
@@ -2179,8 +2170,8 @@ void ClientEnvironment::step(float dtime)
 	/*
 		Stuff that can be done in an arbitarily large dtime
 	*/
-	for(core::list<Player*>::Iterator i = m_players.begin();
-			i != m_players.end(); i++)
+	for(std::list<Player*>::iterator i = m_players.begin();
+			i != m_players.end(); ++i)
 	{
 		Player *player = *i;
 		v3f playerpos = player->getPosition();
@@ -2214,11 +2205,11 @@ void ClientEnvironment::step(float dtime)
 	*/
 	
 	bool update_lighting = m_active_object_light_update_interval.step(dtime, 0.21);
-	for(core::map<u16, ClientActiveObject*>::Iterator
-			i = m_active_objects.getIterator();
-			i.atEnd()==false; i++)
+	for(std::map<u16, ClientActiveObject*>::iterator
+			i = m_active_objects.begin();
+			i != m_active_objects.end(); ++i)
 	{
-		ClientActiveObject* obj = i.getNode()->getValue();
+		ClientActiveObject* obj = i->second;
 		// Step object
 		obj->step(dtime, this);
 
@@ -2242,12 +2233,12 @@ void ClientEnvironment::step(float dtime)
 	/*
 		Step and handle simple objects
 	*/
-	for(core::list<ClientSimpleObject*>::Iterator
+	for(std::list<ClientSimpleObject*>::iterator
 			i = m_simple_objects.begin(); i != m_simple_objects.end();)
 	{
 		ClientSimpleObject *simple = *i;
-		core::list<ClientSimpleObject*>::Iterator cur = i;
-		i++;
+		std::list<ClientSimpleObject*>::iterator cur = i;
+		++i;
 		simple->step(dtime);
 		if(simple->m_to_be_removed){
 			delete simple;
@@ -2263,31 +2254,24 @@ void ClientEnvironment::addSimpleObject(ClientSimpleObject *simple)
 
 ClientActiveObject* ClientEnvironment::getActiveObject(u16 id)
 {
-	core::map<u16, ClientActiveObject*>::Node *n;
+	std::map<u16, ClientActiveObject*>::iterator n;
 	n = m_active_objects.find(id);
-	if(n == NULL)
+	if(n == m_active_objects.end())
 		return NULL;
-	return n->getValue();
+	return n->second;
 }
 
 bool isFreeClientActiveObjectId(u16 id,
-		core::map<u16, ClientActiveObject*> &objects)
+		std::map<u16, ClientActiveObject*> &objects)
 {
 	if(id == 0)
 		return false;
-	
-	for(core::map<u16, ClientActiveObject*>::Iterator
-			i = objects.getIterator();
-			i.atEnd()==false; i++)
-	{
-		if(i.getNode()->getKey() == id)
-			return false;
-	}
-	return true;
+
+	return objects.find(id) == objects.end();
 }
 
 u16 getFreeClientActiveObjectId(
-		core::map<u16, ClientActiveObject*> &objects)
+		std::map<u16, ClientActiveObject*> &objects)
 {
 	u16 new_id = 1;
 	for(;;)
@@ -2326,7 +2310,7 @@ u16 ClientEnvironment::addActiveObject(ClientActiveObject *object)
 	}
 	infostream<<"ClientEnvironment::addActiveObject(): "
 			<<"added (id="<<object->getId()<<")"<<std::endl;
-	m_active_objects.insert(object->getId(), object);
+	m_active_objects[object->getId()] = object;
 	object->addToScene(m_smgr, m_texturesource, m_irr);
 	{ // Update lighting immediately
 		u8 light = 0;
@@ -2389,7 +2373,7 @@ void ClientEnvironment::removeActiveObject(u16 id)
 	}
 	obj->removeFromScene(true);
 	delete obj;
-	m_active_objects.remove(id);
+	m_active_objects.erase(id);
 }
 
 void ClientEnvironment::processActiveObjectMessage(u16 id,
@@ -2445,13 +2429,13 @@ void ClientEnvironment::damageLocalPlayer(u8 damage, bool handle_hp)
 */
 	
 void ClientEnvironment::getActiveObjects(v3f origin, f32 max_d,
-		core::array<DistanceSortedActiveObject> &dest)
+		std::vector<DistanceSortedActiveObject> &dest)
 {
-	for(core::map<u16, ClientActiveObject*>::Iterator
-			i = m_active_objects.getIterator();
-			i.atEnd()==false; i++)
+	for(std::map<u16, ClientActiveObject*>::iterator
+			i = m_active_objects.begin();
+			i != m_active_objects.end(); ++i)
 	{
-		ClientActiveObject* obj = i.getNode()->getValue();
+		ClientActiveObject* obj = i->second;
 
 		f32 d = (obj->getPosition() - origin).getLength();
 
@@ -2466,7 +2450,7 @@ void ClientEnvironment::getActiveObjects(v3f origin, f32 max_d,
 
 ClientEnvEvent ClientEnvironment::getClientEvent()
 {
-	if(m_client_event_queue.size() == 0)
+	if(m_client_event_queue.empty())
 	{
 		ClientEnvEvent event;
 		event.type = CEE_NONE;
diff --git a/src/environment.h b/src/environment.h
index 07a4d7635673dd40dcee52f1420f2c2f236c1065..02301e5d3027b07f74997b5cbf91170e2b22ba1f 100644
--- a/src/environment.h
+++ b/src/environment.h
@@ -31,6 +31,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 */
 
 #include <set>
+#include <list>
 #include "irrlichttypes_extrabloated.h"
 #include "player.h"
 #include <ostream>
@@ -73,8 +74,8 @@ class Environment
 	Player * getPlayer(const char *name);
 	Player * getRandomConnectedPlayer();
 	Player * getNearestConnectedPlayer(v3f pos);
-	core::list<Player*> getPlayers();
-	core::list<Player*> getPlayers(bool ignore_disconnected);
+	std::list<Player*> getPlayers();
+	std::list<Player*> getPlayers(bool ignore_disconnected);
 	void printPlayers(std::ostream &o);
 	
 	u32 getDayNightRatio();
@@ -102,7 +103,7 @@ class Environment
 
 protected:
 	// peer_ids in here should be unique, except that there may be many 0s
-	core::list<Player*> m_players;
+	std::list<Player*> m_players;
 	// Time of day in milli-hours (0-23999); determines day and night
 	u32 m_time_of_day;
 	// Time of day in 0...1
@@ -156,20 +157,20 @@ struct ABMWithState
 class ActiveBlockList
 {
 public:
-	void update(core::list<v3s16> &active_positions,
+	void update(std::list<v3s16> &active_positions,
 			s16 radius,
-			core::map<v3s16, bool> &blocks_removed,
-			core::map<v3s16, bool> &blocks_added);
+			std::set<v3s16> &blocks_removed,
+			std::set<v3s16> &blocks_added);
 
 	bool contains(v3s16 p){
-		return (m_list.find(p) != NULL);
+		return (m_list.find(p) != m_list.end());
 	}
 
 	void clear(){
 		m_list.clear();
 	}
 
-	core::map<v3s16, bool> m_list;
+	std::set<v3s16> m_list;
 
 private:
 };
@@ -249,16 +250,16 @@ class ServerEnvironment : public Environment
 		inside a radius around a position
 	*/
 	void getAddedActiveObjects(v3s16 pos, s16 radius,
-			core::map<u16, bool> &current_objects,
-			core::map<u16, bool> &added_objects);
+			std::set<u16> &current_objects,
+			std::set<u16> &added_objects);
 
 	/*
 		Find out what new objects have been removed from
 		inside a radius around a position
 	*/
 	void getRemovedActiveObjects(v3s16 pos, s16 radius,
-			core::map<u16, bool> &current_objects,
-			core::map<u16, bool> &removed_objects);
+			std::set<u16> &current_objects,
+			std::set<u16> &removed_objects);
 	
 	/*
 		Get the next message emitted by some active object.
@@ -350,7 +351,7 @@ class ServerEnvironment : public Environment
 	// Background block emerger (the server, in practice)
 	IBackgroundBlockEmerger *m_emerger;
 	// Active object list
-	core::map<u16, ServerActiveObject*> m_active_objects;
+	std::map<u16, ServerActiveObject*> m_active_objects;
 	// Outgoing network message buffer for active objects
 	Queue<ActiveObjectMessage> m_active_object_messages;
 	// Some timers
@@ -368,7 +369,7 @@ class ServerEnvironment : public Environment
 	u32 m_game_time;
 	// A helper variable for incrementing the latter
 	float m_game_time_fraction_counter;
-	core::list<ABMWithState> m_abms;
+	std::list<ABMWithState> m_abms;
 	// An interval for generally sending object positions and stuff
 	float m_recommended_send_interval;
 };
@@ -463,7 +464,7 @@ class ClientEnvironment : public Environment
 	
 	// Get all nearby objects
 	void getActiveObjects(v3f origin, f32 max_d,
-			core::array<DistanceSortedActiveObject> &dest);
+			std::vector<DistanceSortedActiveObject> &dest);
 	
 	// Get event from queue. CEE_NONE is returned if queue is empty.
 	ClientEnvEvent getClientEvent();
@@ -476,8 +477,8 @@ class ClientEnvironment : public Environment
 	ITextureSource *m_texturesource;
 	IGameDef *m_gamedef;
 	IrrlichtDevice *m_irr;
-	core::map<u16, ClientActiveObject*> m_active_objects;
-	core::list<ClientSimpleObject*> m_simple_objects;
+	std::map<u16, ClientActiveObject*> m_active_objects;
+	std::list<ClientSimpleObject*> m_simple_objects;
 	Queue<ClientEnvEvent> m_client_event_queue;
 	IntervalLimiter m_active_object_light_update_interval;
 	IntervalLimiter m_lava_hurt_interval;
diff --git a/src/farmesh.cpp b/src/farmesh.cpp
index 443e2b3bf00e713d782c220c812b5fc6b78c7e65..ecf01ee07a7f9a935e34ff629d7da656df9353dc 100644
--- a/src/farmesh.cpp
+++ b/src/farmesh.cpp
@@ -112,13 +112,13 @@ struct HeightPoint
 	float have_sand;
 	float tree_amount;
 };
-core::map<v2s16, HeightPoint> g_heights;
+std::map<v2s16, HeightPoint> g_heights;
 
 HeightPoint ground_height(u64 seed, v2s16 p2d)
 {
-	core::map<v2s16, HeightPoint>::Node *n = g_heights.find(p2d);
-	if(n)
-		return n->getValue();
+	std::map<v2s16, HeightPoint>::iterator n = g_heights.find(p2d);
+	if(n != g_heights.end())
+		return n->second;
 	HeightPoint hp;
 	s16 level = Mapgen::find_ground_level_from_noise(seed, p2d, 3);
 	hp.gh = (level-4)*BS;
diff --git a/src/game.cpp b/src/game.cpp
index 1ae29b13b6956f5387d31c5fd301907998c442e3..5e4e0614803f5050733c0a3c11d227d16207d0b6 100644
--- a/src/game.cpp
+++ b/src/game.cpp
@@ -2186,6 +2186,47 @@ void the_game(
 				{
 					update_wielded_item_trigger = true;
 				}
+				else if(event.type == CE_SPAWN_PARTICLE)
+				{
+					LocalPlayer* player = client.getEnv().getLocalPlayer();
+					AtlasPointer ap =
+						gamedef->tsrc()->getTexture(*(event.spawn_particle.texture));
+
+					new Particle(gamedef, smgr, player, client.getEnv(),
+						*event.spawn_particle.pos,
+						*event.spawn_particle.vel,
+						*event.spawn_particle.acc,
+						 event.spawn_particle.expirationtime,
+						 event.spawn_particle.size,
+						 event.spawn_particle.collisiondetection, ap);
+				}
+				else if(event.type == CE_ADD_PARTICLESPAWNER)
+				{
+					LocalPlayer* player = client.getEnv().getLocalPlayer();
+					AtlasPointer ap =
+						gamedef->tsrc()->getTexture(*(event.add_particlespawner.texture));
+
+					new ParticleSpawner(gamedef, smgr, player,
+						 event.add_particlespawner.amount,
+						 event.add_particlespawner.spawntime,
+						*event.add_particlespawner.minpos,
+						*event.add_particlespawner.maxpos,
+						*event.add_particlespawner.minvel,
+						*event.add_particlespawner.maxvel,
+						*event.add_particlespawner.minacc,
+						*event.add_particlespawner.maxacc,
+						 event.add_particlespawner.minexptime,
+						 event.add_particlespawner.maxexptime,
+						 event.add_particlespawner.minsize,
+						 event.add_particlespawner.maxsize,
+						 event.add_particlespawner.collisiondetection,
+						 ap,
+						 event.add_particlespawner.id);
+				}
+				else if(event.type == CE_DELETE_PARTICLESPAWNER)
+				{
+					delete_particlespawner (event.delete_particlespawner.id);
+				}
 			}
 		}
 		
@@ -2353,11 +2394,6 @@ void the_game(
 				}
 			}
 			
-			// We can't actually know, but assume the sound of right-clicking
-			// to be the sound of placing a node
-			soundmaker.m_player_rightpunch_sound.gain = 0.5;
-			soundmaker.m_player_rightpunch_sound.name = "default_place_node";
-			
 			/*
 				Handle digging
 			*/
@@ -2415,7 +2451,8 @@ void the_game(
 						const ContentFeatures &features =
 							client.getNodeDefManager()->get(n);
 						addPunchingParticles
-							(gamedef, smgr, player, nodepos, features.tiles);
+							(gamedef, smgr, player, client.getEnv(),
+							 nodepos, features.tiles);
 					}
 				}
 
@@ -2453,7 +2490,8 @@ void the_game(
 						const ContentFeatures &features =
 							client.getNodeDefManager()->get(wasnode);
 						addDiggingParticles
-							(gamedef, smgr, player, nodepos, features.tiles);
+							(gamedef, smgr, player, client.getEnv(),
+							 nodepos, features.tiles);
 					}
 
 					dig_time = 0;
@@ -2574,6 +2612,9 @@ void the_game(
 									<<") - Position not loaded"<<std::endl;
 						}
 					}while(0);
+					
+					// Read the sound
+					soundmaker.m_player_rightpunch_sound = def.sound_place;
 				}
 			}
 		}
@@ -2734,6 +2775,7 @@ void the_game(
 		*/
 
 		allparticles_step(dtime, client.getEnv());
+		allparticlespawners_step(dtime, client.getEnv());
 		
 		/*
 			Fog
@@ -3220,6 +3262,7 @@ void the_game(
 		clouds->drop();
 	if(gui_chat_console)
 		gui_chat_console->drop();
+	clear_particles ();
 	
 	/*
 		Draw a "shutting down" screen, which will be shown while the map
diff --git a/src/game.h b/src/game.h
index fef777fea1fb69546523f43b2ee9023dd512580e..a2c1fc09cc48eb7cb7d9790ab6cf6c3216ff8911 100644
--- a/src/game.h
+++ b/src/game.h
@@ -23,17 +23,18 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include "irrlichttypes_extrabloated.h"
 #include <string>
 #include "keycode.h"
+#include <list>
 
-class KeyList : protected core::list<KeyPress>
+class KeyList : protected std::list<KeyPress>
 {
-	typedef core::list<KeyPress> super;
-	typedef super::Iterator Iterator;
-	typedef super::ConstIterator ConstIterator;
+	typedef std::list<KeyPress> super;
+	typedef super::iterator iterator;
+	typedef super::const_iterator const_iterator;
 
-	virtual ConstIterator find(const KeyPress &key) const
+	virtual const_iterator find(const KeyPress &key) const
 	{
-		ConstIterator f(begin());
-		ConstIterator e(end());
+		const_iterator f(begin());
+		const_iterator e(end());
 		while (f!=e) {
 			if (*f == key)
 				return f;
@@ -42,10 +43,10 @@ class KeyList : protected core::list<KeyPress>
 		return e;
 	}
 
-	virtual Iterator find(const KeyPress &key)
+	virtual iterator find(const KeyPress &key)
 	{
-		Iterator f(begin());
-		Iterator e(end());
+		iterator f(begin());
+		iterator e(end());
 		while (f!=e) {
 			if (*f == key)
 				return f;
@@ -65,14 +66,14 @@ class KeyList : protected core::list<KeyPress>
 
 	void unset(const KeyPress &key)
 	{
-		Iterator p(find(key));
+		iterator p(find(key));
 		if (p != end())
 			erase(p);
 	}
 
 	void toggle(const KeyPress &key)
 	{
-		Iterator p(this->find(key));
+		iterator p(this->find(key));
 		if (p != end())
 			erase(p);
 		else
diff --git a/src/gettime.h b/src/gettime.h
index 611906559b5059caf127553cd0f2954d8ea28fe0..cde1471e508541a2016b38284801ba3867826fe8 100644
--- a/src/gettime.h
+++ b/src/gettime.h
@@ -31,7 +31,15 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 		Normal build: main.cpp
 		Server build: servermain.cpp
 */
+enum TimePrecision {
+	PRECISION_SECONDS,
+	PRECISION_MILLI,
+	PRECISION_MICRO,
+	PRECISION_NANO
+};
+
 extern u32 getTimeMs();
+extern u32 getTime(TimePrecision prec);
 
 /*
 	Timestamp stuff
diff --git a/src/guiChatConsole.cpp b/src/guiChatConsole.cpp
index f522af01f966481ac3e4158b680d48c2fce677a1..5fc576cf8315f83c8a5b6268a83b79c507c43660 100644
--- a/src/guiChatConsole.cpp
+++ b/src/guiChatConsole.cpp
@@ -535,7 +535,7 @@ bool GUIChatConsole::OnEvent(const SEvent& event)
 		{
 			// Tab or Shift-Tab pressed
 			// Nick completion
-			core::list<std::wstring> names = m_client->getConnectedPlayerNames();
+			std::list<std::wstring> names = m_client->getConnectedPlayerNames();
 			bool backwards = event.KeyInput.Shift;
 			m_chat_backend->getPrompt().nickCompletion(names, backwards);
 			return true;
diff --git a/src/guiFormSpecMenu.cpp b/src/guiFormSpecMenu.cpp
index 120d6629aed2f7931f8b11992402584f93bc54e6..1754422d08637eeee0f2e1126030a1b2d6742e98 100644
--- a/src/guiFormSpecMenu.cpp
+++ b/src/guiFormSpecMenu.cpp
@@ -207,18 +207,18 @@ void GUIFormSpecMenu::regenerateGui(v2u32 screensize)
 	Strfnd f(m_formspec_string);
 	while(f.atend() == false)
 	{
-		std::string type = trim(f.next("["));
+		std::string type = trim(f.next_esc("["));
 		if(type == "invsize" || type == "size")
 		{
 			v2f invsize;
-			invsize.X = stof(f.next(","));
+			invsize.X = stof(f.next_esc(","));
 			if(type == "size")
 			{
-				invsize.Y = stof(f.next("]"));
+				invsize.Y = stof(f.next_esc("]"));
 			}
 			else{
-				invsize.Y = stof(f.next(";"));
-				f.next("]");
+				invsize.Y = stof(f.next_esc(";"));
+				f.next_esc("]");
 			}
 			infostream<<"Form size ("<<invsize.X<<","<<invsize.Y<<")"<<std::endl;
 
@@ -242,24 +242,24 @@ void GUIFormSpecMenu::regenerateGui(v2u32 screensize)
 		}
 		else if(type == "list")
 		{
-			std::string name = f.next(";");
+			std::string name = f.next_esc(";");
 			InventoryLocation loc;
 			if(name == "context" || name == "current_name")
 				loc = m_current_inventory_location;
 			else
 				loc.deSerialize(name);
-			std::string listname = f.next(";");
+			std::string listname = f.next_esc(";");
 			v2s32 pos = basepos;
-			pos.X += stof(f.next(",")) * (float)spacing.X;
-			pos.Y += stof(f.next(";")) * (float)spacing.Y;
+			pos.X += stof(f.next_esc(",")) * (float)spacing.X;
+			pos.Y += stof(f.next_esc(";")) * (float)spacing.Y;
 			v2s32 geom;
-			geom.X = stoi(f.next(","));
-			geom.Y = stoi(f.next(";"));
+			geom.X = stoi(f.next_esc(","));
+			geom.Y = stoi(f.next_esc(";"));
 			infostream<<"list inv="<<name<<", listname="<<listname
 					<<", pos=("<<pos.X<<","<<pos.Y<<")"
 					<<", geom=("<<geom.X<<","<<geom.Y<<")"
 					<<std::endl;
-			std::string start_i_s = f.next("]");
+			std::string start_i_s = f.next_esc("]");
 			s32 start_i = 0;
 			if(start_i_s != "")
 				start_i = stoi(start_i_s);
@@ -270,12 +270,12 @@ void GUIFormSpecMenu::regenerateGui(v2u32 screensize)
 		else if(type == "image")
 		{
 			v2s32 pos = basepos;
-			pos.X += stof(f.next(",")) * (float)spacing.X;
-			pos.Y += stof(f.next(";")) * (float)spacing.Y;
+			pos.X += stof(f.next_esc(",")) * (float)spacing.X;
+			pos.Y += stof(f.next_esc(";")) * (float)spacing.Y;
 			v2s32 geom;
-			geom.X = stof(f.next(",")) * (float)imgsize.X;
-			geom.Y = stof(f.next(";")) * (float)imgsize.Y;
-			std::string name = f.next("]");
+			geom.X = stof(f.next_esc(",")) * (float)imgsize.X;
+			geom.Y = stof(f.next_esc(";")) * (float)imgsize.Y;
+			std::string name = f.next_esc("]");
 			infostream<<"image name="<<name
 					<<", pos=("<<pos.X<<","<<pos.Y<<")"
 					<<", geom=("<<geom.X<<","<<geom.Y<<")"
@@ -287,12 +287,12 @@ void GUIFormSpecMenu::regenerateGui(v2u32 screensize)
 		else if(type == "item_image")
 		{
 			v2s32 pos = basepos;
-			pos.X += stof(f.next(",")) * (float)spacing.X;
-			pos.Y += stof(f.next(";")) * (float)spacing.Y;
+			pos.X += stof(f.next_esc(",")) * (float)spacing.X;
+			pos.Y += stof(f.next_esc(";")) * (float)spacing.Y;
 			v2s32 geom;
-			geom.X = stof(f.next(",")) * (float)imgsize.X;
-			geom.Y = stof(f.next(";")) * (float)imgsize.Y;
-			std::string name = f.next("]");
+			geom.X = stof(f.next_esc(",")) * (float)imgsize.X;
+			geom.Y = stof(f.next_esc(";")) * (float)imgsize.Y;
+			std::string name = f.next_esc("]");
 			infostream<<"item name="<<name
 					<<", pos=("<<pos.X<<","<<pos.Y<<")"
 					<<", geom=("<<geom.X<<","<<geom.Y<<")"
@@ -304,12 +304,12 @@ void GUIFormSpecMenu::regenerateGui(v2u32 screensize)
 		else if(type == "background")
 		{
 			v2s32 pos = basepos;
-			pos.X += stof(f.next(",")) * (float)spacing.X - ((float)spacing.X-(float)imgsize.X)/2;
-			pos.Y += stof(f.next(";")) * (float)spacing.Y - ((float)spacing.Y-(float)imgsize.Y)/2;
+			pos.X += stof(f.next_esc(",")) * (float)spacing.X - ((float)spacing.X-(float)imgsize.X)/2;
+			pos.Y += stof(f.next_esc(";")) * (float)spacing.Y - ((float)spacing.Y-(float)imgsize.Y)/2;
 			v2s32 geom;
-			geom.X = stof(f.next(",")) * (float)spacing.X;
-			geom.Y = stof(f.next(";")) * (float)spacing.Y;
-			std::string name = f.next("]");
+			geom.X = stof(f.next_esc(",")) * (float)spacing.X;
+			geom.Y = stof(f.next_esc(";")) * (float)spacing.Y;
+			std::string name = f.next_esc("]");
 			infostream<<"image name="<<name
 					<<", pos=("<<pos.X<<","<<pos.Y<<")"
 					<<", geom=("<<geom.X<<","<<geom.Y<<")"
@@ -320,8 +320,8 @@ void GUIFormSpecMenu::regenerateGui(v2u32 screensize)
 		}
 		else if(type == "field" || type == "textarea")
 		{
-			std::string fname = f.next(";");
-			std::string flabel = f.next(";");
+			std::string fname = f.next_esc(";");
+			std::string flabel = f.next_esc(";");
 
 			if(fname.find(",") == std::string::npos && flabel.find(",") == std::string::npos)
 			{
@@ -372,14 +372,14 @@ void GUIFormSpecMenu::regenerateGui(v2u32 screensize)
 
 
 				
-				fname = f.next(";");
-				flabel = f.next(";");
+				fname = f.next_esc(";");
+				flabel = f.next_esc(";");
 				if(bp_set != 2)
 					errorstream<<"WARNING: invalid use of positioned "<<type<<" without a size[] element"<<std::endl;
 				
 			}
 
-			std::string odefault = f.next("]");
+			std::string odefault = f.next_esc("]");
 			std::string fdefault;
 
 			// fdefault may contain a variable reference, which
@@ -389,6 +389,9 @@ void GUIFormSpecMenu::regenerateGui(v2u32 screensize)
 			else
 				fdefault = odefault;
 
+			fdefault = unescape_string(fdefault);
+			flabel = unescape_string(flabel);
+
 			FieldSpec spec = FieldSpec(
 				narrow_to_wide(fname.c_str()),
 				narrow_to_wide(flabel.c_str()),
@@ -434,15 +437,17 @@ void GUIFormSpecMenu::regenerateGui(v2u32 screensize)
 		else if(type == "label")
 		{
 			v2s32 pos = padding;
-			pos.X += stof(f.next(",")) * (float)spacing.X;
-			pos.Y += stof(f.next(";")) * (float)spacing.Y;
+			pos.X += stof(f.next_esc(",")) * (float)spacing.X;
+			pos.Y += stof(f.next_esc(";")) * (float)spacing.Y;
 
 			rect = core::rect<s32>(pos.X, pos.Y+((imgsize.Y/2)-15), pos.X+300, pos.Y+((imgsize.Y/2)+15));
 			
-			std::string flabel = f.next("]");
+			std::string flabel = f.next_esc("]");
 			if(bp_set != 2)
 				errorstream<<"WARNING: invalid use of label without a size[] element"<<std::endl;
 
+			flabel = unescape_string(flabel);
+
 			FieldSpec spec = FieldSpec(
 				narrow_to_wide(""),
 				narrow_to_wide(flabel.c_str()),
@@ -455,19 +460,21 @@ void GUIFormSpecMenu::regenerateGui(v2u32 screensize)
 		else if(type == "button" || type == "button_exit")
 		{
 			v2s32 pos = padding;
-			pos.X += stof(f.next(",")) * (float)spacing.X;
-			pos.Y += stof(f.next(";")) * (float)spacing.Y;
+			pos.X += stof(f.next_esc(",")) * (float)spacing.X;
+			pos.Y += stof(f.next_esc(";")) * (float)spacing.Y;
 			v2s32 geom;
-			geom.X = (stof(f.next(",")) * (float)spacing.X)-(spacing.X-imgsize.X);
-			pos.Y += (stof(f.next(";")) * (float)imgsize.Y)/2;
+			geom.X = (stof(f.next_esc(",")) * (float)spacing.X)-(spacing.X-imgsize.X);
+			pos.Y += (stof(f.next_esc(";")) * (float)imgsize.Y)/2;
 
 			rect = core::rect<s32>(pos.X, pos.Y-15, pos.X+geom.X, pos.Y+15);
 			
-			std::string fname = f.next(";");
-			std::string flabel = f.next("]");
+			std::string fname = f.next_esc(";");
+			std::string flabel = f.next_esc("]");
 			if(bp_set != 2)
 				errorstream<<"WARNING: invalid use of button without a size[] element"<<std::endl;
 
+			flabel = unescape_string(flabel);
+
 			FieldSpec spec = FieldSpec(
 				narrow_to_wide(fname.c_str()),
 				narrow_to_wide(flabel.c_str()),
@@ -483,20 +490,22 @@ void GUIFormSpecMenu::regenerateGui(v2u32 screensize)
 		else if(type == "image_button" || type == "image_button_exit")
 		{
 			v2s32 pos = padding;
-			pos.X += stof(f.next(",")) * (float)spacing.X;
-			pos.Y += stof(f.next(";")) * (float)spacing.Y;
+			pos.X += stof(f.next_esc(",")) * (float)spacing.X;
+			pos.Y += stof(f.next_esc(";")) * (float)spacing.Y;
 			v2s32 geom;
-			geom.X = (stof(f.next(",")) * (float)spacing.X)-(spacing.X-imgsize.X);
-			geom.Y = (stof(f.next(";")) * (float)spacing.Y)-(spacing.Y-imgsize.Y);
+			geom.X = (stof(f.next_esc(",")) * (float)spacing.X)-(spacing.X-imgsize.X);
+			geom.Y = (stof(f.next_esc(";")) * (float)spacing.Y)-(spacing.Y-imgsize.Y);
 
 			rect = core::rect<s32>(pos.X, pos.Y, pos.X+geom.X, pos.Y+geom.Y);
 			
-			std::string fimage = f.next(";");
-			std::string fname = f.next(";");
-			std::string flabel = f.next("]");
+			std::string fimage = f.next_esc(";");
+			std::string fname = f.next_esc(";");
+			std::string flabel = f.next_esc("]");
 			if(bp_set != 2)
 				errorstream<<"WARNING: invalid use of image_button without a size[] element"<<std::endl;
 
+			flabel = unescape_string(flabel);
+
 			FieldSpec spec = FieldSpec(
 				narrow_to_wide(fname.c_str()),
 				narrow_to_wide(flabel.c_str()),
@@ -519,15 +528,15 @@ void GUIFormSpecMenu::regenerateGui(v2u32 screensize)
 		else if(type == "item_image_button")
 		{
 			v2s32 pos = padding;
-			pos.X += stof(f.next(",")) * (float)spacing.X;
-			pos.Y += stof(f.next(";")) * (float)spacing.Y;
+			pos.X += stof(f.next_esc(",")) * (float)spacing.X;
+			pos.Y += stof(f.next_esc(";")) * (float)spacing.Y;
 			v2s32 geom;
-			geom.X = (stof(f.next(",")) * (float)spacing.X)-(spacing.X-imgsize.X);
-			geom.Y = (stof(f.next(";")) * (float)spacing.Y)-(spacing.Y-imgsize.Y);
+			geom.X = (stof(f.next_esc(",")) * (float)spacing.X)-(spacing.X-imgsize.X);
+			geom.Y = (stof(f.next_esc(";")) * (float)spacing.Y)-(spacing.Y-imgsize.Y);
 			rect = core::rect<s32>(pos.X, pos.Y, pos.X+geom.X, pos.Y+geom.Y);		
-			std::string fimage = f.next(";");
-			std::string fname = f.next(";");
-			std::string flabel = f.next("]");
+			std::string fimage = f.next_esc(";");
+			std::string fname = f.next_esc(";");
+			std::string flabel = f.next_esc("]");
 			if(bp_set != 2)
 				errorstream<<"WARNING: invalid use of item_image_button without a size[] element"<<std::endl;		
 			IItemDefManager *idef = m_gamedef->idef();
@@ -535,6 +544,7 @@ void GUIFormSpecMenu::regenerateGui(v2u32 screensize)
 			item.deSerialize(fimage, idef);
 			video::ITexture *texture = idef->getInventoryTexture(item.getDefinition(idef).name, m_gamedef);
 			std::string tooltip = item.getDefinition(idef).description;
+			flabel = unescape_string(flabel);
 			FieldSpec spec = FieldSpec(
 				narrow_to_wide(fname.c_str()),
 				narrow_to_wide(flabel.c_str()),
@@ -556,7 +566,7 @@ void GUIFormSpecMenu::regenerateGui(v2u32 screensize)
 		else
 		{
 			// Ignore others
-			std::string ts = f.next("]");
+			std::string ts = f.next_esc("]");
 			infostream<<"Unknown DrawSpec: type="<<type<<", data=\""<<ts<<"\""
 					<<std::endl;
 		}
diff --git a/src/guiFormSpecMenu.h b/src/guiFormSpecMenu.h
index aee16736e124c50347f3c1260cb61fba606656ce..17b202b187e5ab30c2bd39ae49283236e86ce172 100644
--- a/src/guiFormSpecMenu.h
+++ b/src/guiFormSpecMenu.h
@@ -209,11 +209,11 @@ class GUIFormSpecMenu : public GUIModalMenu
 	IFormSource *m_form_src;
 	TextDest *m_text_dst;
 
-	core::array<ListDrawSpec> m_inventorylists;
-	core::array<ImageDrawSpec> m_backgrounds;	
-	core::array<ImageDrawSpec> m_images;
-	core::array<ImageDrawSpec> m_itemimages;
-	core::array<FieldSpec> m_fields;
+	std::vector<ListDrawSpec> m_inventorylists;
+	std::vector<ImageDrawSpec> m_backgrounds;
+	std::vector<ImageDrawSpec> m_images;
+	std::vector<ImageDrawSpec> m_itemimages;
+	std::vector<FieldSpec> m_fields;
 
 	ItemSpec *m_selected_item;
 	u32 m_selected_amount;
diff --git a/src/guiMainMenu.cpp b/src/guiMainMenu.cpp
index c2e68579e9fcf7c3ff7eaf48731ac1d944487146..4c203003985dfdc18a8900f48314e92ecd1907e1 100644
--- a/src/guiMainMenu.cpp
+++ b/src/guiMainMenu.cpp
@@ -42,6 +42,43 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include "util/string.h"
 #include "subgame.h"
 
+#define ARRAYLEN(x) (sizeof(x) / sizeof((x)[0]))
+#define LSTRING(x) LSTRING_(x)
+#define LSTRING_(x) L##x
+
+const wchar_t *contrib_core_strs[] = {
+	L"Perttu Ahola (celeron55) <celeron55@gmail.com>",
+	L"Ryan Kwolek (kwolekr) <kwolekr@minetest.net>",
+	L"PilzAdam <pilzadam@minetest.net>",
+	L"Ilya Zhuravlev (thexyz) <xyz@minetest.net>",
+	L"Lisa Milne (darkrose) <lisa@ltmnet.com>",
+	L"Maciej Kasatkin (RealBadAngel) <mk@realbadangel.pl>",
+	L"proller <proler@gmail.com>"
+};
+
+const wchar_t *contrib_active_strs[] = {
+	L"sfan5 <sfan5@live.de>",
+	L"sapier <sapier@gmx.net>",
+	L"Vanessa Ezekowitz (VanessaE) <vanessaezekowitz@gmail.com>",
+	L"Jurgen Doser (doserj) <jurgen.doser@gmail.com>",
+	L"Jeija <jeija@mesecons.net>",
+	L"MirceaKitsune <mirceakitsune@gmail.com>",
+	L"ShadowNinja",
+	L"dannydark <the_skeleton_of_a_child@yahoo.co.uk>",
+	L"0gb.us <0gb.us@0gb.us>"
+};
+
+const wchar_t *contrib_previous_strs[] = {
+	L"kahrl <kahrl@gmx.net>",
+	L"Giuseppe Bilotta (Oblomov) <giuseppe.bilotta@gmail.com>",
+	L"Jonathan Neuschafer <j.neuschaefer@gmx.net>",
+	L"Nils Dagsson Moskopp (erlehmann) <nils@dieweltistgarnichtso.net>",
+	L"Constantin Wenger (SpeedProg) <constantin.wenger@googlemail.com>",
+	L"matttpt <matttpt@gmail.com>",
+	L"JacobF <queatz@gmail.com>" 
+};
+
+
 struct CreateWorldDestMainMenu : public CreateWorldDest
 {
 	CreateWorldDestMainMenu(GUIMainMenu *menu):
@@ -106,6 +143,7 @@ enum
 	GUI_ID_SHADERS_CB,
 	GUI_ID_PRELOAD_ITEM_VISUALS_CB,
 	GUI_ID_ENABLE_PARTICLES_CB,
+	GUI_ID_LIQUID_FINITE_CB,
 	GUI_ID_DAMAGE_CB,
 	GUI_ID_CREATIVE_CB,
 	GUI_ID_PUBLIC_CB,
@@ -119,6 +157,7 @@ enum
 	GUI_ID_SERVERLIST,
 	GUI_ID_SERVERLIST_TOGGLE,
 	GUI_ID_SERVERLIST_DELETE,
+	GUI_ID_SERVERLIST_TITLE,
 };
 
 enum
@@ -209,7 +248,6 @@ void GUIMainMenu::regenerateGui(v2u32 screensize)
 	changeCtype("");
 
 	// Version
-	//if(m_data->selected_tab != TAB_CREDITS)
 	{
 		core::rect<s32> rect(0, 0, size.X, 40);
 		rect += v2s32(4, 0);
@@ -219,7 +257,7 @@ void GUIMainMenu::regenerateGui(v2u32 screensize)
 	}
 
 	//v2s32 center(size.X/2, size.Y/2);
-	v2s32 c800(size.X/2-400, size.Y/2-300);
+	v2s32 c800(size.X/2-400, size.Y/2-270);
 	
 	m_topleft_client = c800 + v2s32(90, 70+50+30);
 	m_size_client = v2s32(620, 270);
@@ -237,7 +275,6 @@ void GUIMainMenu::regenerateGui(v2u32 screensize)
 	m_topleft_server = m_topleft_client + v2s32(0, m_size_client.Y+20);
 	
 	// Tabs
-#if 1
 	{
 		core::rect<s32> rect(0, 0, m_size_client.X, 30);
 		rect += m_topleft_client + v2s32(0, -30);
@@ -250,7 +287,6 @@ void GUIMainMenu::regenerateGui(v2u32 screensize)
 		e->addTab(wgettext("Credits"));
 		e->setActiveTab(m_data->selected_tab);
 	}
-#endif
 	
 	if(m_data->selected_tab == TAB_SINGLEPLAYER)
 	{
@@ -259,7 +295,7 @@ void GUIMainMenu::regenerateGui(v2u32 screensize)
 			core::rect<s32> rect(0, 0, 10, m_size_client.Y);
 			rect += m_topleft_client + v2s32(15, 0);
 			//const wchar_t *text = L"H\nY\nB\nR\nI\nD";
-			const wchar_t *text = L"T\nA\nP\nE\n\nA\nN\nD\n\nG\nL\nU\nE";
+			const wchar_t *text = L"S\nI\nN\nG\nL\nE\n \nP\nL\nA\nY\nE\nR\n";
 			gui::IGUIStaticText *t =
 			Environment->addStaticText(text, rect, false, true, this, -1);
 			t->setTextAlignment(gui::EGUIA_CENTER, gui::EGUIA_CENTER);
@@ -392,13 +428,38 @@ void GUIMainMenu::regenerateGui(v2u32 screensize)
 		changeCtype("");
 		// Server List
 		{
-			core::rect<s32> rect(0, 0, 390, 160);
-			rect += m_topleft_client + v2s32(50, 10);
+			core::rect<s32> rect(0, 0, 390, 140);
+			rect += m_topleft_client + v2s32(50, 30);
 			gui::IGUIListBox *e = Environment->addListBox(rect, this,
 					GUI_ID_SERVERLIST);
 			e->setDrawBackground(true);
-			if (m_data->serverlist_show_available == false)
+#if USE_CURL
+			if(m_data->selected_serverlist == SERVERLIST_FAVORITES) {
 				m_data->servers = ServerList::getLocal();
+				{
+					core::rect<s32> rect(0, 0, 390, 20);
+					rect += m_topleft_client + v2s32(50, 10);
+					Environment->addStaticText(wgettext("Favorites:"),
+						rect, false, true, this, GUI_ID_SERVERLIST_TITLE);
+				}
+			} else {
+				m_data->servers = ServerList::getOnline();
+				{
+					core::rect<s32> rect(0, 0, 390, 20);
+					rect += m_topleft_client + v2s32(50, 10);
+					Environment->addStaticText(wgettext("Public Server List:"),
+						rect, false, true, this, GUI_ID_SERVERLIST_TITLE);
+				}
+			}
+#else
+			m_data->servers = ServerList::getLocal();
+			{
+				core::rect<s32> rect(0, 0, 390, 20);
+				rect += m_topleft_client + v2s32(50, 10);
+				Environment->addStaticText(wgettext("Favorites:"),
+					rect, false, true, this, GUI_ID_SERVERLIST_TITLE);
+			}
+#endif
 			updateGuiServerList();
 			e->setSelected(0);
 		}
@@ -435,7 +496,7 @@ void GUIMainMenu::regenerateGui(v2u32 screensize)
 			gui::IGUIButton *e = Environment->addButton(rect, this, GUI_ID_SERVERLIST_TOGGLE,
 				wgettext("Show Public"));
 			e->setIsPushButton(true);
-			if (m_data->serverlist_show_available)
+			if (m_data->selected_serverlist == SERVERLIST_PUBLIC)
 			{
 				e->setText(wgettext("Show Favorites"));
 				e->setPressed();
@@ -448,7 +509,7 @@ void GUIMainMenu::regenerateGui(v2u32 screensize)
 			rect += m_topleft_client + v2s32(50+260+10, 180);
 			gui::IGUIButton *e = Environment->addButton(rect, this, GUI_ID_SERVERLIST_DELETE,
 				wgettext("Delete"));
-			if (m_data->serverlist_show_available) // Hidden on Show-Online mode
+			if (m_data->selected_serverlist == SERVERLIST_PUBLIC) // Hidden when on public list
 				e->setVisible(false);
 		}
 		// Start game button
@@ -691,6 +752,13 @@ void GUIMainMenu::regenerateGui(v2u32 screensize)
 					GUI_ID_ENABLE_PARTICLES_CB, wgettext("Enable Particles"));
 		}
 
+		{
+			core::rect<s32> rect(0, 0, option_w+20+20, 30);
+			rect += m_topleft_client + v2s32(option_x+175*2, option_y+20*3);
+			Environment->addCheckBox(m_data->liquid_finite, rect, this,
+					GUI_ID_LIQUID_FINITE_CB, wgettext("Finite liquid"));
+		}
+
 		// Key change button
 		{
 			core::rect<s32> rect(0, 0, 120, 30);
@@ -706,7 +774,7 @@ void GUIMainMenu::regenerateGui(v2u32 screensize)
 	{
 		// CREDITS
 		{
-			core::rect<s32> rect(0, 0, 10, m_size_client.Y);
+			core::rect<s32> rect(0, 0, 9, m_size_client.Y);
 			rect += m_topleft_client + v2s32(15, 0);
 			const wchar_t *text = L"C\nR\nE\nD\nI\nT\nS";
 			gui::IGUIStaticText *t =
@@ -714,15 +782,34 @@ void GUIMainMenu::regenerateGui(v2u32 screensize)
 			t->setTextAlignment(gui::EGUIA_CENTER, gui::EGUIA_CENTER);
 		}
 		{
-			core::rect<s32> rect(0, 0, 454, 250);
-			rect += m_topleft_client + v2s32(110, 50+35);
-			Environment->addStaticText(narrow_to_wide(
-			"Minetest " VERSION_STRING "\n"
-			"http://minetest.net/\n"
-			"\n"
-			"by Perttu Ahola <celeron55@gmail.com>\n"
-			"and contributors: PilzAdam, Taoki, tango_, kahrl (kaaaaaahrl?), darkrose, matttpt, erlehmann, SpeedProg, JacobF, teddydestodes, marktraceur, Jonathan Neuschäfer, thexyz, VanessaE, sfan5... and tens of more random people."
-			).c_str(), rect, false, true, this, -1);
+			core::rect<s32> rect(0, 0, 130, 70);
+			rect += m_topleft_client + v2s32(35, 160);
+			Environment->addStaticText(
+				L"Minetest " LSTRING(VERSION_STRING) L"\nhttp://minetest.net/",
+				 rect, false, true, this, -1);
+		}
+		{
+			video::SColor yellow(255, 255, 255, 0);
+			core::rect<s32> rect(0, 0, 450, 260);
+			rect += m_topleft_client + v2s32(168, 5);
+			
+			irr::gui::IGUIListBox *list = Environment->addListBox(rect, this);
+			
+			list->addItem(L"Core Developers");
+			list->setItemOverrideColor(list->getItemCount() - 1, yellow);
+			for (int i = 0; i != ARRAYLEN(contrib_core_strs); i++)
+				list->addItem(contrib_core_strs[i]);
+			list->addItem(L"");
+			list->addItem(L"Active Contributors");
+			list->setItemOverrideColor(list->getItemCount() - 1, yellow);
+			for (int i = 0; i != ARRAYLEN(contrib_active_strs); i++)
+				list->addItem(contrib_active_strs[i]);
+			list->addItem(L"");
+			list->addItem(L"Previous Contributors");
+			list->setItemOverrideColor(list->getItemCount() - 1, yellow);
+			for (int i = 0; i != ARRAYLEN(contrib_previous_strs); i++)
+				list->addItem(contrib_previous_strs[i]);
+			list->addItem(L"");
 		}
 	}
 
@@ -786,15 +873,15 @@ void GUIMainMenu::drawMenu()
 			driver->draw2DRectangle(bgcolor, rect, &AbsoluteClippingRect);
 		}
 		video::ITexture *logotexture =
-				driver->getTexture(getTexturePath("menulogo.png").c_str());
+				driver->getTexture(getTexturePath("logo.png").c_str());
 		if(logotexture)
 		{
 			v2s32 logosize(logotexture->getOriginalSize().Width,
 					logotexture->getOriginalSize().Height);
-			logosize *= 2;
+
 			core::rect<s32> rect(0,0,logosize.X,logosize.Y);
 			rect += AbsoluteRect.UpperLeftCorner + m_topleft_client;
-			rect += v2s32(130, 50);
+			rect += v2s32(50, 60);
 			driver->draw2DImage(logotexture, rect,
 				core::rect<s32>(core::position2d<s32>(0,0),
 				core::dimension2di(logotexture->getSize())),
@@ -918,6 +1005,12 @@ void GUIMainMenu::readInput(MainMenuData *dst)
 			dst->enable_particles = ((gui::IGUICheckBox*)e)->isChecked();
 	}
 
+	{
+		gui::IGUIElement *e = getElementFromId(GUI_ID_LIQUID_FINITE_CB);
+		if(e != NULL && e->getType() == gui::EGUIET_CHECK_BOX)
+			dst->liquid_finite = ((gui::IGUICheckBox*)e)->isChecked();
+	}
+
 	{
 		gui::IGUIElement *e = getElementFromId(GUI_ID_WORLD_LISTBOX);
 		if(e != NULL && e->getType() == gui::EGUIET_LIST_BOX)
@@ -1083,25 +1176,28 @@ bool GUIMainMenu::OnEvent(const SEvent& event)
 				gui::IGUIElement *togglebutton = getElementFromId(GUI_ID_SERVERLIST_TOGGLE);
 				gui::IGUIElement *deletebutton = getElementFromId(GUI_ID_SERVERLIST_DELETE);
 				gui::IGUIListBox *serverlist = (gui::IGUIListBox*)getElementFromId(GUI_ID_SERVERLIST);
-				if (m_data->serverlist_show_available) // switch to favorite list
+				gui::IGUIElement *title = getElementFromId(GUI_ID_SERVERLIST_TITLE);
+				if (m_data->selected_serverlist == SERVERLIST_PUBLIC) // switch to favorite list
 				{
 					m_data->servers = ServerList::getLocal();
 					togglebutton->setText(wgettext("Show Public"));
+					title->setText(wgettext("Favorites:"));
 					deletebutton->setVisible(true);
 					updateGuiServerList();
 					serverlist->setSelected(0);
+					m_data->selected_serverlist = SERVERLIST_FAVORITES;
 				}
 				else // switch to online list
 				{
 					m_data->servers = ServerList::getOnline();
 					togglebutton->setText(wgettext("Show Favorites"));
+					title->setText(wgettext("Public Server List:"));
 					deletebutton->setVisible(false);
 					updateGuiServerList();
 					serverlist->setSelected(0);
+					m_data->selected_serverlist = SERVERLIST_PUBLIC;
 				}
 				serverListOnSelected();
-
-				m_data->serverlist_show_available = !m_data->serverlist_show_available;
 			}
 			#endif
 			}
diff --git a/src/guiMainMenu.h b/src/guiMainMenu.h
index a21f3b32acb4801f24e51790859b8b8bd726fb11..a594ccd418bac8c2b9a052b5740ab6ccc1245440 100644
--- a/src/guiMainMenu.h
+++ b/src/guiMainMenu.h
@@ -29,6 +29,11 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 
 class IGameCallback;
 
+enum {
+	SERVERLIST_FAVORITES,
+	SERVERLIST_PUBLIC,
+};
+
 struct MainMenuData
 {
 	// These are in the native format of the gui elements
@@ -52,6 +57,7 @@ struct MainMenuData
 	int enable_shaders;
 	bool preload_item_visuals;
 	bool enable_particles;
+	bool liquid_finite;
 	// Server options
 	bool creative_mode;
 	bool enable_damage;
@@ -63,7 +69,7 @@ struct MainMenuData
 	std::string create_world_gameid;
 	bool only_refresh;
 
-	bool serverlist_show_available; // if false show local favorites only
+	int selected_serverlist;
 
 	std::vector<WorldSpec> worlds;
 	std::vector<SubgameSpec> games;
@@ -84,7 +90,7 @@ struct MainMenuData
 		// Actions
 		only_refresh(false),
 
-		serverlist_show_available(false)
+		selected_serverlist(SERVERLIST_FAVORITES)
 	{}
 };
 
diff --git a/src/inventory.cpp b/src/inventory.cpp
index 7051b611fe014f4c28996a35a7645b7b133b322d..d6815d32903258831c0088c1c12be4117eb1fff4 100644
--- a/src/inventory.cpp
+++ b/src/inventory.cpp
@@ -903,6 +903,10 @@ void Inventory::deSerialize(std::istream &is)
 
 			m_lists.push_back(list);
 		}
+		else
+		{
+			throw SerializationError("invalid inventory specifier");
+		}
 	}
 }
 
diff --git a/src/itemdef.cpp b/src/itemdef.cpp
index 5fd27fca382933a8cdfbd39cc7ae8b1b0283a479..72ce0e6546dd990093e5b120bd5e0b622e5d3169 100644
--- a/src/itemdef.cpp
+++ b/src/itemdef.cpp
@@ -75,6 +75,7 @@ ItemDefinition& ItemDefinition::operator=(const ItemDefinition &def)
 	}
 	groups = def.groups;
 	node_placement_prediction = def.node_placement_prediction;
+	sound_place = def.sound_place;
 	return *this;
 }
 
@@ -107,13 +108,17 @@ void ItemDefinition::reset()
 		tool_capabilities = NULL;
 	}
 	groups.clear();
+	sound_place = SimpleSoundSpec();
 
 	node_placement_prediction = "";
 }
 
-void ItemDefinition::serialize(std::ostream &os) const
+void ItemDefinition::serialize(std::ostream &os, u16 protocol_version) const
 {
-	writeU8(os, 1); // version
+	if(protocol_version <= 17)
+		writeU8(os, 1); // version
+	else
+		writeU8(os, 2); // version
 	writeU8(os, type);
 	os<<serializeString(name);
 	os<<serializeString(description);
@@ -126,7 +131,7 @@ void ItemDefinition::serialize(std::ostream &os) const
 	std::string tool_capabilities_s = "";
 	if(tool_capabilities){
 		std::ostringstream tmp_os(std::ios::binary);
-		tool_capabilities->serialize(tmp_os);
+		tool_capabilities->serialize(tmp_os, protocol_version);
 		tool_capabilities_s = tmp_os.str();
 	}
 	os<<serializeString(tool_capabilities_s);
@@ -137,6 +142,11 @@ void ItemDefinition::serialize(std::ostream &os) const
 		writeS16(os, i->second);
 	}
 	os<<serializeString(node_placement_prediction);
+	if(protocol_version > 17){
+		//serializeSimpleSoundSpec(sound_place, os);
+		os<<serializeString(sound_place.name);
+		writeF1000(os, sound_place.gain);
+	}
 }
 
 void ItemDefinition::deSerialize(std::istream &is)
@@ -146,7 +156,7 @@ void ItemDefinition::deSerialize(std::istream &is)
 
 	// Deserialize
 	int version = readU8(is);
-	if(version != 1)
+	if(version != 1 && version != 2)
 		throw SerializationError("unsupported ItemDefinition version");
 	type = (enum ItemType)readU8(is);
 	name = deSerializeString(is);
@@ -171,10 +181,24 @@ void ItemDefinition::deSerialize(std::istream &is)
 		int value = readS16(is);
 		groups[name] = value;
 	}
+	if(version == 1){
+		// We cant be sure that node_placement_prediction is send in version 1
+		try{
+			node_placement_prediction = deSerializeString(is);
+		}catch(SerializationError &e) {};
+		// Set the old default sound
+		sound_place.name = "default_place_node";
+		sound_place.gain = 0.5;
+	} else if(version == 2) {
+		node_placement_prediction = deSerializeString(is);
+		//deserializeSimpleSoundSpec(sound_place, is);
+		sound_place.name = deSerializeString(is);
+		sound_place.gain = readF1000(is);
+	}
 	// If you add anything here, insert it primarily inside the try-catch
 	// block to not need to increase the version.
 	try{
-		node_placement_prediction = deSerializeString(is);
+		
 	}catch(SerializationError &e) {};
 }
 
@@ -211,8 +235,8 @@ class CItemDefManager: public IWritableItemDefManager
 	virtual ~CItemDefManager()
 	{
 #ifndef SERVER
-		const core::list<ClientCached*> &values = m_clientcached.getValues();
-		for(core::list<ClientCached*>::ConstIterator
+		const std::list<ClientCached*> &values = m_clientcached.getValues();
+		for(std::list<ClientCached*>::const_iterator
 				i = values.begin(); i != values.end(); ++i)
 		{
 			ClientCached *cc = *i;
@@ -547,7 +571,7 @@ class CItemDefManager: public IWritableItemDefManager
 			m_aliases[name] = convert_to;
 		}
 	}
-	void serialize(std::ostream &os)
+	void serialize(std::ostream &os, u16 protocol_version)
 	{
 		writeU8(os, 0); // version
 		u16 count = m_item_definitions.size();
@@ -559,7 +583,7 @@ class CItemDefManager: public IWritableItemDefManager
 			ItemDefinition *def = i->second;
 			// Serialize ItemDefinition and write wrapped in a string
 			std::ostringstream tmp_os(std::ios::binary);
-			def->serialize(tmp_os);
+			def->serialize(tmp_os, protocol_version);
 			os<<serializeString(tmp_os.str());
 		}
 		writeU16(os, m_aliases.size());
@@ -599,7 +623,7 @@ class CItemDefManager: public IWritableItemDefManager
 	void processQueue(IGameDef *gamedef)
 	{
 #ifndef SERVER
-		while(m_get_clientcached_queue.size() > 0)
+		while(!m_get_clientcached_queue.empty())
 		{
 			GetRequest<std::string, ClientCached*, u8, u8>
 					request = m_get_clientcached_queue.pop();
diff --git a/src/itemdef.h b/src/itemdef.h
index dd20ba353a403476de70da40d7431af6ab80ff1d..08c9c835870b111254f8e43a5dbebb520e78d94f 100644
--- a/src/itemdef.h
+++ b/src/itemdef.h
@@ -26,6 +26,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include <iostream>
 #include <set>
 #include "itemgroup.h"
+#include "sound.h"
 class IGameDef;
 struct ToolCapabilities;
 
@@ -66,6 +67,7 @@ struct ItemDefinition
 	// May be NULL. If non-NULL, deleted by destructor
 	ToolCapabilities *tool_capabilities;
 	ItemGroupList groups;
+	SimpleSoundSpec sound_place;
 
 	// Client shall immediately place this node when player places the item.
 	// Server will update the precise end result a moment later.
@@ -80,7 +82,7 @@ struct ItemDefinition
 	ItemDefinition& operator=(const ItemDefinition &def);
 	~ItemDefinition();
 	void reset();
-	void serialize(std::ostream &os) const;
+	void serialize(std::ostream &os, u16 protocol_version) const;
 	void deSerialize(std::istream &is);
 private:
 	void resetInitial();
@@ -109,7 +111,7 @@ class IItemDefManager
 		IGameDef *gamedef) const=0;
 #endif
 
-	virtual void serialize(std::ostream &os)=0;
+	virtual void serialize(std::ostream &os, u16 protocol_version)=0;
 };
 
 class IWritableItemDefManager : public IItemDefManager
@@ -146,7 +148,7 @@ class IWritableItemDefManager : public IItemDefManager
 	virtual void registerAlias(const std::string &name,
 			const std::string &convert_to)=0;
 
-	virtual void serialize(std::ostream &os)=0;
+	virtual void serialize(std::ostream &os, u16 protocol_version)=0;
 	virtual void deSerialize(std::istream &is)=0;
 
 	// Do stuff asked by threads that can only be done in the main thread
diff --git a/src/json/CMakeLists.txt b/src/json/CMakeLists.txt
index 0957799aa8d4f0c2e6fd5001e47d6747800fd271..5887d523aabea878ccdc43ddfa47591ac3393d62 100644
--- a/src/json/CMakeLists.txt
+++ b/src/json/CMakeLists.txt
@@ -6,9 +6,9 @@ else( UNIX )
 	set(json_platform_LIBS "")
 endif( UNIX )
 
-add_library(json ${json_SRCS})
+add_library(jsoncpp ${json_SRCS})
 
 target_link_libraries(
-	json
+	jsoncpp
 	${json_platform_LIBS}
 )
diff --git a/src/keycode.cpp b/src/keycode.cpp
index 8aadab2f17b6f79371c80a660c947e7df3bd753c..96631b4eaf1e158d408769350fff538505b34a31 100644
--- a/src/keycode.cpp
+++ b/src/keycode.cpp
@@ -345,17 +345,16 @@ const KeyPress NumberKey[] = {
 */
 
 // A simple cache for quicker lookup
-core::map<std::string, KeyPress> g_key_setting_cache;
+std::map<std::string, KeyPress> g_key_setting_cache;
 
 KeyPress getKeySetting(const char *settingname)
 {
-	core::map<std::string, KeyPress>::Node *n;
+	std::map<std::string, KeyPress>::iterator n;
 	n = g_key_setting_cache.find(settingname);
-	if(n)
-		return n->getValue();
-	g_key_setting_cache.insert(settingname,
-			g_settings->get(settingname).c_str());
-	return g_key_setting_cache.find(settingname)->getValue();
+	if(n != g_key_setting_cache.end())
+		return n->second;
+	g_key_setting_cache[settingname] = g_settings->get(settingname).c_str();
+	return g_key_setting_cache.find(settingname)->second;
 }
 
 void clearKeyCache()
diff --git a/src/localplayer.cpp b/src/localplayer.cpp
index 0554302e0496a7b4c9fb742a107ec3736ef00b95..ee9b41c58eb34f948b2edccdacacb2aed2e63304 100644
--- a/src/localplayer.cpp
+++ b/src/localplayer.cpp
@@ -25,6 +25,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include "gamedef.h"
 #include "nodedef.h"
 #include "settings.h"
+#include "environment.h"
 #include "map.h"
 #include "util/numeric.h"
 
@@ -57,9 +58,10 @@ LocalPlayer::~LocalPlayer()
 {
 }
 
-void LocalPlayer::move(f32 dtime, Map &map, f32 pos_max_d,
-		core::list<CollisionInfo> *collision_info)
+void LocalPlayer::move(f32 dtime, ClientEnvironment *env, f32 pos_max_d,
+		std::list<CollisionInfo> *collision_info)
 {
+	Map *map = &env->getMap();
 	INodeDefManager *nodemgr = m_gamedef->ndef();
 
 	v3f position = getPosition();
@@ -97,15 +99,15 @@ void LocalPlayer::move(f32 dtime, Map &map, f32 pos_max_d,
 		if(in_liquid)
 		{
 			v3s16 pp = floatToInt(position + v3f(0,BS*0.1,0), BS);
-			in_liquid = nodemgr->get(map.getNode(pp).getContent()).isLiquid();
-			liquid_viscosity = nodemgr->get(map.getNode(pp).getContent()).liquid_viscosity;
+			in_liquid = nodemgr->get(map->getNode(pp).getContent()).isLiquid();
+			liquid_viscosity = nodemgr->get(map->getNode(pp).getContent()).liquid_viscosity;
 		}
 		// If not in liquid, the threshold of going in is at lower y
 		else
 		{
 			v3s16 pp = floatToInt(position + v3f(0,BS*0.5,0), BS);
-			in_liquid = nodemgr->get(map.getNode(pp).getContent()).isLiquid();
-			liquid_viscosity = nodemgr->get(map.getNode(pp).getContent()).liquid_viscosity;
+			in_liquid = nodemgr->get(map->getNode(pp).getContent()).isLiquid();
+			liquid_viscosity = nodemgr->get(map->getNode(pp).getContent()).liquid_viscosity;
 		}
 	}
 	catch(InvalidPositionException &e)
@@ -118,7 +120,7 @@ void LocalPlayer::move(f32 dtime, Map &map, f32 pos_max_d,
 	*/
 	try{
 		v3s16 pp = floatToInt(position + v3f(0,0,0), BS);
-		in_liquid_stable = nodemgr->get(map.getNode(pp).getContent()).isLiquid();
+		in_liquid_stable = nodemgr->get(map->getNode(pp).getContent()).isLiquid();
 	}
 	catch(InvalidPositionException &e)
 	{
@@ -132,8 +134,8 @@ void LocalPlayer::move(f32 dtime, Map &map, f32 pos_max_d,
 	try {
 		v3s16 pp = floatToInt(position + v3f(0,0.5*BS,0), BS);
 		v3s16 pp2 = floatToInt(position + v3f(0,-0.2*BS,0), BS);
-		is_climbing = ((nodemgr->get(map.getNode(pp).getContent()).climbable ||
-		nodemgr->get(map.getNode(pp2).getContent()).climbable) && !free_move);
+		is_climbing = ((nodemgr->get(map->getNode(pp).getContent()).climbable ||
+		nodemgr->get(map->getNode(pp2).getContent()).climbable) && !free_move);
 	}
 	catch(InvalidPositionException &e)
 	{
@@ -197,7 +199,7 @@ void LocalPlayer::move(f32 dtime, Map &map, f32 pos_max_d,
 
 	v3f accel_f = v3f(0,0,0);
 
-	collisionMoveResult result = collisionMoveSimple(&map, m_gamedef,
+	collisionMoveResult result = collisionMoveSimple(env, m_gamedef,
 			pos_max_d, playerbox, player_stepheight, dtime,
 			position, m_speed, accel_f);
 
@@ -219,7 +221,7 @@ void LocalPlayer::move(f32 dtime, Map &map, f32 pos_max_d,
 	*/
 	v3s16 current_node = floatToInt(position - v3f(0,BS/2,0), BS);
 	if(m_sneak_node_exists &&
-	   nodemgr->get(map.getNodeNoEx(m_old_node_below)).name == "air" &&
+	   nodemgr->get(map->getNodeNoEx(m_old_node_below)).name == "air" &&
 	   m_old_node_below_type != "air")
 	{
 		// Old node appears to have been removed; that is,
@@ -227,7 +229,7 @@ void LocalPlayer::move(f32 dtime, Map &map, f32 pos_max_d,
 		m_need_to_get_new_sneak_node = false;
 		m_sneak_node_exists = false;
 	}
-	else if(nodemgr->get(map.getNodeNoEx(current_node)).name != "air")
+	else if(nodemgr->get(map->getNodeNoEx(current_node)).name != "air")
 	{
 		// We are on something, so make sure to recalculate the sneak
 		// node.
@@ -267,10 +269,10 @@ void LocalPlayer::move(f32 dtime, Map &map, f32 pos_max_d,
 
 			try{
 				// The node to be sneaked on has to be walkable
-				if(nodemgr->get(map.getNode(p)).walkable == false)
+				if(nodemgr->get(map->getNode(p)).walkable == false)
 					continue;
 				// And the node above it has to be nonwalkable
-				if(nodemgr->get(map.getNode(p+v3s16(0,1,0))).walkable == true)
+				if(nodemgr->get(map->getNode(p+v3s16(0,1,0))).walkable == true)
 					continue;
 			}
 			catch(InvalidPositionException &e)
@@ -331,7 +333,7 @@ void LocalPlayer::move(f32 dtime, Map &map, f32 pos_max_d,
 	{
 		camera_barely_in_ceiling = false;
 		v3s16 camera_np = floatToInt(getEyePosition(), BS);
-		MapNode n = map.getNodeNoEx(camera_np);
+		MapNode n = map->getNodeNoEx(camera_np);
 		if(n.getContent() != CONTENT_IGNORE){
 			if(nodemgr->get(n).walkable && nodemgr->get(n).solidness == 2){
 				camera_barely_in_ceiling = true;
@@ -343,21 +345,21 @@ void LocalPlayer::move(f32 dtime, Map &map, f32 pos_max_d,
 		Update the node last under the player
 	*/
 	m_old_node_below = floatToInt(position - v3f(0,BS/2,0), BS);
-	m_old_node_below_type = nodemgr->get(map.getNodeNoEx(m_old_node_below)).name;
+	m_old_node_below_type = nodemgr->get(map->getNodeNoEx(m_old_node_below)).name;
 	
 	/*
 		Check properties of the node on which the player is standing
 	*/
-	const ContentFeatures &f = nodemgr->get(map.getNodeNoEx(getStandingNodePos()));
+	const ContentFeatures &f = nodemgr->get(map->getNodeNoEx(getStandingNodePos()));
 	// Determine if jumping is possible
 	m_can_jump = touching_ground && !in_liquid;
 	if(itemgroup_get(f.groups, "disable_jump"))
 		m_can_jump = false;
 }
 
-void LocalPlayer::move(f32 dtime, Map &map, f32 pos_max_d)
+void LocalPlayer::move(f32 dtime, ClientEnvironment *env, f32 pos_max_d)
 {
-	move(dtime, map, pos_max_d, NULL);
+	move(dtime, env, pos_max_d, NULL);
 }
 
 void LocalPlayer::applyControl(float dtime)
diff --git a/src/localplayer.h b/src/localplayer.h
index f372c787d8eeefd6d58b800f8e38ec7caf29c699..17434d37910e2103b377afd61e47317651a1a2bc 100644
--- a/src/localplayer.h
+++ b/src/localplayer.h
@@ -21,6 +21,9 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #define LOCALPLAYER_HEADER
 
 #include "player.h"
+#include <list>
+
+class ClientEnvironment;
 
 class LocalPlayer : public Player
 {
@@ -37,9 +40,9 @@ class LocalPlayer : public Player
 
 	v3f overridePosition;
 	
-	void move(f32 dtime, Map &map, f32 pos_max_d,
-			core::list<CollisionInfo> *collision_info);
-	void move(f32 dtime, Map &map, f32 pos_max_d);
+	void move(f32 dtime, ClientEnvironment *env, f32 pos_max_d,
+			std::list<CollisionInfo> *collision_info);
+	void move(f32 dtime, ClientEnvironment *env, f32 pos_max_d);
 
 	void applyControl(float dtime);
 
diff --git a/src/main.cpp b/src/main.cpp
index cfd643ac76ba6fb9f7fc6d24c75dc66ba15eb82f..2e57a8c20b2681bc381c8d17cbd5f9b487a94d17 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -50,6 +50,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include "irrlichttypes_extrabloated.h"
 #include "debug.h"
 #include "test.h"
+#include "clouds.h"
 #include "server.h"
 #include "constants.h"
 #include "porting.h"
@@ -132,7 +133,12 @@ MainGameCallback *g_gamecallback = NULL;
 u32 getTimeMs()
 {
 	/* Use imprecise system calls directly (from porting.h) */
-	return porting::getTimeMs();
+	return porting::getTime(PRECISION_MILLI);
+}
+
+u32 getTime(TimePrecision prec)
+{
+	return porting::getTime(prec);
 }
 
 #else
@@ -141,7 +147,7 @@ u32 getTimeMs()
 class TimeGetter
 {
 public:
-	virtual u32 getTime() = 0;
+	virtual u32 getTime(TimePrecision prec) = 0;
 };
 
 // A precise irrlicht one
@@ -151,11 +157,15 @@ class IrrlichtTimeGetter: public TimeGetter
 	IrrlichtTimeGetter(IrrlichtDevice *device):
 		m_device(device)
 	{}
-	u32 getTime()
-	{
-		if(m_device == NULL)
-			return 0;
-		return m_device->getTimer()->getRealTime();
+	u32 getTime(TimePrecision prec)
+	{
+		if (prec == PRECISION_MILLI) {
+			if(m_device == NULL)
+				return 0;
+			return m_device->getTimer()->getRealTime();
+		} else {
+			return porting::getTime(prec);
+		}
 	}
 private:
 	IrrlichtDevice *m_device;
@@ -164,9 +174,9 @@ class IrrlichtTimeGetter: public TimeGetter
 class SimpleTimeGetter: public TimeGetter
 {
 public:
-	u32 getTime()
+	u32 getTime(TimePrecision prec)
 	{
-		return porting::getTimeMs();
+		return porting::getTime(prec);
 	}
 };
 
@@ -178,7 +188,13 @@ u32 getTimeMs()
 {
 	if(g_timegetter == NULL)
 		return 0;
-	return g_timegetter->getTime();
+	return g_timegetter->getTime(PRECISION_MILLI);
+}
+
+u32 getTime(TimePrecision prec) {
+	if (g_timegetter == NULL)
+		return 0;
+	return g_timegetter->getTime(prec);
 }
 
 #endif
@@ -596,50 +612,120 @@ class RandomInputHandler : public InputHandler
 	bool rightreleased;
 };
 
-void drawMenuBackground(video::IVideoDriver* driver)
-{
+//Draw the tiled menu background
+void drawMenuBackground(video::IVideoDriver* driver) {
 	core::dimension2d<u32> screensize = driver->getScreenSize();
+
+	std::string path = getTexturePath("menubg.png");
+	if (path[0]) {
+		video::ITexture *bgtexture =
+			driver->getTexture(path.c_str());
+
+		if (bgtexture) {
+			s32 scaledsize = 128;
 		
-	video::ITexture *bgtexture =
-			driver->getTexture(getTexturePath("menubg.png").c_str());
-	if(bgtexture)
-	{
-		s32 scaledsize = 128;
-		
-		// The important difference between destsize and screensize is
-		// that destsize is rounded to whole scaled pixels.
-		// These formulas use component-wise multiplication and division of v2u32.
-		v2u32 texturesize = bgtexture->getSize();
-		v2u32 sourcesize = texturesize * screensize / scaledsize + v2u32(1,1);
-		v2u32 destsize = scaledsize * sourcesize / texturesize;
+			// The important difference between destsize and screensize is
+			// that destsize is rounded to whole scaled pixels.
+			// These formulas use component-wise multiplication and division of v2u32.
+			v2u32 texturesize = bgtexture->getSize();
+			v2u32 sourcesize = texturesize * screensize / scaledsize + v2u32(1,1);
+			v2u32 destsize = scaledsize * sourcesize / texturesize;
 		
-		// Default texture wrapping mode in Irrlicht is ETC_REPEAT.
-		driver->draw2DImage(bgtexture,
-			core::rect<s32>(0, 0, destsize.X, destsize.Y),
-			core::rect<s32>(0, 0, sourcesize.X, sourcesize.Y),
-			NULL, NULL, true);
+			// Default texture wrapping mode in Irrlicht is ETC_REPEAT.
+			driver->draw2DImage(bgtexture,
+				core::rect<s32>(0, 0, destsize.X, destsize.Y),
+				core::rect<s32>(0, 0, sourcesize.X, sourcesize.Y),
+				NULL, NULL, true);
+		}
 	}
-	
-	video::ITexture *logotexture =
-			driver->getTexture(getTexturePath("menulogo.png").c_str());
-	if(logotexture)
-	{
-		v2s32 logosize(logotexture->getOriginalSize().Width,
-				logotexture->getOriginalSize().Height);
-		logosize *= 4;
-
-		video::SColor bgcolor(255,50,50,50);
-		core::rect<s32> bgrect(0, screensize.Height-logosize.Y-20,
-				screensize.Width, screensize.Height);
-		driver->draw2DRectangle(bgcolor, bgrect, NULL);
-
-		core::rect<s32> rect(0,0,logosize.X,logosize.Y);
-		rect += v2s32(screensize.Width/2,screensize.Height-10-logosize.Y);
-		rect -= v2s32(logosize.X/2, 0);
-		driver->draw2DImage(logotexture, rect,
-			core::rect<s32>(core::position2d<s32>(0,0),
-			core::dimension2di(logotexture->getSize())),
-			NULL, NULL, true);
+}
+
+//Draw the footer at the bottom of the window
+void drawMenuFooter(video::IVideoDriver* driver, bool clouds) {
+	core::dimension2d<u32> screensize = driver->getScreenSize();
+	std::string path = getTexturePath(clouds ?
+						"menufooter_clouds.png" : "menufooter.png");
+	if (path[0]) {
+		video::ITexture *footertexture =
+			driver->getTexture(path.c_str());
+
+		if (footertexture) {
+			f32 mult = (((f32)screensize.Width)) /
+				((f32)footertexture->getOriginalSize().Width);
+
+			v2s32 footersize(((f32)footertexture->getOriginalSize().Width) * mult,
+					((f32)footertexture->getOriginalSize().Height) * mult);
+
+			// Don't draw the footer if there isn't enough room
+			s32 free_space = (((s32)screensize.Height)-320)/2;
+			if (free_space > footersize.Y) {
+				core::rect<s32> rect(0,0,footersize.X,footersize.Y);
+				rect += v2s32(screensize.Width/2,screensize.Height-footersize.Y);
+				rect -= v2s32(footersize.X/2, 0);
+
+				driver->draw2DImage(footertexture, rect,
+					core::rect<s32>(core::position2d<s32>(0,0),
+					core::dimension2di(footertexture->getSize())),
+					NULL, NULL, true);
+			}
+		}
+	}
+}
+
+// Draw the Header over the main menu
+void drawMenuHeader(video::IVideoDriver* driver) {
+	core::dimension2d<u32> screensize = driver->getScreenSize();
+
+	std::string path = getTexturePath("menuheader.png");
+	if (path[0]) {
+		video::ITexture *splashtexture =
+		driver->getTexture(path.c_str());
+
+		if(splashtexture) {
+			//v2s32 splashsize((splashtexture->getOriginalSize().Width*100)/
+			//	splashtexture->getOriginalSize().Height, 80);
+
+			f32 mult = (((f32)screensize.Width / 2)) /
+				((f32)splashtexture->getOriginalSize().Width);
+
+			v2s32 splashsize(((f32)splashtexture->getOriginalSize().Width) * mult,
+					((f32)splashtexture->getOriginalSize().Height) * mult);
+
+			// Don't draw the header is there isn't enough room
+			s32 free_space = (((s32)screensize.Height)-320)/2;
+			if (free_space > splashsize.Y) {
+				core::rect<s32> splashrect(0, 0, splashsize.X, splashsize.Y);
+				splashrect += v2s32((screensize.Width/2)-(splashsize.X/2),
+					((free_space/2)-splashsize.Y/2)+10);
+
+				video::SColor bgcolor(255,50,50,50);
+
+				driver->draw2DImage(splashtexture, splashrect,
+					core::rect<s32>(core::position2d<s32>(0,0),
+					core::dimension2di(splashtexture->getSize())),
+					NULL, NULL, true);
+			}
+		}
+	}
+}
+
+// Draw the Splash over the clouds and under the main menu
+void drawMenuSplash(video::IVideoDriver* driver) {
+	core::dimension2d<u32> screensize = driver->getScreenSize();
+	if (getTexturePath("menusplash.png") != "") {
+		video::ITexture *splashtexture =
+			driver->getTexture(getTexturePath("menusplash.png").c_str());
+
+		if(splashtexture) {
+			core::rect<s32> splashrect(0, 0, screensize.Width, screensize.Height);
+
+			video::SColor bgcolor(255,50,50,50);
+
+			driver->draw2DImage(splashtexture, splashrect,
+				core::rect<s32>(core::position2d<s32>(0,0),
+				core::dimension2di(splashtexture->getSize())),
+				NULL, NULL, true);
+		}
 	}
 }
 
@@ -700,14 +786,14 @@ void SpeedTests()
 	}
 
 	{
-		TimeTaker timer("Testing core::map speed");
+		TimeTaker timer("Testing std::map speed");
 		
-		core::map<v2s16, f32> map1;
+		std::map<v2s16, f32> map1;
 		tempf = -324;
 		const s16 ii=300;
 		for(s16 y=0; y<ii; y++){
 			for(s16 x=0; x<ii; x++){
-				map1.insert(v2s16(x,y), tempf);
+				map1[v2s16(x,y)] =  tempf;
 				tempf += 1;
 			}
 		}
@@ -734,7 +820,7 @@ void SpeedTests()
 			}
 		}
 		// Do at least 10ms
-		while(timer.getTime() < 10);
+		while(timer.getTimerTime() < 10);
 
 		u32 dtime = timer.stop();
 		u32 per_ms = n / dtime;
@@ -788,48 +874,48 @@ int main(int argc, char *argv[])
 	*/
 	
 	// List all allowed options
-	core::map<std::string, ValueSpec> allowed_options;
-	allowed_options.insert("help", ValueSpec(VALUETYPE_FLAG,
-			_("Show allowed options")));
-	allowed_options.insert("config", ValueSpec(VALUETYPE_STRING,
-			_("Load configuration from specified file")));
-	allowed_options.insert("port", ValueSpec(VALUETYPE_STRING,
-			_("Set network port (UDP)")));
-	allowed_options.insert("disable-unittests", ValueSpec(VALUETYPE_FLAG,
-			_("Disable unit tests")));
-	allowed_options.insert("enable-unittests", ValueSpec(VALUETYPE_FLAG,
-			_("Enable unit tests")));
-	allowed_options.insert("map-dir", ValueSpec(VALUETYPE_STRING,
-			_("Same as --world (deprecated)")));
-	allowed_options.insert("world", ValueSpec(VALUETYPE_STRING,
-			_("Set world path (implies local game) ('list' lists all)")));
-	allowed_options.insert("worldname", ValueSpec(VALUETYPE_STRING,
-			_("Set world by name (implies local game)")));
-	allowed_options.insert("info", ValueSpec(VALUETYPE_FLAG,
-			_("Print more information to console")));
-	allowed_options.insert("verbose", ValueSpec(VALUETYPE_FLAG,
-			_("Print even more information to console")));
-	allowed_options.insert("trace", ValueSpec(VALUETYPE_FLAG,
-			_("Print enormous amounts of information to log and console")));
-	allowed_options.insert("logfile", ValueSpec(VALUETYPE_STRING,
-			_("Set logfile path ('' = no logging)")));
-	allowed_options.insert("gameid", ValueSpec(VALUETYPE_STRING,
-			_("Set gameid (\"--gameid list\" prints available ones)")));
+	std::map<std::string, ValueSpec> allowed_options;
+	allowed_options.insert(std::make_pair("help", ValueSpec(VALUETYPE_FLAG,
+			_("Show allowed options"))));
+	allowed_options.insert(std::make_pair("config", ValueSpec(VALUETYPE_STRING,
+			_("Load configuration from specified file"))));
+	allowed_options.insert(std::make_pair("port", ValueSpec(VALUETYPE_STRING,
+			_("Set network port (UDP)"))));
+	allowed_options.insert(std::make_pair("disable-unittests", ValueSpec(VALUETYPE_FLAG,
+			_("Disable unit tests"))));
+	allowed_options.insert(std::make_pair("enable-unittests", ValueSpec(VALUETYPE_FLAG,
+			_("Enable unit tests"))));
+	allowed_options.insert(std::make_pair("map-dir", ValueSpec(VALUETYPE_STRING,
+			_("Same as --world (deprecated)"))));
+	allowed_options.insert(std::make_pair("world", ValueSpec(VALUETYPE_STRING,
+			_("Set world path (implies local game) ('list' lists all)"))));
+	allowed_options.insert(std::make_pair("worldname", ValueSpec(VALUETYPE_STRING,
+			_("Set world by name (implies local game)"))));
+	allowed_options.insert(std::make_pair("info", ValueSpec(VALUETYPE_FLAG,
+			_("Print more information to console"))));
+	allowed_options.insert(std::make_pair("verbose",  ValueSpec(VALUETYPE_FLAG,
+			_("Print even more information to console"))));
+	allowed_options.insert(std::make_pair("trace", ValueSpec(VALUETYPE_FLAG,
+			_("Print enormous amounts of information to log and console"))));
+	allowed_options.insert(std::make_pair("logfile", ValueSpec(VALUETYPE_STRING,
+			_("Set logfile path ('' = no logging)"))));
+	allowed_options.insert(std::make_pair("gameid", ValueSpec(VALUETYPE_STRING,
+			_("Set gameid (\"--gameid list\" prints available ones)"))));
 #ifndef SERVER
-	allowed_options.insert("speedtests", ValueSpec(VALUETYPE_FLAG,
-			_("Run speed tests")));
-	allowed_options.insert("address", ValueSpec(VALUETYPE_STRING,
-			_("Address to connect to. ('' = local game)")));
-	allowed_options.insert("random-input", ValueSpec(VALUETYPE_FLAG,
-			_("Enable random user input, for testing")));
-	allowed_options.insert("server", ValueSpec(VALUETYPE_FLAG,
-			_("Run dedicated server")));
-	allowed_options.insert("name", ValueSpec(VALUETYPE_STRING,
-			_("Set player name")));
-	allowed_options.insert("password", ValueSpec(VALUETYPE_STRING,
-			_("Set password")));
-	allowed_options.insert("go", ValueSpec(VALUETYPE_FLAG,
-			_("Disable main menu")));
+	allowed_options.insert(std::make_pair("speedtests", ValueSpec(VALUETYPE_FLAG,
+			_("Run speed tests"))));
+	allowed_options.insert(std::make_pair("address", ValueSpec(VALUETYPE_STRING,
+			_("Address to connect to. ('' = local game)"))));
+	allowed_options.insert(std::make_pair("random-input", ValueSpec(VALUETYPE_FLAG,
+			_("Enable random user input, for testing"))));
+	allowed_options.insert(std::make_pair("server", ValueSpec(VALUETYPE_FLAG,
+			_("Run dedicated server"))));
+	allowed_options.insert(std::make_pair("name", ValueSpec(VALUETYPE_STRING,
+			_("Set player name"))));
+	allowed_options.insert(std::make_pair("password", ValueSpec(VALUETYPE_STRING,
+			_("Set password"))));
+	allowed_options.insert(std::make_pair("go", ValueSpec(VALUETYPE_FLAG,
+			_("Disable main menu"))));
 #endif
 
 	Settings cmd_args;
@@ -839,20 +925,20 @@ int main(int argc, char *argv[])
 	if(ret == false || cmd_args.getFlag("help") || cmd_args.exists("nonopt1"))
 	{
 		dstream<<_("Allowed options:")<<std::endl;
-		for(core::map<std::string, ValueSpec>::Iterator
-				i = allowed_options.getIterator();
-				i.atEnd() == false; i++)
+		for(std::map<std::string, ValueSpec>::iterator
+				i = allowed_options.begin();
+				i != allowed_options.end(); ++i)
 		{
 			std::ostringstream os1(std::ios::binary);
-			os1<<"  --"<<i.getNode()->getKey();
-			if(i.getNode()->getValue().type == VALUETYPE_FLAG)
+			os1<<"  --"<<i->first;
+			if(i->second.type == VALUETYPE_FLAG)
 				{}
 			else
 				os1<<_(" <value>");
 			dstream<<padStringRight(os1.str(), 24);
 
-			if(i.getNode()->getValue().help != NULL)
-				dstream<<i.getNode()->getValue().help;
+			if(i->second.help != NULL)
+				dstream<<i->second.help;
 			dstream<<std::endl;
 		}
 
@@ -953,7 +1039,7 @@ int main(int argc, char *argv[])
 	}
 	else
 	{
-		core::array<std::string> filenames;
+		std::vector<std::string> filenames;
 		filenames.push_back(porting::path_user +
 				DIR_DELIM + "minetest.conf");
 		// Legacy configuration file location
@@ -1279,6 +1365,14 @@ int main(int argc, char *argv[])
 		driverType = video::EDT_DIRECT3D9;
 	else if(driverstring == "opengl")
 		driverType = video::EDT_OPENGL;
+#ifdef _IRR_COMPILE_WITH_OGLES1_
+	else if(driverstring == "ogles1")
+		driverType = video::EDT_OGLES1;
+#endif
+#ifdef _IRR_COMPILE_WITH_OGLES2_
+	else if(driverstring == "ogles2")
+		driverType = video::EDT_OGLES2;
+#endif
 	else
 	{
 		errorstream<<"WARNING: Invalid video_driver specified; defaulting "
@@ -1462,6 +1556,8 @@ int main(int argc, char *argv[])
 				MainMenuData menudata;
 				if(g_settings->exists("selected_mainmenu_tab"))
 					menudata.selected_tab = g_settings->getS32("selected_mainmenu_tab");
+				if(g_settings->exists("selected_serverlist"))
+					menudata.selected_serverlist = g_settings->getS32("selected_serverlist");
 				menudata.address = narrow_to_wide(address);
 				menudata.name = narrow_to_wide(playername);
 				menudata.port = narrow_to_wide(itos(port));
@@ -1478,6 +1574,7 @@ int main(int argc, char *argv[])
 				menudata.enable_shaders = g_settings->getS32("enable_shaders");
 				menudata.preload_item_visuals = g_settings->getBool("preload_item_visuals");
 				menudata.enable_particles = g_settings->getBool("enable_particles");
+				menudata.liquid_finite = g_settings->getBool("liquid_finite");
 				driver->setTextureCreationFlag(video::ETCF_CREATE_MIP_MAPS, menudata.mip_map);
 				menudata.creative_mode = g_settings->getBool("creative_mode");
 				menudata.enable_damage = g_settings->getBool("enable_damage");
@@ -1518,7 +1615,7 @@ int main(int argc, char *argv[])
 				if(skip_main_menu == false)
 				{
 					video::IVideoDriver* driver = device->getVideoDriver();
-					
+					float fps_max = g_settings->getFloat("fps_max");
 					infostream<<"Waiting for other menus"<<std::endl;
 					while(device->run() && kill == false)
 					{
@@ -1540,6 +1637,22 @@ int main(int argc, char *argv[])
 								&g_menumgr, &menudata, g_gamecallback);
 					menu->allowFocusRemoval(true);
 
+					// Clouds for the main menu
+					bool cloud_menu_background = false;
+					Clouds *clouds = NULL;
+					if (g_settings->getBool("menu_clouds")) {
+						cloud_menu_background = true;
+						clouds = new Clouds(smgr->getRootSceneNode(),
+											smgr, -1, rand(), 100);
+						clouds->update(v2f(0, 0), video::SColor(255,200,200,255));
+
+						// A camera to see the clouds
+						scene::ICameraSceneNode* camera;
+						camera = smgr->addCameraSceneNode(0,
+									v3f(0,0,0), v3f(0, 60, 100));
+						camera->setFarValue(10000);
+					}
+
 					if(error_message != L"")
 					{
 						verbosestream<<"error_message = "
@@ -1552,6 +1665,9 @@ int main(int argc, char *argv[])
 						error_message = L"";
 					}
 
+					// Time is in milliseconds, for clouds
+					u32 lasttime = device->getTimer()->getTime();
+
 					infostream<<"Created main menu"<<std::endl;
 
 					while(device->run() && kill == false)
@@ -1559,26 +1675,75 @@ int main(int argc, char *argv[])
 						if(menu->getStatus() == true)
 							break;
 
-						//driver->beginScene(true, true, video::SColor(255,0,0,0));
-						driver->beginScene(true, true, video::SColor(255,128,128,128));
+						// Time calc for the clouds
+						f32 dtime; // in seconds
+						if (cloud_menu_background) {
+							u32 time = device->getTimer()->getTime();
+							if(time > lasttime)
+								dtime = (time - lasttime) / 1000.0;
+ 							else
+								dtime = 0;
+							lasttime = time;
+						}
 
-						drawMenuBackground(driver);
+						//driver->beginScene(true, true, video::SColor(255,0,0,0));
+						driver->beginScene(true, true, video::SColor(255,140,186,250));
+
+						if (cloud_menu_background) {
+							// *3 otherwise the clouds would move very slowly
+							clouds->step(dtime*3); 
+							clouds->render();
+							smgr->drawAll();
+							drawMenuSplash(driver);
+							drawMenuFooter(driver, true);
+							drawMenuHeader(driver);
+						} else {
+							drawMenuBackground(driver);
+							drawMenuFooter(driver, false);
+						}
 
 						guienv->drawAll();
-						
+
 						driver->endScene();
 						
 						// On some computers framerate doesn't seem to be
 						// automatically limited
-						sleep_ms(25);
+						if (cloud_menu_background) {
+							// Time of frame without fps limit
+							float busytime;
+							u32 busytime_u32;
+							// not using getRealTime is necessary for wine
+							u32 time = device->getTimer()->getTime();
+							if(time > lasttime)
+								busytime_u32 = time - lasttime;
+							else
+								busytime_u32 = 0;
+							busytime = busytime_u32 / 1000.0;
+
+							// FPS limiter
+							u32 frametime_min = 1000./fps_max;
+			
+							if(busytime_u32 < frametime_min) {
+								u32 sleeptime = frametime_min - busytime_u32;
+								device->sleep(sleeptime);
+							}
+						} else {
+							sleep_ms(25);
+						}
 					}
 					
 					infostream<<"Dropping main menu"<<std::endl;
 
 					menu->drop();
+					if (cloud_menu_background) {
+						clouds->drop();
+						smgr->clear();
+					}
 				}
 
 				playername = wide_to_narrow(menudata.name);
+				if (playername == "")
+					playername = std::string("Guest") + itos(myrand_range(1000,9999));
 				password = translatePassword(playername, menudata.password);
 				//infostream<<"Main: password hash: '"<<password<<"'"<<std::endl;
 
@@ -1589,6 +1754,7 @@ int main(int argc, char *argv[])
 				simple_singleplayer_mode = menudata.simple_singleplayer_mode;
 				// Save settings
 				g_settings->setS32("selected_mainmenu_tab", menudata.selected_tab);
+				g_settings->setS32("selected_serverlist", menudata.selected_serverlist);
 				g_settings->set("new_style_leaves", itos(menudata.fancy_trees));
 				g_settings->set("smooth_lighting", itos(menudata.smooth_lighting));
 				g_settings->set("enable_3d_clouds", itos(menudata.clouds_3d));
@@ -1602,6 +1768,7 @@ int main(int argc, char *argv[])
 				g_settings->setS32("enable_shaders", menudata.enable_shaders);
 				g_settings->set("preload_item_visuals", itos(menudata.preload_item_visuals));
 				g_settings->set("enable_particles", itos(menudata.enable_particles));
+				g_settings->set("liquid_finite", itos(menudata.liquid_finite));
 
 				g_settings->set("creative_mode", itos(menudata.creative_mode));
 				g_settings->set("enable_damage", itos(menudata.enable_damage));
diff --git a/src/mainmenumanager.h b/src/mainmenumanager.h
index ce7684f1156bc034581342b2bccad874bf93f820..a3133686bb69e32735f6d336288785f45a0710cc 100644
--- a/src/mainmenumanager.h
+++ b/src/mainmenumanager.h
@@ -26,6 +26,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include "debug.h" // assert
 #include "modalMenu.h"
 #include "guiPauseMenu.h" //For IGameCallback
+#include <list>
 
 extern gui::IGUIEnvironment* guienv;
 extern gui::IGUIStaticText *guiroot;
@@ -37,15 +38,15 @@ class MainMenuManager : public IMenuManager
 public:
 	virtual void createdMenu(GUIModalMenu *menu)
 	{
-		for(core::list<GUIModalMenu*>::Iterator
+		for(std::list<GUIModalMenu*>::iterator
 				i = m_stack.begin();
-				i != m_stack.end(); i++)
+				i != m_stack.end(); ++i)
 		{
 			assert(*i != menu);
 		}
 
 		if(m_stack.size() != 0)
-			(*m_stack.getLast())->setVisible(false);
+			m_stack.back()->setVisible(false);
 		m_stack.push_back(menu);
 	}
 
@@ -55,9 +56,9 @@ class MainMenuManager : public IMenuManager
 		bool removed_entry;
 		do{
 			removed_entry = false;
-			for(core::list<GUIModalMenu*>::Iterator
+			for(std::list<GUIModalMenu*>::iterator
 					i = m_stack.begin();
-					i != m_stack.end(); i++)
+					i != m_stack.end(); ++i)
 			{
 				if(*i == menu)
 				{
@@ -73,7 +74,7 @@ class MainMenuManager : public IMenuManager
 		m_stack.erase(i);*/
 		
 		if(m_stack.size() != 0)
-			(*m_stack.getLast())->setVisible(true);
+			m_stack.back()->setVisible(true);
 	}
 
 	u32 menuCount()
@@ -81,7 +82,7 @@ class MainMenuManager : public IMenuManager
 		return m_stack.size();
 	}
 
-	core::list<GUIModalMenu*> m_stack;
+	std::list<GUIModalMenu*> m_stack;
 };
 
 extern MainMenuManager g_menumgr;
diff --git a/src/map.cpp b/src/map.cpp
index 4be094326f182e47243699107f704cbfa96876ef..5d6b79fb098dd1fb78dafc69a8a97fe95ec1daf9 100644
--- a/src/map.cpp
+++ b/src/map.cpp
@@ -35,6 +35,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include "rollback_interface.h"
 #include "emerge.h"
 #include "mapgen_v6.h"
+#include "mapgen_indev.h"
 
 #define PP(x) "("<<(x).X<<","<<(x).Y<<","<<(x).Z<<")"
 
@@ -73,34 +74,30 @@ Map::~Map()
 	/*
 		Free all MapSectors
 	*/
-	core::map<v2s16, MapSector*>::Iterator i = m_sectors.getIterator();
-	for(; i.atEnd() == false; i++)
+	for(std::map<v2s16, MapSector*>::iterator i = m_sectors.begin();
+		i != m_sectors.end(); ++i)
 	{
-		MapSector *sector = i.getNode()->getValue();
-		delete sector;
+		delete i->second;
 	}
 }
 
 void Map::addEventReceiver(MapEventReceiver *event_receiver)
 {
-	m_event_receivers.insert(event_receiver, false);
+	m_event_receivers.insert(event_receiver);
 }
 
 void Map::removeEventReceiver(MapEventReceiver *event_receiver)
 {
-	if(m_event_receivers.find(event_receiver) == NULL)
-		return;
-	m_event_receivers.remove(event_receiver);
+	m_event_receivers.erase(event_receiver);
 }
 
 void Map::dispatchEvent(MapEditEvent *event)
 {
-	for(core::map<MapEventReceiver*, bool>::Iterator
-			i = m_event_receivers.getIterator();
-			i.atEnd()==false; i++)
+	for(std::set<MapEventReceiver*>::iterator
+			i = m_event_receivers.begin();
+			i != m_event_receivers.end(); ++i)
 	{
-		MapEventReceiver* event_receiver = i.getNode()->getKey();
-		event_receiver->onMapEditEvent(event);
+		(*i)->onMapEditEvent(event);
 	}
 }
 
@@ -111,12 +108,12 @@ MapSector * Map::getSectorNoGenerateNoExNoLock(v2s16 p)
 		return sector;
 	}
 
-	core::map<v2s16, MapSector*>::Node *n = m_sectors.find(p);
+	std::map<v2s16, MapSector*>::iterator n = m_sectors.find(p);
 
-	if(n == NULL)
+	if(n == m_sectors.end())
 		return NULL;
 
-	MapSector *sector = n->getValue();
+	MapSector *sector = n->second;
 
 	// Cache the last result
 	m_sector_cache_p = p;
@@ -236,9 +233,9 @@ void Map::setNode(v3s16 p, MapNode & n)
 	values of from_nodes are lighting values.
 */
 void Map::unspreadLight(enum LightBank bank,
-		core::map<v3s16, u8> & from_nodes,
-		core::map<v3s16, bool> & light_sources,
-		core::map<v3s16, MapBlock*>  & modified_blocks)
+		std::map<v3s16, u8> & from_nodes,
+		std::set<v3s16> & light_sources,
+		std::map<v3s16, MapBlock*>  & modified_blocks)
 {
 	INodeDefManager *nodemgr = m_gamedef->ndef();
 
@@ -256,9 +253,7 @@ void Map::unspreadLight(enum LightBank bank,
 
 	u32 blockchangecount = 0;
 
-	core::map<v3s16, u8> unlighted_nodes;
-	core::map<v3s16, u8>::Iterator j;
-	j = from_nodes.getIterator();
+	std::map<v3s16, u8> unlighted_nodes;
 
 	/*
 		Initialize block cache
@@ -268,9 +263,10 @@ void Map::unspreadLight(enum LightBank bank,
 	// Cache this a bit, too
 	bool block_checked_in_modified = false;
 
-	for(; j.atEnd() == false; j++)
+	for(std::map<v3s16, u8>::iterator j = from_nodes.begin();
+		j != from_nodes.end(); ++j)
 	{
-		v3s16 pos = j.getNode()->getKey();
+		v3s16 pos = j->first;
 		v3s16 blockpos = getNodeBlockPos(pos);
 
 		// Only fetch a new block if the block position has changed
@@ -297,7 +293,7 @@ void Map::unspreadLight(enum LightBank bank,
 		// Get node straight from the block
 		MapNode n = block->getNode(relpos);
 
-		u8 oldlight = j.getNode()->getValue();
+		u8 oldlight = j->second;
 
 		// Loop through 6 neighbors
 		for(u16 i=0; i<6; i++)
@@ -354,7 +350,7 @@ void Map::unspreadLight(enum LightBank bank,
 						n2.setLight(bank, 0, nodemgr);
 						block->setNode(relpos, n2);
 
-						unlighted_nodes.insert(n2pos, current_light);
+						unlighted_nodes[n2pos] = current_light;
 						changed = true;
 
 						/*
@@ -373,16 +369,16 @@ void Map::unspreadLight(enum LightBank bank,
 						light_sources.remove(n2pos);*/
 				}
 				else{
-					light_sources.insert(n2pos, true);
+					light_sources.insert(n2pos);
 				}
 
 				// Add to modified_blocks
 				if(changed == true && block_checked_in_modified == false)
 				{
 					// If the block is not found in modified_blocks, add.
-					if(modified_blocks.find(blockpos) == NULL)
+					if(modified_blocks.find(blockpos) == modified_blocks.end())
 					{
-						modified_blocks.insert(blockpos, block);
+						modified_blocks[blockpos] = block;
 					}
 					block_checked_in_modified = true;
 				}
@@ -408,11 +404,11 @@ void Map::unspreadLight(enum LightBank bank,
 */
 void Map::unLightNeighbors(enum LightBank bank,
 		v3s16 pos, u8 lightwas,
-		core::map<v3s16, bool> & light_sources,
-		core::map<v3s16, MapBlock*>  & modified_blocks)
+		std::set<v3s16> & light_sources,
+		std::map<v3s16, MapBlock*>  & modified_blocks)
 {
-	core::map<v3s16, u8> from_nodes;
-	from_nodes.insert(pos, lightwas);
+	std::map<v3s16, u8> from_nodes;
+	from_nodes[pos] = lightwas;
 
 	unspreadLight(bank, from_nodes, light_sources, modified_blocks);
 }
@@ -422,8 +418,8 @@ void Map::unLightNeighbors(enum LightBank bank,
 	goes on recursively.
 */
 void Map::spreadLight(enum LightBank bank,
-		core::map<v3s16, bool> & from_nodes,
-		core::map<v3s16, MapBlock*> & modified_blocks)
+		std::set<v3s16> & from_nodes,
+		std::map<v3s16, MapBlock*> & modified_blocks)
 {
 	INodeDefManager *nodemgr = m_gamedef->ndef();
 
@@ -441,9 +437,7 @@ void Map::spreadLight(enum LightBank bank,
 
 	u32 blockchangecount = 0;
 
-	core::map<v3s16, bool> lighted_nodes;
-	core::map<v3s16, bool>::Iterator j;
-	j = from_nodes.getIterator();
+	std::set<v3s16> lighted_nodes;
 
 	/*
 		Initialize block cache
@@ -453,12 +447,10 @@ void Map::spreadLight(enum LightBank bank,
 	// Cache this a bit, too
 	bool block_checked_in_modified = false;
 
-	for(; j.atEnd() == false; j++)
-	//for(; j != from_nodes.end(); j++)
+	for(std::set<v3s16>::iterator j = from_nodes.begin();
+		j != from_nodes.end(); ++j)
 	{
-		v3s16 pos = j.getNode()->getKey();
-		//v3s16 pos = *j;
-		//infostream<<"pos=("<<pos.X<<","<<pos.Y<<","<<pos.Z<<")"<<std::endl;
+		v3s16 pos = *j;
 		v3s16 blockpos = getNodeBlockPos(pos);
 
 		// Only fetch a new block if the block position has changed
@@ -525,8 +517,7 @@ void Map::spreadLight(enum LightBank bank,
 				*/
 				if(n2.getLight(bank, nodemgr) > undiminish_light(oldlight))
 				{
-					lighted_nodes.insert(n2pos, true);
-					//lighted_nodes.push_back(n2pos);
+					lighted_nodes.insert(n2pos);
 					changed = true;
 				}
 				/*
@@ -539,8 +530,7 @@ void Map::spreadLight(enum LightBank bank,
 					{
 						n2.setLight(bank, newlight, nodemgr);
 						block->setNode(relpos, n2);
-						lighted_nodes.insert(n2pos, true);
-						//lighted_nodes.push_back(n2pos);
+						lighted_nodes.insert(n2pos);
 						changed = true;
 					}
 				}
@@ -549,9 +539,9 @@ void Map::spreadLight(enum LightBank bank,
 				if(changed == true && block_checked_in_modified == false)
 				{
 					// If the block is not found in modified_blocks, add.
-					if(modified_blocks.find(blockpos) == NULL)
+					if(modified_blocks.find(blockpos) == modified_blocks.end())
 					{
-						modified_blocks.insert(blockpos, block);
+						modified_blocks[blockpos] = block;
 					}
 					block_checked_in_modified = true;
 				}
@@ -577,10 +567,10 @@ void Map::spreadLight(enum LightBank bank,
 */
 void Map::lightNeighbors(enum LightBank bank,
 		v3s16 pos,
-		core::map<v3s16, MapBlock*> & modified_blocks)
+		std::map<v3s16, MapBlock*> & modified_blocks)
 {
-	core::map<v3s16, bool> from_nodes;
-	from_nodes.insert(pos, true);
+	std::set<v3s16> from_nodes;
+	from_nodes.insert(pos);
 	spreadLight(bank, from_nodes, modified_blocks);
 }
 
@@ -635,7 +625,7 @@ v3s16 Map::getBrightestNeighbour(enum LightBank bank, v3s16 p)
 	Mud is turned into grass in where the sunlight stops.
 */
 s16 Map::propagateSunlight(v3s16 start,
-		core::map<v3s16, MapBlock*> & modified_blocks)
+		std::map<v3s16, MapBlock*> & modified_blocks)
 {
 	INodeDefManager *nodemgr = m_gamedef->ndef();
 
@@ -662,7 +652,7 @@ s16 Map::propagateSunlight(v3s16 start,
 			n.setLight(LIGHTBANK_DAY, LIGHT_SUN, nodemgr);
 			block->setNode(relpos, n);
 
-			modified_blocks.insert(blockpos, block);
+			modified_blocks[blockpos] = block;
 		}
 		else
 		{
@@ -674,8 +664,8 @@ s16 Map::propagateSunlight(v3s16 start,
 }
 
 void Map::updateLighting(enum LightBank bank,
-		core::map<v3s16, MapBlock*> & a_blocks,
-		core::map<v3s16, MapBlock*> & modified_blocks)
+		std::map<v3s16, MapBlock*> & a_blocks,
+		std::map<v3s16, MapBlock*> & modified_blocks)
 {
 	INodeDefManager *nodemgr = m_gamedef->ndef();
 
@@ -688,22 +678,21 @@ void Map::updateLighting(enum LightBank bank,
 	//bool debug=true;
 	//u32 count_was = modified_blocks.size();
 
-	core::map<v3s16, MapBlock*> blocks_to_update;
+	std::map<v3s16, MapBlock*> blocks_to_update;
 
-	core::map<v3s16, bool> light_sources;
+	std::set<v3s16> light_sources;
 
-	core::map<v3s16, u8> unlight_from;
+	std::map<v3s16, u8> unlight_from;
 
 	int num_bottom_invalid = 0;
 
 	{
 	//TimeTaker t("first stuff");
 
-	core::map<v3s16, MapBlock*>::Iterator i;
-	i = a_blocks.getIterator();
-	for(; i.atEnd() == false; i++)
+	for(std::map<v3s16, MapBlock*>::iterator i = a_blocks.begin();
+		i != a_blocks.end(); ++i)
 	{
-		MapBlock *block = i.getNode()->getValue();
+		MapBlock *block = i->second;
 
 		for(;;)
 		{
@@ -713,9 +702,8 @@ void Map::updateLighting(enum LightBank bank,
 
 			v3s16 pos = block->getPos();
 			v3s16 posnodes = block->getPosRelative();
-			modified_blocks.insert(pos, block);
-
-			blocks_to_update.insert(pos, block);
+			modified_blocks[pos] = block;
+			blocks_to_update[pos] = block;
 
 			/*
 				Clear all light from block
@@ -735,7 +723,7 @@ void Map::updateLighting(enum LightBank bank,
 					// If node sources light, add to list
 					u8 source = nodemgr->get(n).light_source;
 					if(source != 0)
-						light_sources[p + posnodes] = true;
+						light_sources.insert(p + posnodes);
 
 					// Collect borders for unlighting
 					if((x==0 || x == MAP_BLOCKSIZE-1
@@ -744,7 +732,7 @@ void Map::updateLighting(enum LightBank bank,
 					&& oldlight != 0)
 					{
 						v3s16 p_map = p + posnodes;
-						unlight_from.insert(p_map, oldlight);
+						unlight_from[p_map] = oldlight;
 					}
 				}
 				catch(InvalidPositionException &e)
@@ -912,8 +900,8 @@ void Map::updateLighting(enum LightBank bank,
 	//m_dout<<"Done ("<<getTimestamp()<<")"<<std::endl;
 }
 
-void Map::updateLighting(core::map<v3s16, MapBlock*> & a_blocks,
-		core::map<v3s16, MapBlock*> & modified_blocks)
+void Map::updateLighting(std::map<v3s16, MapBlock*> & a_blocks,
+		std::map<v3s16, MapBlock*> & modified_blocks)
 {
 	updateLighting(LIGHTBANK_DAY, a_blocks, modified_blocks);
 	updateLighting(LIGHTBANK_NIGHT, a_blocks, modified_blocks);
@@ -921,11 +909,11 @@ void Map::updateLighting(core::map<v3s16, MapBlock*> & a_blocks,
 	/*
 		Update information about whether day and night light differ
 	*/
-	for(core::map<v3s16, MapBlock*>::Iterator
-			i = modified_blocks.getIterator();
-			i.atEnd() == false; i++)
+	for(std::map<v3s16, MapBlock*>::iterator
+			i = modified_blocks.begin();
+			i != modified_blocks.end(); ++i)
 	{
-		MapBlock *block = i.getNode()->getValue();
+		MapBlock *block = i->second;
 		block->expireDayNightDiff();
 	}
 }
@@ -933,7 +921,7 @@ void Map::updateLighting(core::map<v3s16, MapBlock*> & a_blocks,
 /*
 */
 void Map::addNodeAndUpdate(v3s16 p, MapNode n,
-		core::map<v3s16, MapBlock*> &modified_blocks)
+		std::map<v3s16, MapBlock*> &modified_blocks)
 {
 	INodeDefManager *ndef = m_gamedef->ndef();
 
@@ -952,7 +940,7 @@ void Map::addNodeAndUpdate(v3s16 p, MapNode n,
 	v3s16 bottompos = p + v3s16(0,-1,0);
 
 	bool node_under_sunlight = true;
-	core::map<v3s16, bool> light_sources;
+	std::set<v3s16> light_sources;
 
 	/*
 		Collect old node for rollback
@@ -994,7 +982,7 @@ void Map::addNodeAndUpdate(v3s16 p, MapNode n,
 		v3s16 blockpos = getNodeBlockPos(p);
 		MapBlock * block = getBlockNoCreate(blockpos);
 		assert(block != NULL);
-		modified_blocks.insert(blockpos, block);
+		modified_blocks[blockpos] = block;
 
 		assert(isValidPosition(p));
 
@@ -1078,12 +1066,11 @@ void Map::addNodeAndUpdate(v3s16 p, MapNode n,
 	/*
 		Update information about whether day and night light differ
 	*/
-	for(core::map<v3s16, MapBlock*>::Iterator
-			i = modified_blocks.getIterator();
-			i.atEnd() == false; i++)
+	for(std::map<v3s16, MapBlock*>::iterator
+			i = modified_blocks.begin();
+			i != modified_blocks.end(); ++i)
 	{
-		MapBlock *block = i.getNode()->getValue();
-		block->expireDayNightDiff();
+		i->second->expireDayNightDiff();
 	}
 
 	/*
@@ -1132,7 +1119,7 @@ void Map::addNodeAndUpdate(v3s16 p, MapNode n,
 /*
 */
 void Map::removeNodeAndUpdate(v3s16 p,
-		core::map<v3s16, MapBlock*> &modified_blocks)
+		std::map<v3s16, MapBlock*> &modified_blocks)
 {
 	INodeDefManager *ndef = m_gamedef->ndef();
 
@@ -1166,7 +1153,7 @@ void Map::removeNodeAndUpdate(v3s16 p,
 	{
 	}
 
-	core::map<v3s16, bool> light_sources;
+	std::set<v3s16> light_sources;
 
 	enum LightBank banks[] =
 	{
@@ -1214,7 +1201,7 @@ void Map::removeNodeAndUpdate(v3s16 p,
 	v3s16 blockpos = getNodeBlockPos(p);
 	MapBlock * block = getBlockNoCreate(blockpos);
 	assert(block != NULL);
-	modified_blocks.insert(blockpos, block);
+	modified_blocks[blockpos] = block;
 
 	/*
 		If the removed node was under sunlight, propagate the
@@ -1270,12 +1257,11 @@ void Map::removeNodeAndUpdate(v3s16 p,
 	/*
 		Update information about whether day and night light differ
 	*/
-	for(core::map<v3s16, MapBlock*>::Iterator
-			i = modified_blocks.getIterator();
-			i.atEnd() == false; i++)
+	for(std::map<v3s16, MapBlock*>::iterator
+			i = modified_blocks.begin();
+			i != modified_blocks.end(); ++i)
 	{
-		MapBlock *block = i.getNode()->getValue();
-		block->expireDayNightDiff();
+		i->second->expireDayNightDiff();
 	}
 
 	/*
@@ -1330,15 +1316,15 @@ bool Map::addNodeWithEvent(v3s16 p, MapNode n)
 
 	bool succeeded = true;
 	try{
-		core::map<v3s16, MapBlock*> modified_blocks;
+		std::map<v3s16, MapBlock*> modified_blocks;
 		addNodeAndUpdate(p, n, modified_blocks);
 
 		// Copy modified_blocks to event
-		for(core::map<v3s16, MapBlock*>::Iterator
-				i = modified_blocks.getIterator();
-				i.atEnd()==false; i++)
+		for(std::map<v3s16, MapBlock*>::iterator
+				i = modified_blocks.begin();
+				i != modified_blocks.end(); ++i)
 		{
-			event.modified_blocks.insert(i.getNode()->getKey(), false);
+			event.modified_blocks.insert(i->first);
 		}
 	}
 	catch(InvalidPositionException &e){
@@ -1358,15 +1344,15 @@ bool Map::removeNodeWithEvent(v3s16 p)
 
 	bool succeeded = true;
 	try{
-		core::map<v3s16, MapBlock*> modified_blocks;
+		std::map<v3s16, MapBlock*> modified_blocks;
 		removeNodeAndUpdate(p, modified_blocks);
 
 		// Copy modified_blocks to event
-		for(core::map<v3s16, MapBlock*>::Iterator
-				i = modified_blocks.getIterator();
-				i.atEnd()==false; i++)
+		for(std::map<v3s16, MapBlock*>::iterator
+				i = modified_blocks.begin();
+				i != modified_blocks.end(); ++i)
 		{
-			event.modified_blocks.insert(i.getNode()->getKey(), false);
+			event.modified_blocks.insert(i->first);
 		}
 	}
 	catch(InvalidPositionException &e){
@@ -1439,33 +1425,31 @@ bool Map::getDayNightDiff(v3s16 blockpos)
 	Updates usage timers
 */
 void Map::timerUpdate(float dtime, float unload_timeout,
-		core::list<v3s16> *unloaded_blocks)
+		std::list<v3s16> *unloaded_blocks)
 {
 	bool save_before_unloading = (mapType() == MAPTYPE_SERVER);
 
 	// Profile modified reasons
 	Profiler modprofiler;
 
-	core::list<v2s16> sector_deletion_queue;
+	std::list<v2s16> sector_deletion_queue;
 	u32 deleted_blocks_count = 0;
 	u32 saved_blocks_count = 0;
 	u32 block_count_all = 0;
 
-	core::map<v2s16, MapSector*>::Iterator si;
-
 	beginSave();
-	si = m_sectors.getIterator();
-	for(; si.atEnd() == false; si++)
+	for(std::map<v2s16, MapSector*>::iterator si = m_sectors.begin();
+		si != m_sectors.end(); ++si)
 	{
-		MapSector *sector = si.getNode()->getValue();
+		MapSector *sector = si->second;
 
 		bool all_blocks_deleted = true;
 
-		core::list<MapBlock*> blocks;
+		std::list<MapBlock*> blocks;
 		sector->getBlocks(blocks);
 
-		for(core::list<MapBlock*>::Iterator i = blocks.begin();
-				i != blocks.end(); i++)
+		for(std::list<MapBlock*>::iterator i = blocks.begin();
+				i != blocks.end(); ++i)
 		{
 			MapBlock *block = (*i);
 
@@ -1501,7 +1485,7 @@ void Map::timerUpdate(float dtime, float unload_timeout,
 
 		if(all_blocks_deleted)
 		{
-			sector_deletion_queue.push_back(si.getNode()->getKey());
+			sector_deletion_queue.push_back(si->first);
 		}
 	}
 	endSave();
@@ -1526,17 +1510,17 @@ void Map::timerUpdate(float dtime, float unload_timeout,
 	}
 }
 
-void Map::deleteSectors(core::list<v2s16> &list)
+void Map::deleteSectors(std::list<v2s16> &list)
 {
-	core::list<v2s16>::Iterator j;
-	for(j=list.begin(); j!=list.end(); j++)
+	for(std::list<v2s16>::iterator j = list.begin();
+		j != list.end(); ++j)
 	{
 		MapSector *sector = m_sectors[*j];
 		// If sector is in sector cache, remove it from there
 		if(m_sector_cache == sector)
 			m_sector_cache = NULL;
 		// Remove from map and delete
-		m_sectors.remove(*j);
+		m_sectors.erase(*j);
 		delete sector;
 	}
 }
@@ -1642,7 +1626,7 @@ const v3s16 g_7dirs[7] =
 #define D_TOP 6
 #define D_SELF 1
 
-void Map::transformLiquidsFinite(core::map<v3s16, MapBlock*> & modified_blocks)
+void Map::transformLiquidsFinite(std::map<v3s16, MapBlock*> & modified_blocks)
 {
 	INodeDefManager *nodemgr = m_gamedef->ndef();
 
@@ -1656,19 +1640,16 @@ void Map::transformLiquidsFinite(core::map<v3s16, MapBlock*> & modified_blocks)
 	bool fast_flood = g_settings->getS16("liquid_fast_flood");
 	int water_level = g_settings->getS16("water_level");
 
-	/*if(initial_size != 0)
-		infostream<<"transformLiquids(): initial_size="<<initial_size<<std::endl;*/
-
 	// list of nodes that due to viscosity have not reached their max level height
 	UniqueQueue<v3s16> must_reflow, must_reflow_second;
 
 	// List of MapBlocks that will require a lighting update (due to lava)
-	core::map<v3s16, MapBlock*> lighting_modified_blocks;
+	std::map<v3s16, MapBlock*> lighting_modified_blocks;
 
-	while(m_transforming_liquid.size() > 0)
+	while (m_transforming_liquid.size() > 0)
 	{
 		// This should be done here so that it is done when continue is used
-		if(loopcount >= initial_size || loopcount >= 1000)
+		if (loopcount >= initial_size || loopcount >= 1000)
 			break;
 		loopcount++;
 		/*
@@ -1676,9 +1657,12 @@ void Map::transformLiquidsFinite(core::map<v3s16, MapBlock*> & modified_blocks)
 		*/
 		v3s16 p0 = m_transforming_liquid.pop_front();
 		u16 total_level = 0;
-		NodeNeighbor neighbors[7]; // surrounding flowing liquid nodes
-		s8 liquid_levels[7]      = {-1, -1, -1, -1, -1, -1, -1}; // current level of every block
-		s8 liquid_levels_want[7] = {-1, -1, -1, -1, -1, -1, -1}; // target levels
+		// surrounding flowing liquid nodes
+		NodeNeighbor neighbors[7]; 
+		// current level of every block
+		s8 liquid_levels[7] = {-1, -1, -1, -1, -1, -1, -1};
+		 // target levels
+		s8 liquid_levels_want[7] = {-1, -1, -1, -1, -1, -1, -1};
 		s8 can_liquid_same_level = 0;
 		content_t liquid_kind = CONTENT_IGNORE;
 		content_t liquid_kind_flowing = CONTENT_IGNORE;
@@ -1713,9 +1697,11 @@ void Map::transformLiquidsFinite(core::map<v3s16, MapBlock*> & modified_blocks)
 					}
 					break;
 				case LIQUID_SOURCE:
-					// if this node is not (yet) of a liquid type, choose the first liquid type we encounter
+					// if this node is not (yet) of a liquid type,
+					// choose the first liquid type we encounter
 					if (liquid_kind_flowing == CONTENT_IGNORE)
-						liquid_kind_flowing = nodemgr->getId(nodemgr->get(nb.n).liquid_alternative_flowing);
+						liquid_kind_flowing = nodemgr->getId(
+							nodemgr->get(nb.n).liquid_alternative_flowing);
 					if (liquid_kind == CONTENT_IGNORE)
 						liquid_kind = nb.n.getContent();
 					if (nb.n.getContent() == liquid_kind) {
@@ -1725,37 +1711,55 @@ void Map::transformLiquidsFinite(core::map<v3s16, MapBlock*> & modified_blocks)
 					}
 					break;
 				case LIQUID_FLOWING:
-					// if this node is not (yet) of a liquid type, choose the first liquid type we encounter
+					// if this node is not (yet) of a liquid type,
+					// choose the first liquid type we encounter
 					if (liquid_kind_flowing == CONTENT_IGNORE)
 						liquid_kind_flowing = nb.n.getContent();
 					if (liquid_kind == CONTENT_IGNORE)
-						liquid_kind = nodemgr->getId(nodemgr->get(nb.n).liquid_alternative_source);
+						liquid_kind = nodemgr->getId(
+							nodemgr->get(nb.n).liquid_alternative_source);
 					if (nb.n.getContent() == liquid_kind_flowing) {
 						liquid_levels[i] = (nb.n.param2 & LIQUID_LEVEL_MASK);
 						nb.l = 1;
 					}
 					break;
 			}
-			if (nb.l && nb.t == NEIGHBOR_SAME_LEVEL) ++can_liquid_same_level;
-			if (liquid_levels[i] > 0) total_level += liquid_levels[i];
+			
+			if (nb.l && nb.t == NEIGHBOR_SAME_LEVEL)
+				++can_liquid_same_level;
+			if (liquid_levels[i] > 0)
+				total_level += liquid_levels[i];
 
 			/*
-			infostream << "get node i=" <<(int)i<<" " << PP(npos) << " c="<<nb.n.getContent() <<" p0="<< (int)nb.n.param0 <<" p1="<< (int)nb.n.param1 <<" p2="<< (int)nb.n.param2 << " lt="<<nodemgr->get(nb.n.getContent()).liquid_type
+			infostream << "get node i=" <<(int)i<<" " << PP(npos) << " c="
+			<< nb.n.getContent() <<" p0="<< (int)nb.n.param0 <<" p1="
+			<< (int)nb.n.param1 <<" p2="<< (int)nb.n.param2 << " lt="
+			<< nodemgr->get(nb.n.getContent()).liquid_type
 			//<< " lk=" << liquid_kind << " lkf=" << liquid_kind_flowing
-			<< " l="<< nb.l	<< " inf="<< nb.i << " nlevel=" <<  (int)liquid_levels[i] << " tlevel=" << (int)total_level << " cansame="<<(int)can_liquid_same_level<<std::endl;
+			<< " l="<< nb.l	<< " inf="<< nb.i << " nlevel=" << (int)liquid_levels[i]
+			<< " tlevel=" << (int)total_level << " cansame="
+			<< (int)can_liquid_same_level << std::endl;
 			*/
 		}
 
-		if (liquid_kind == CONTENT_IGNORE || !neighbors[D_SELF].l || total_level <= 0)
+		if (liquid_kind == CONTENT_IGNORE ||
+			!neighbors[D_SELF].l ||
+			total_level <= 0)
 			continue;
 
 		// fill bottom block
 		if (neighbors[D_BOTTOM].l) {
-			liquid_levels_want[D_BOTTOM] = total_level > LIQUID_LEVEL_SOURCE ? LIQUID_LEVEL_SOURCE : total_level;
+			liquid_levels_want[D_BOTTOM] = total_level > LIQUID_LEVEL_SOURCE ?
+				LIQUID_LEVEL_SOURCE : total_level;
 			total_level -= liquid_levels_want[D_BOTTOM];
 		}
 
-		if (relax && p0.Y <= water_level && liquid_levels[D_TOP] == 0 && total_level >= LIQUID_LEVEL_SOURCE * can_liquid_same_level - can_liquid_same_level + 2 && can_liquid_same_level >= relax + 1) { //relax up
+		//relax up
+		if (relax && p0.Y <= water_level && liquid_levels[D_TOP] == 0 &&
+			liquid_levels[D_BOTTOM] == LIQUID_LEVEL_SOURCE &&
+			total_level >= LIQUID_LEVEL_SOURCE * can_liquid_same_level-
+			(can_liquid_same_level - relax) &&
+			can_liquid_same_level >= relax + 1) { 
 			total_level = LIQUID_LEVEL_SOURCE * can_liquid_same_level; 
 		}
 
@@ -1766,7 +1770,11 @@ void Map::transformLiquidsFinite(core::map<v3s16, MapBlock*> & modified_blocks)
 			: total_level / can_liquid_same_level;
 		total_level -= want_level * can_liquid_same_level;
 
-		if (relax && p0.Y > water_level && liquid_levels[D_TOP] == 0 && liquid_levels[D_BOTTOM] == LIQUID_LEVEL_SOURCE && want_level == 0 && total_level <= can_liquid_same_level - 2 && can_liquid_same_level >= relax + 1) { //relax down
+		//relax down
+		if (relax && p0.Y == water_level + 1 && liquid_levels[D_TOP] == 0 &&
+			liquid_levels[D_BOTTOM] == LIQUID_LEVEL_SOURCE && want_level == 0 &&
+			total_level <= (can_liquid_same_level - relax) &&
+			can_liquid_same_level >= relax + 1) {
 			total_level = 0;
 		}
 
@@ -1784,7 +1792,8 @@ void Map::transformLiquidsFinite(core::map<v3s16, MapBlock*> & modified_blocks)
 
 		for (u16 ii = 0; ii < 7; ++ii) {
 			if (total_level < 1) break;
-			if (liquid_levels_want[ii] >= 0 && liquid_levels_want[ii] < LIQUID_LEVEL_SOURCE) {
+			if (liquid_levels_want[ii] >= 0 &&
+				liquid_levels_want[ii] < LIQUID_LEVEL_SOURCE) {
 				++liquid_levels_want[ii];
 				--total_level;
 			}
@@ -1792,7 +1801,8 @@ void Map::transformLiquidsFinite(core::map<v3s16, MapBlock*> & modified_blocks)
 
 		// fill top block if can
 		if (neighbors[D_TOP].l) {
-			liquid_levels_want[D_TOP] = total_level > LIQUID_LEVEL_SOURCE ? LIQUID_LEVEL_SOURCE : total_level ;
+			liquid_levels_want[D_TOP] = total_level > LIQUID_LEVEL_SOURCE ?
+				LIQUID_LEVEL_SOURCE : total_level;
 			total_level -= liquid_levels_want[D_TOP];
 		}
 
@@ -1807,7 +1817,15 @@ void Map::transformLiquidsFinite(core::map<v3s16, MapBlock*> & modified_blocks)
 				   && liquid_levels[D_TOP] >= LIQUID_LEVEL_SOURCE))))
 				liquid_levels_want[ii] = LIQUID_LEVEL_SOURCE;
 
-		//if (total_level > 0 /*|| flowed != volume*/) infostream <<" AFTER level=" << (int)total_level /*<< " flowed="<<flowed<< " volume=" <<volume*/<< " wantsame="<<(int)want_level<< " top="<< (int)liquid_levels_want[D_TOP]<< " topwas="<< (int)liquid_levels[D_TOP]<< " bot="<< (int)liquid_levels_want[D_BOTTOM]<<std::endl;
+		/*
+		if (total_level > 0) //|| flowed != volume)
+			infostream <<" AFTER level=" << (int)total_level 
+			//<< " flowed="<<flowed<< " volume=" << volume
+			<< " wantsame="<<(int)want_level<< " top="
+			<< (int)liquid_levels_want[D_TOP]<< " topwas="
+			<< (int)liquid_levels[D_TOP]<< " bot="
+			<< (int)liquid_levels_want[D_BOTTOM]<<std::endl;
+		*/
 
 		u8 changed = 0;
 		for (u16 i = 0; i < 7; i++) {
@@ -1831,8 +1849,10 @@ void Map::transformLiquidsFinite(core::map<v3s16, MapBlock*> & modified_blocks)
 					new_node_level = liquid_levels[i] - 1;
 				else if (level_inc > 0)
 					new_node_level = liquid_levels[i] + 1;
-			} else
+			} else {
 				new_node_level = liquid_levels_want[i];
+			}
+			
 			if (new_node_level >= LIQUID_LEVEL_SOURCE)
 				new_node_content = liquid_kind;
 			else if (new_node_level > 0)
@@ -1841,25 +1861,30 @@ void Map::transformLiquidsFinite(core::map<v3s16, MapBlock*> & modified_blocks)
 				new_node_content = CONTENT_AIR;
 
 			// last level must flow down on stairs
-			if (liquid_levels_want[i] != liquid_levels[i] && liquid_levels[D_TOP] <= 0 && !neighbors[D_BOTTOM].l && new_node_level >= 1 && new_node_level <= 2) //maybe == 1 // 
+			if (liquid_levels_want[i] != liquid_levels[i] &&
+				liquid_levels[D_TOP] <= 0 && !neighbors[D_BOTTOM].l &&
+				new_node_level >= 1 && new_node_level <= 2) {
 				for (u16 ii = D_SELF + 1; ii < D_TOP; ++ii) { // only same level
-				if (!neighbors[ii].l)
-					continue;
-				must_reflow_second.push_back(p0 + dirs[ii]);
+					if (neighbors[ii].l)
+						must_reflow_second.push_back(p0 + dirs[ii]);
+				}
 			}
 
 			/*
-				check if anything has changed. if not, just continue with the next node.
+				check if anything has changed.
+				if not, just continue with the next node.
 			 */
 			if (
 				 new_node_content == n0.getContent() 
 				&& (nodemgr->get(n0.getContent()).liquid_type != LIQUID_FLOWING ||
 				 ((n0.param2 & LIQUID_LEVEL_MASK) == (u8)new_node_level 
-				 // &&((n0.param2 & LIQUID_FLOW_DOWN_MASK) == LIQUID_FLOW_DOWN_MASK)== flowing_down
+				 //&& ((n0.param2 & LIQUID_FLOW_DOWN_MASK) ==
+				 //LIQUID_FLOW_DOWN_MASK) == flowing_down
 				 ))
 				&&
 				 (nodemgr->get(n0.getContent()).liquid_type != LIQUID_SOURCE ||
-				 (((n0.param2 & LIQUID_INFINITY_MASK) == LIQUID_INFINITY_MASK) == neighbors[i].i
+				 (((n0.param2 & LIQUID_INFINITY_MASK) ==
+					LIQUID_INFINITY_MASK) == neighbors[i].i
 				 ))
 			   ) {
 				continue;
@@ -1876,7 +1901,12 @@ void Map::transformLiquidsFinite(core::map<v3s16, MapBlock*> & modified_blocks)
 				//n0.param2 = ~(LIQUID_LEVEL_MASK | LIQUID_FLOW_DOWN_MASK);
 				n0.param2 = (neighbors[i].i ? LIQUID_INFINITY_MASK : 0x00);
 			}
-			//infostream << "set node i=" <<(int)i<<" "<< PP(p0)<< " nc="<<new_node_content<< " p2="<<(int)n0.param2<< " nl="<<(int)new_node_level<<std::endl;
+			/*
+			infostream << "set node i=" <<(int)i<<" "<< PP(p0)<< " nc="
+			<<new_node_content<< " p2="<<(int)n0.param2<< " nl="
+			<<(int)new_node_level<<std::endl;
+			*/
+			
 			n0.setContent(new_node_content);
 			// Find out whether there is a suspect for this action
 			std::string suspect;
@@ -1886,7 +1916,8 @@ void Map::transformLiquidsFinite(core::map<v3s16, MapBlock*> & modified_blocks)
 
 			if(!suspect.empty()){
 				// Blame suspect
-				RollbackScopeActor rollback_scope(m_gamedef->rollback(), suspect, true);
+				RollbackScopeActor rollback_scope(m_gamedef->rollback(),
+													suspect, true);
 				// Get old node for rollback
 				RollbackNode rollback_oldnode(this, p0, m_gamedef);
 				// Set node
@@ -1904,20 +1935,25 @@ void Map::transformLiquidsFinite(core::map<v3s16, MapBlock*> & modified_blocks)
 			v3s16 blockpos = getNodeBlockPos(p0);
 			MapBlock *block = getBlockNoCreateNoEx(blockpos);
 			if(block != NULL) {
-				modified_blocks.insert(blockpos, block);
+				modified_blocks[blockpos] = block;
 				// If node emits light, MapBlock requires lighting update
 				if(nodemgr->get(n0).light_source != 0)
 					lighting_modified_blocks[block->getPos()] = block;
 			}
 			must_reflow.push_back(neighbors[i].p);
 		}
-		/* //for better relax
-		if (changed)  for (u16 ii = D_SELF + 1; ii < D_TOP; ++ii) { // only same level
+		/* //for better relax  only same level
+		if (changed)  for (u16 ii = D_SELF + 1; ii < D_TOP; ++ii) {
 			if (!neighbors[ii].l) continue;
 			must_reflow.push_back(p0 + dirs[ii]);
 		}*/
 	}
-	//if (loopcount) infostream<<"Map::transformLiquids(): loopcount="<<loopcount<<" reflow="<<must_reflow.size()<<" queue="<< m_transforming_liquid.size()<<std::endl;
+	/*
+	if (loopcount)
+		infostream<<"Map::transformLiquids(): loopcount="<<loopcount
+		<<" reflow="<<must_reflow.size()
+		<<" queue="<< m_transforming_liquid.size()<<std::endl;
+	*/
 	while (must_reflow.size() > 0)
 		m_transforming_liquid.push_back(must_reflow.pop_front());
 	while (must_reflow_second.size() > 0)
@@ -1925,11 +1961,12 @@ void Map::transformLiquidsFinite(core::map<v3s16, MapBlock*> & modified_blocks)
 	updateLighting(lighting_modified_blocks, modified_blocks);
 }
 
-void Map::transformLiquids(core::map<v3s16, MapBlock*> & modified_blocks)
+void Map::transformLiquids(std::map<v3s16, MapBlock*> & modified_blocks)
 {
 
-	if (g_settings->getBool("liquid_finite")) return Map::transformLiquidsFinite(modified_blocks);
-	
+	if (g_settings->getBool("liquid_finite"))
+		return Map::transformLiquidsFinite(modified_blocks);
+
 	INodeDefManager *nodemgr = m_gamedef->ndef();
 
 	DSTACK(__FUNCTION_NAME);
@@ -1945,7 +1982,7 @@ void Map::transformLiquids(core::map<v3s16, MapBlock*> & modified_blocks)
 	UniqueQueue<v3s16> must_reflow;
 
 	// List of MapBlocks that will require a lighting update (due to lava)
-	core::map<v3s16, MapBlock*> lighting_modified_blocks;
+	std::map<v3s16, MapBlock*> lighting_modified_blocks;
 
 	while(m_transforming_liquid.size() != 0)
 	{
@@ -2165,7 +2202,7 @@ void Map::transformLiquids(core::map<v3s16, MapBlock*> & modified_blocks)
 		v3s16 blockpos = getNodeBlockPos(p0);
 		MapBlock *block = getBlockNoCreateNoEx(blockpos);
 		if(block != NULL) {
-			modified_blocks.insert(blockpos, block);
+			modified_blocks[blockpos] =  block;
 			// If node emits light, MapBlock requires lighting update
 			if(nodemgr->get(n0).light_source != 0)
 				lighting_modified_blocks[block->getPos()] = block;
@@ -2460,19 +2497,15 @@ bool ServerMap::initBlockMake(BlockMakeData *data, v3s16 blockpos)
 	bool enable_mapgen_debug_info = m_emerge->mapgen_debug_info;
 	EMERGE_DBG_OUT("initBlockMake(): " PP(blockpos) " - " PP(blockpos));
 
-	//s16 chunksize = 3;
-	//v3s16 chunk_offset(-1,-1,-1);
-	//s16 chunksize = 4;
-	//v3s16 chunk_offset(-1,-1,-1);
-	s16 chunksize = 5;
-	v3s16 chunk_offset(-2,-2,-2);
+	s16 chunksize = m_mgparams->chunksize;
+	s16 coffset = -chunksize / 2;
+	v3s16 chunk_offset(coffset, coffset, coffset);
 	v3s16 blockpos_div = getContainerPos(blockpos - chunk_offset, chunksize);
 	v3s16 blockpos_min = blockpos_div * chunksize;
 	v3s16 blockpos_max = blockpos_div * chunksize + v3s16(1,1,1)*(chunksize-1);
 	blockpos_min += chunk_offset;
 	blockpos_max += chunk_offset;
 
-	//v3s16 extra_borders(1,1,1);
 	v3s16 extra_borders(1,1,1);
 
 	// Do nothing if not inside limits (+-1 because of neighbors)
@@ -2571,7 +2604,7 @@ bool ServerMap::initBlockMake(BlockMakeData *data, v3s16 blockpos)
 }
 
 MapBlock* ServerMap::finishBlockMake(BlockMakeData *data,
-		core::map<v3s16, MapBlock*> &changed_blocks)
+		std::map<v3s16, MapBlock*> &changed_blocks)
 {
 	v3s16 blockpos_min = data->blockpos_min;
 	v3s16 blockpos_max = data->blockpos_max;
@@ -2676,10 +2709,10 @@ MapBlock* ServerMap::finishBlockMake(BlockMakeData *data,
 	/*
 		Go through changed blocks
 	*/
-	for(core::map<v3s16, MapBlock*>::Iterator i = changed_blocks.getIterator();
-			i.atEnd() == false; i++)
+	for(std::map<v3s16, MapBlock*>::iterator i = changed_blocks.begin();
+			i != changed_blocks.end(); ++i)
 	{
-		MapBlock *block = i.getNode()->getValue();
+		MapBlock *block = i->second;
 		assert(block);
 		/*
 			Update day/night difference cache of the MapBlocks
@@ -2797,7 +2830,7 @@ ServerMapSector * ServerMap::createSector(v2s16 p2d)
 	/*
 		Insert to container
 	*/
-	m_sectors.insert(p2d, sector);
+	m_sectors[p2d] = sector;
 
 	return sector;
 }
@@ -2808,7 +2841,7 @@ ServerMapSector * ServerMap::createSector(v2s16 p2d)
 */
 MapBlock * ServerMap::generateBlock(
 		v3s16 p,
-		core::map<v3s16, MapBlock*> &modified_blocks
+		std::map<v3s16, MapBlock*> &modified_blocks
 )
 {
 	DSTACKF("%s: p=(%d,%d,%d)", __FUNCTION_NAME, p.X, p.Y, p.Z);
@@ -3008,7 +3041,7 @@ MapBlock * ServerMap::emergeBlock(v3s16 p, bool create_blank)
 	}
 	/*if(allow_generate)
 	{
-		core::map<v3s16, MapBlock*> modified_blocks;
+		std::map<v3s16, MapBlock*> modified_blocks;
 		MapBlock *block = generateBlock(p, modified_blocks);
 		if(block)
 		{
@@ -3017,11 +3050,11 @@ MapBlock * ServerMap::emergeBlock(v3s16 p, bool create_blank)
 			event.p = p;
 
 			// Copy modified_blocks to event
-			for(core::map<v3s16, MapBlock*>::Iterator
-					i = modified_blocks.getIterator();
-					i.atEnd()==false; i++)
+			for(std::map<v3s16, MapBlock*>::iterator
+					i = modified_blocks.begin();
+					i != modified_blocks.end(); ++i)
 			{
-				event.modified_blocks.insert(i.getNode()->getKey(), false);
+				event.modified_blocks.insert(i->first);
 			}
 
 			// Queue event
@@ -3262,10 +3295,10 @@ void ServerMap::save(ModifiedState save_level)
 	// Don't do anything with sqlite unless something is really saved
 	bool save_started = false;
 
-	core::map<v2s16, MapSector*>::Iterator i = m_sectors.getIterator();
-	for(; i.atEnd() == false; i++)
+	for(std::map<v2s16, MapSector*>::iterator i = m_sectors.begin();
+		i != m_sectors.end(); ++i)
 	{
-		ServerMapSector *sector = (ServerMapSector*)i.getNode()->getValue();
+		ServerMapSector *sector = (ServerMapSector*)i->second;
 		assert(sector->getId() == MAPSECTOR_SERVER);
 
 		if(sector->differs_from_disk || save_level == MOD_STATE_CLEAN)
@@ -3273,11 +3306,11 @@ void ServerMap::save(ModifiedState save_level)
 			saveSectorMeta(sector);
 			sector_meta_count++;
 		}
-		core::list<MapBlock*> blocks;
+		std::list<MapBlock*> blocks;
 		sector->getBlocks(blocks);
-		core::list<MapBlock*>::Iterator j;
 
-		for(j=blocks.begin(); j!=blocks.end(); j++)
+		for(std::list<MapBlock*>::iterator j = blocks.begin();
+			j != blocks.end(); ++j)
 		{
 			MapBlock *block = *j;
 
@@ -3350,7 +3383,7 @@ v3s16 ServerMap::getIntegerAsBlock(sqlite3_int64 i)
 	return v3s16(x,y,z);
 }
 
-void ServerMap::listAllLoadableBlocks(core::list<v3s16> &dst)
+void ServerMap::listAllLoadableBlocks(std::list<v3s16> &dst)
 {
 	if(loadFromFolders()){
 		errorstream<<"Map::listAllLoadableBlocks(): Result will be missing "
@@ -3487,7 +3520,7 @@ MapSector* ServerMap::loadSectorMeta(std::string sectordir, bool save_after_load
 					<<" Continuing with a sector with no metadata."
 					<<std::endl;*/
 			sector = new ServerMapSector(this, p2d, m_gamedef);
-			m_sectors.insert(p2d, sector);
+			m_sectors[p2d] = sector;
 		}
 		else
 		{
@@ -3987,9 +4020,9 @@ void MapVoxelManipulator::emerge(VoxelArea a, s32 caller_id)
 		u8 flags = 0;
 		MapBlock *block;
 		v3s16 p(x,y,z);
-		core::map<v3s16, u8>::Node *n;
+		std::map<v3s16, u8>::iterator n;
 		n = m_loaded_blocks.find(p);
-		if(n != NULL)
+		if(n != m_loaded_blocks.end())
 			continue;
 
 		bool block_data_inexistent = false;
@@ -4017,7 +4050,7 @@ void MapVoxelManipulator::emerge(VoxelArea a, s32 caller_id)
 		if(block_data_inexistent)
 		{
 			flags |= VMANIP_BLOCK_DATA_INEXIST;
-			
+
 			VoxelArea a(p*MAP_BLOCKSIZE, (p+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
 			// Fill with VOXELFLAG_INEXISTENT
 			for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++)
@@ -4033,7 +4066,7 @@ void MapVoxelManipulator::emerge(VoxelArea a, s32 caller_id)
 			flags |= VMANIP_BLOCK_CONTAINS_CIGNORE;
 		}*/
 
-		m_loaded_blocks.insert(p, flags);
+		m_loaded_blocks[p] = flags;
 	}
 
 	//infostream<<"emerge done"<<std::endl;
@@ -4045,7 +4078,7 @@ void MapVoxelManipulator::emerge(VoxelArea a, s32 caller_id)
 		  run on background.
 */
 void MapVoxelManipulator::blitBack
-		(core::map<v3s16, MapBlock*> & modified_blocks)
+		(std::map<v3s16, MapBlock*> & modified_blocks)
 {
 	if(m_area.getExtent() == v3s16(0,0,0))
 		return;
@@ -4156,9 +4189,9 @@ void ManualMapVoxelManipulator::initialEmerge(
 		u8 flags = 0;
 		MapBlock *block;
 		v3s16 p(x,y,z);
-		core::map<v3s16, u8>::Node *n;
+		std::map<v3s16, u8>::iterator n;
 		n = m_loaded_blocks.find(p);
-		if(n != NULL)
+		if(n != m_loaded_blocks.end())
 			continue;
 
 		bool block_data_inexistent = false;
@@ -4199,12 +4232,12 @@ void ManualMapVoxelManipulator::initialEmerge(
 			flags |= VMANIP_BLOCK_CONTAINS_CIGNORE;
 		}*/
 
-		m_loaded_blocks.insert(p, flags);
+		m_loaded_blocks[p] = flags;
 	}
 }
 
 void ManualMapVoxelManipulator::blitBackAll(
-		core::map<v3s16, MapBlock*> * modified_blocks)
+		std::map<v3s16, MapBlock*> * modified_blocks)
 {
 	if(m_area.getExtent() == v3s16(0,0,0))
 		return;
@@ -4212,37 +4245,22 @@ void ManualMapVoxelManipulator::blitBackAll(
 	/*
 		Copy data of all blocks
 	*/
-	for(core::map<v3s16, u8>::Iterator
-			i = m_loaded_blocks.getIterator();
-			i.atEnd() == false; i++)
+	for(std::map<v3s16, u8>::iterator
+			i = m_loaded_blocks.begin();
+			i != m_loaded_blocks.end(); ++i)
 	{
-		v3s16 p = i.getNode()->getKey();
-		u8 flags = i.getNode()->getValue();
-		
-		bool existed = !(flags & VMANIP_BLOCK_DATA_INEXIST);
-		if(existed == false)
-		{
-			// The Great Bug was found using this
-			/*infostream<<"ManualMapVoxelManipulator::blitBackAll: "
-					<<"Inexistent ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
-					<<std::endl;*/
-			continue;
-		}
-		
+		v3s16 p = i->first;
 		MapBlock *block = m_map->getBlockNoCreateNoEx(p);
-		if(block == NULL)
+		bool existed = !(i->second & VMANIP_BLOCK_DATA_INEXIST);
+		if(existed == false)
 		{
-			infostream<<"WARNING: "<<__FUNCTION_NAME
-					<<": got NULL block "
-					<<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
-					<<std::endl;
 			continue;
 		}
 
 		block->copyFrom(*this);
-		
+
 		if(modified_blocks)
-			modified_blocks->insert(p, block);
+			(*modified_blocks)[p] = block;
 	}
 }
 
diff --git a/src/map.h b/src/map.h
index d356da2d1c65f00ce6fb39019a21af2d39896465..31001e4c397b9342a430671bf2e85d4f668b00a6 100644
--- a/src/map.h
+++ b/src/map.h
@@ -25,6 +25,9 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include <jthread.h>
 #include <iostream>
 #include <sstream>
+#include <set>
+#include <map>
+#include <list>
 
 #include "irrlichttypes_bloated.h"
 #include "mapnode.h"
@@ -47,7 +50,7 @@ class NodeMetadata;
 class IGameDef;
 class IRollbackReportSink;
 class EmergeManager;
-class BlockMakeData;
+struct BlockMakeData;
 
 
 /*
@@ -75,7 +78,7 @@ struct MapEditEvent
 	MapEditEventType type;
 	v3s16 p;
 	MapNode n;
-	core::map<v3s16, bool> modified_blocks;
+	std::set<v3s16> modified_blocks;
 	u16 already_known_by_peer;
 
 	MapEditEvent():
@@ -90,14 +93,7 @@ struct MapEditEvent
 		event->type = type;
 		event->p = p;
 		event->n = n;
-		for(core::map<v3s16, bool>::Iterator
-				i = modified_blocks.getIterator();
-				i.atEnd()==false; i++)
-		{
-			v3s16 p = i.getNode()->getKey();
-			bool v = i.getNode()->getValue();
-			event->modified_blocks.insert(p, v);
-		}
+		event->modified_blocks = modified_blocks;
 		return event;
 	}
 
@@ -117,11 +113,11 @@ struct MapEditEvent
 		case MEET_OTHER:
 		{
 			VoxelArea a;
-			for(core::map<v3s16, bool>::Iterator
-					i = modified_blocks.getIterator();
-					i.atEnd()==false; i++)
+			for(std::set<v3s16>::iterator
+					i = modified_blocks.begin();
+					i != modified_blocks.end(); ++i)
 			{
-				v3s16 p = i.getNode()->getKey();
+				v3s16 p = *i;
 				v3s16 np1 = p*MAP_BLOCKSIZE;
 				v3s16 np2 = np1 + v3s16(1,1,1)*MAP_BLOCKSIZE - v3s16(1,1,1);
 				a.addPoint(np1);
@@ -186,7 +182,7 @@ class Map /*: public NodeContainer*/
 	*/
 	virtual MapSector * emergeSector(v2s16 p){ return NULL; }
 	virtual MapSector * emergeSector(v2s16 p,
-			core::map<v3s16, MapBlock*> &changed_blocks){ return NULL; }
+			std::map<v3s16, MapBlock*> &changed_blocks){ return NULL; }
 
 	// Returns InvalidPositionException if not found
 	MapBlock * getBlockNoCreate(v3s16 p);
@@ -212,42 +208,42 @@ class Map /*: public NodeContainer*/
 	MapNode getNodeNoEx(v3s16 p);
 
 	void unspreadLight(enum LightBank bank,
-			core::map<v3s16, u8> & from_nodes,
-			core::map<v3s16, bool> & light_sources,
-			core::map<v3s16, MapBlock*> & modified_blocks);
+			std::map<v3s16, u8> & from_nodes,
+			std::set<v3s16> & light_sources,
+			std::map<v3s16, MapBlock*> & modified_blocks);
 
 	void unLightNeighbors(enum LightBank bank,
 			v3s16 pos, u8 lightwas,
-			core::map<v3s16, bool> & light_sources,
-			core::map<v3s16, MapBlock*> & modified_blocks);
+			std::set<v3s16> & light_sources,
+			std::map<v3s16, MapBlock*> & modified_blocks);
 
 	void spreadLight(enum LightBank bank,
-			core::map<v3s16, bool> & from_nodes,
-			core::map<v3s16, MapBlock*> & modified_blocks);
+			std::set<v3s16> & from_nodes,
+			std::map<v3s16, MapBlock*> & modified_blocks);
 
 	void lightNeighbors(enum LightBank bank,
 			v3s16 pos,
-			core::map<v3s16, MapBlock*> & modified_blocks);
+			std::map<v3s16, MapBlock*> & modified_blocks);
 
 	v3s16 getBrightestNeighbour(enum LightBank bank, v3s16 p);
 
 	s16 propagateSunlight(v3s16 start,
-			core::map<v3s16, MapBlock*> & modified_blocks);
+			std::map<v3s16, MapBlock*> & modified_blocks);
 
 	void updateLighting(enum LightBank bank,
-			core::map<v3s16, MapBlock*>  & a_blocks,
-			core::map<v3s16, MapBlock*> & modified_blocks);
+			std::map<v3s16, MapBlock*>  & a_blocks,
+			std::map<v3s16, MapBlock*> & modified_blocks);
 
-	void updateLighting(core::map<v3s16, MapBlock*>  & a_blocks,
-			core::map<v3s16, MapBlock*> & modified_blocks);
+	void updateLighting(std::map<v3s16, MapBlock*>  & a_blocks,
+			std::map<v3s16, MapBlock*> & modified_blocks);
 
 	/*
 		These handle lighting but not faces.
 	*/
 	void addNodeAndUpdate(v3s16 p, MapNode n,
-			core::map<v3s16, MapBlock*> &modified_blocks);
+			std::map<v3s16, MapBlock*> &modified_blocks);
 	void removeNodeAndUpdate(v3s16 p,
-			core::map<v3s16, MapBlock*> &modified_blocks);
+			std::map<v3s16, MapBlock*> &modified_blocks);
 
 	/*
 		Wrappers for the latter ones.
@@ -281,12 +277,12 @@ class Map /*: public NodeContainer*/
 		Saves modified blocks before unloading on MAPTYPE_SERVER.
 	*/
 	void timerUpdate(float dtime, float unload_timeout,
-			core::list<v3s16> *unloaded_blocks=NULL);
+			std::list<v3s16> *unloaded_blocks=NULL);
 
 	// Deletes sectors and their blocks from memory
 	// Takes cache into account
 	// If deleted sector is in sector cache, clears cache
-	void deleteSectors(core::list<v2s16> &list);
+	void deleteSectors(std::list<v2s16> &list);
 
 #if 0
 	/*
@@ -301,8 +297,8 @@ class Map /*: public NodeContainer*/
 	// For debug printing. Prints "Map: ", "ServerMap: " or "ClientMap: "
 	virtual void PrintInfo(std::ostream &out);
 
-	void transformLiquids(core::map<v3s16, MapBlock*> & modified_blocks);
-	void transformLiquidsFinite(core::map<v3s16, MapBlock*> & modified_blocks);
+	void transformLiquids(std::map<v3s16, MapBlock*> & modified_blocks);
+	void transformLiquidsFinite(std::map<v3s16, MapBlock*> & modified_blocks);
 
 	/*
 		Node metadata
@@ -325,7 +321,7 @@ class Map /*: public NodeContainer*/
 	/*
 		Misc.
 	*/
-	core::map<v2s16, MapSector*> *getSectorsPtr(){return &m_sectors;}
+	std::map<v2s16, MapSector*> *getSectorsPtr(){return &m_sectors;}
 
 	/*
 		Variables
@@ -340,9 +336,9 @@ class Map /*: public NodeContainer*/
 
 	IGameDef *m_gamedef;
 
-	core::map<MapEventReceiver*, bool> m_event_receivers;
+	std::set<MapEventReceiver*> m_event_receivers;
 
-	core::map<v2s16, MapSector*> m_sectors;
+	std::map<v2s16, MapSector*> m_sectors;
 
 	// Be sure to set this to NULL when the cached sector is deleted
 	MapSector *m_sector_cache;
@@ -385,13 +381,7 @@ class ServerMap : public Map
 	*/
 	bool initBlockMake(BlockMakeData *data, v3s16 blockpos);
 	MapBlock *finishBlockMake(BlockMakeData *data,
-			core::map<v3s16, MapBlock*> &changed_blocks);
-
-	// A non-threaded wrapper to the above  - DEFUNCT
-/*	MapBlock * generateBlock(
-			v3s16 p,
-			core::map<v3s16, MapBlock*> &modified_blocks
-	);*/
+			std::map<v3s16, MapBlock*> &changed_blocks);
 
 	/*
 		Get a block from somewhere.
@@ -444,9 +434,7 @@ class ServerMap : public Map
 
 	void save(ModifiedState save_level);
 	//void loadAll();
-
-	void listAllLoadableBlocks(core::list<v3s16> &dst);
-
+	void listAllLoadableBlocks(std::list<v3s16> &dst);
 	// Saves map seed and possibly other stuff
 	void saveMapMeta();
 	void loadMapMeta();
@@ -538,15 +526,15 @@ class MapVoxelManipulator : public VoxelManipulator
 
 	virtual void emerge(VoxelArea a, s32 caller_id=-1);
 
-	void blitBack(core::map<v3s16, MapBlock*> & modified_blocks);
-	
+	void blitBack(std::map<v3s16, MapBlock*> & modified_blocks);
+
+protected:
+	Map *m_map;
 	/*
 		key = blockpos
 		value = flags describing the block
 	*/
-	core::map<v3s16, u8> m_loaded_blocks;
-protected:
-	Map *m_map;
+	std::map<v3s16, u8> m_loaded_blocks;
 };
 
 class ManualMapVoxelManipulator : public MapVoxelManipulator
@@ -563,7 +551,7 @@ class ManualMapVoxelManipulator : public MapVoxelManipulator
 	void initialEmerge(v3s16 blockpos_min, v3s16 blockpos_max);
 
 	// This is much faster with big chunks of generated data
-	void blitBackAll(core::map<v3s16, MapBlock*> * modified_blocks);
+	void blitBackAll(std::map<v3s16, MapBlock*> * modified_blocks);
 
 protected:
 	bool m_create_area;
diff --git a/src/mapblock.cpp b/src/mapblock.cpp
index a6e9b3951f9d8103bbc53176d2f99d39b547e3d0..dd95ab77f8c3f60c93542adee12e3e2f056ddcb9 100644
--- a/src/mapblock.cpp
+++ b/src/mapblock.cpp
@@ -168,7 +168,7 @@ MapNode MapBlock::getNodeParentNoEx(v3s16 p)
 	if black_air_left!=NULL, it is set to true if non-sunlighted
 	air is left in block.
 */
-bool MapBlock::propagateSunlight(core::map<v3s16, bool> & light_sources,
+bool MapBlock::propagateSunlight(std::set<v3s16> & light_sources,
 		bool remove_light, bool *black_air_left)
 {
 	INodeDefManager *nodemgr = m_gamedef->ndef();
@@ -287,7 +287,7 @@ bool MapBlock::propagateSunlight(core::map<v3s16, bool> & light_sources,
 				
 				if(diminish_light(current_light) != 0)
 				{
-					light_sources.insert(pos_relative + pos, true);
+					light_sources.insert(pos_relative + pos);
 				}
 
 				if(current_light == 0 && stopped_to_solid_object)
diff --git a/src/mapblock.h b/src/mapblock.h
index 692b54318a4e17163b4c2c146415fd853086f671..05bb944a68727b7dcafbbb051d63575168cc5adc 100644
--- a/src/mapblock.h
+++ b/src/mapblock.h
@@ -23,6 +23,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include <jmutex.h>
 #include <jmutexautolock.h>
 #include <exception>
+#include <set>
 #include "debug.h"
 #include "irrlichttypes.h"
 #include "irr_v3d.h"
@@ -352,7 +353,7 @@ class MapBlock /*: public NodeContainer*/
 	}
 
 	// See comments in mapblock.cpp
-	bool propagateSunlight(core::map<v3s16, bool> & light_sources,
+	bool propagateSunlight(std::set<v3s16> & light_sources,
 			bool remove_light=false, bool *black_air_left=NULL);
 	
 	// Copies data to VoxelManipulator to getPosRelative()
diff --git a/src/mapblock_mesh.cpp b/src/mapblock_mesh.cpp
index f4d57922a52b0de37e8ce84c8e1276b6f5c96b36..f68a79e411c77f88ee5a41c9788e7af3dad31052 100644
--- a/src/mapblock_mesh.cpp
+++ b/src/mapblock_mesh.cpp
@@ -445,7 +445,7 @@ struct FastFace
 };
 
 static void makeFastFace(TileSpec tile, u16 li0, u16 li1, u16 li2, u16 li3,
-		v3f p, v3s16 dir, v3f scale, u8 light_source, core::array<FastFace> &dest)
+		v3f p, v3s16 dir, v3f scale, u8 light_source, std::vector<FastFace> &dest)
 {
 	FastFace face;
 	
@@ -455,6 +455,80 @@ static void makeFastFace(TileSpec tile, u16 li0, u16 li1, u16 li2, u16 li3,
 	v3f vertex_pos[4];
 	v3s16 vertex_dirs[4];
 	getNodeVertexDirs(dir, vertex_dirs);
+	v3s16 t;
+	switch (tile.rotation)
+	{
+	case 0:
+		break;
+	case 1: //R90
+		t = vertex_dirs[0];
+		vertex_dirs[0] = vertex_dirs[3];
+		vertex_dirs[3] = vertex_dirs[2];
+		vertex_dirs[2] = vertex_dirs[1];
+		vertex_dirs[1] = t;
+		break;
+	case 2: //R180
+		t = vertex_dirs[0];
+		vertex_dirs[0] = vertex_dirs[2];
+		vertex_dirs[2] = t;
+		t = vertex_dirs[1];
+		vertex_dirs[1] = vertex_dirs[3];
+		vertex_dirs[3] = t;
+		break;
+	case 3: //R270
+		t = vertex_dirs[0];
+		vertex_dirs[0] = vertex_dirs[1];
+		vertex_dirs[1] = vertex_dirs[2];
+		vertex_dirs[2] = vertex_dirs[3];
+		vertex_dirs[3] = t;
+		break;
+	case 4: //FXR90
+		t = vertex_dirs[0];
+		vertex_dirs[0] = vertex_dirs[3];
+		vertex_dirs[3] = vertex_dirs[2];
+		vertex_dirs[2] = vertex_dirs[1];
+		vertex_dirs[1] = t;
+		tile.texture.pos.Y += tile.texture.size.Y;
+		tile.texture.size.Y *= -1;
+		break;
+	case 5: //FXR270
+		t = vertex_dirs[0];
+		vertex_dirs[0] = vertex_dirs[1];
+		vertex_dirs[1] = vertex_dirs[2];
+		vertex_dirs[2] = vertex_dirs[3];
+		vertex_dirs[3] = t;
+		tile.texture.pos.Y += tile.texture.size.Y;
+		tile.texture.size.Y *= -1;
+		break;
+	case 6: //FYR90
+		t = vertex_dirs[0];
+		vertex_dirs[0] = vertex_dirs[3];
+		vertex_dirs[3] = vertex_dirs[2];
+		vertex_dirs[2] = vertex_dirs[1];
+		vertex_dirs[1] = t;
+		tile.texture.pos.X += tile.texture.size.X;
+		tile.texture.size.X *= -1;
+		break;
+	case 7: //FYR270
+		t = vertex_dirs[0];
+		vertex_dirs[0] = vertex_dirs[1];
+		vertex_dirs[1] = vertex_dirs[2];
+		vertex_dirs[2] = vertex_dirs[3];
+		vertex_dirs[3] = t;
+		tile.texture.pos.X += tile.texture.size.X;
+		tile.texture.size.X *= -1;
+		break;
+	case 8: //FX
+		tile.texture.pos.Y += tile.texture.size.Y;
+		tile.texture.size.Y *= -1;
+		break;
+	case 9: //FY
+		tile.texture.pos.X += tile.texture.size.X;
+		tile.texture.size.X *= -1;
+		break;
+	default:
+		break;
+	}
 	for(u16 i=0; i<4; i++)
 	{
 		vertex_pos[i] = v3f(
@@ -601,60 +675,50 @@ TileSpec getNodeTile(MapNode mn, v3s16 p, v3s16 dir, MeshMakeData *data)
 	//  5 = (0,0,-1)
 	//  6 = (0,-1,0)
 	//  7 = (-1,0,0)
-	u8 dir_i = (dir.X + 2 * dir.Y + 3 * dir.Z) & 7;
+	u8 dir_i = ((dir.X + 2 * dir.Y + 3 * dir.Z) & 7)*2;
 
 	// Get rotation for things like chests
 	u8 facedir = mn.getFaceDir(ndef);
-	assert(facedir <= 3);
-	
-	static const u8 dir_to_tile[4 * 8] =
+	assert(facedir <= 23);
+	static const u16 dir_to_tile[24 * 16] =
 	{
-		// 0  +X  +Y  +Z   0  -Z  -Y  -X
-		   0,  2,  0,  4,  0,  5,  1,  3,  // facedir = 0
-		   0,  4,  0,  3,  0,  2,  1,  5,  // facedir = 1
-		   0,  3,  0,  5,  0,  4,  1,  2,  // facedir = 2
-		   0,  5,  0,  2,  0,  3,  1,  4,  // facedir = 3
+		// 0     +X    +Y    +Z           -Z    -Y    -X   ->   value=tile,rotation  
+		   0,0,  2,0 , 0,0 , 4,0 ,  0,0,  5,0 , 1,0 , 3,0 ,  // rotate around y+ 0 - 3
+		   0,0,  4,0 , 0,3 , 3,0 ,  0,0,  2,0 , 1,1 , 5,0 ,
+		   0,0,  3,0 , 0,2 , 5,0 ,  0,0,  4,0 , 1,2 , 2,0 ,
+		   0,0,  5,0 , 0,1 , 2,0 ,  0,0,  3,0 , 1,3 , 4,0 ,
+
+		   0,0,  2,3 , 5,0 , 0,2 ,  0,0,  1,0 , 4,2 , 3,1 ,  // rotate around z+ 4 - 7
+		   0,0,  4,3 , 2,0 , 0,3 ,  0,0,  1,1 , 3,2 , 5,1 ,
+		   0,0,  3,3 , 4,0 , 0,0 ,  0,0,  1,2 , 5,2 , 2,1 ,
+		   0,0,  5,3 , 3,0 , 0,1 ,  0,0,  1,3 , 2,2 , 4,1 ,
+
+		   0,0,  2,1 , 4,2 , 1,2 ,  0,0,  0,0 , 5,0 , 3,3 ,  // rotate around z- 8 - 11
+		   0,0,  4,1 , 3,2 , 1,3 ,  0,0,  0,3 , 2,0 , 5,3 ,
+		   0,0,  3,1 , 5,2 , 1,0 ,  0,0,  0,2 , 4,0 , 2,3 ,
+		   0,0,  5,1 , 2,2 , 1,1 ,  0,0,  0,1 , 3,0 , 4,3 ,
+
+		   0,0,  0,3 , 3,3 , 4,1 ,  0,0,  5,3 , 2,3 , 1,3 ,  // rotate around x+ 12 - 15
+		   0,0,  0,2 , 5,3 , 3,1 ,  0,0,  2,3 , 4,3 , 1,0 ,
+		   0,0,  0,1 , 2,3 , 5,1 ,  0,0,  4,3 , 3,3 , 1,1 ,
+		   0,0,  0,0 , 4,3 , 2,1 ,  0,0,  3,3 , 5,3 , 1,2 ,
+		   
+		   0,0,  1,1 , 2,1 , 4,3 ,  0,0,  5,1 , 3,1 , 0,1 ,  // rotate around x- 16 - 19  
+		   0,0,  1,2 , 4,1 , 3,3 ,  0,0,  2,1 , 5,1 , 0,0 ,
+		   0,0,  1,3 , 3,1 , 5,3 ,  0,0,  4,1 , 2,1 , 0,3 ,  
+		   0,0,  1,0 , 5,1 , 2,3 ,  0,0,  3,1 , 4,1 , 0,2 ,  
+		
+		   0,0,  3,2 , 1,2 , 4,2 ,  0,0,  5,2 , 0,2 , 2,2 ,  // rotate around y- 20 - 23
+		   0,0,  5,2 , 1,3 , 3,2 ,  0,0,  2,2 , 0,1 , 4,2 ,  
+		   0,0,  2,2 , 1,0 , 5,2 ,  0,0,  4,2 , 0,0 , 3,2 ,  
+		   0,0,  4,2 , 1,1 , 2,2 ,  0,0,  3,2 , 0,3 , 5,2   
+		   
 	};
-	u8 tileindex = dir_to_tile[facedir*8 + dir_i];
-
-	// If not rotated or is side tile, we're done
-	if(facedir == 0 || (tileindex != 0 && tileindex != 1))
-		return getNodeTileN(mn, p, tileindex, data);
-
-	// This is the top or bottom tile, and it shall be rotated; thus rotate it
-	TileSpec spec = getNodeTileN(mn, p, tileindex, data);
-	if(tileindex == 0){
-		if(facedir == 1){ // -90
-			std::string name = data->m_gamedef->tsrc()->getTextureName(spec.texture.id);
-			name += "^[transformR270";
-			spec.texture = data->m_gamedef->tsrc()->getTexture(name);
-		}
-		else if(facedir == 2){ // 180
-			spec.texture.pos += spec.texture.size;
-			spec.texture.size *= -1;
-		}
-		else if(facedir == 3){ // 90
-			std::string name = data->m_gamedef->tsrc()->getTextureName(spec.texture.id);
-			name += "^[transformR90";
-			spec.texture = data->m_gamedef->tsrc()->getTexture(name);
-		}
-	}
-	else if(tileindex == 1){
-		if(facedir == 1){ // -90
-			std::string name = data->m_gamedef->tsrc()->getTextureName(spec.texture.id);
-			name += "^[transformR90";
-			spec.texture = data->m_gamedef->tsrc()->getTexture(name);
-		}
-		else if(facedir == 2){ // 180
-			spec.texture.pos += spec.texture.size;
-			spec.texture.size *= -1;
-		}
-		else if(facedir == 3){ // 90
-			std::string name = data->m_gamedef->tsrc()->getTextureName(spec.texture.id);
-			name += "^[transformR270";
-			spec.texture = data->m_gamedef->tsrc()->getTexture(name);
-		}
-	}
+	u16 tile_index=facedir*16 + dir_i;
+	TileSpec spec = getNodeTileN(mn, p, dir_to_tile[tile_index], data);
+	spec.rotation=dir_to_tile[tile_index + 1];
+	std::string name = data->m_gamedef->tsrc()->getTextureName(spec.texture.id);
+	spec.texture = data->m_gamedef->tsrc()->getTexture(name);
 	return spec;
 }
 
@@ -745,7 +809,7 @@ static void updateFastFaceRow(
 		v3f translate_dir_f,
 		v3s16 face_dir,
 		v3f face_dir_f,
-		core::array<FastFace> &dest)
+		std::vector<FastFace> &dest)
 {
 	v3s16 p = startpos;
 	
@@ -794,6 +858,7 @@ static void updateFastFaceRow(
 					&& next_lights[2] == lights[2]
 					&& next_lights[3] == lights[3]
 					&& next_tile == tile
+					&& tile.rotation == 0
 					&& next_light_source == light_source)
 			{
 				next_is_different = false;
@@ -897,7 +962,7 @@ static void updateFastFaceRow(
 }
 
 static void updateAllFastFaceRows(MeshMakeData *data,
-		core::array<FastFace> &dest)
+		std::vector<FastFace> &dest)
 {
 	/*
 		Go through every y,z and get top(y+) faces in rows of x+
@@ -962,7 +1027,7 @@ MapBlockMesh::MapBlockMesh(MeshMakeData *data):
 	// 24-155ms for MAP_BLOCKSIZE=32  (NOTE: probably outdated)
 	//TimeTaker timer1("MapBlockMesh()");
 
-	core::array<FastFace> fastfaces_new;
+	std::vector<FastFace> fastfaces_new;
 
 	/*
 		We are including the faces of the trailing edges of the block.
@@ -1124,8 +1189,8 @@ MapBlockMesh::MapBlockMesh(MeshMakeData *data):
 		m_mesh->addMeshBuffer(buf);
 		// Mesh grabbed it
 		buf->drop();
-		buf->append(p.vertices.pointer(), p.vertices.size(),
-				p.indices.pointer(), p.indices.size());
+		buf->append(&p.vertices[0], p.vertices.size(),
+				&p.indices[0], p.indices.size());
 	}
 
 	/*
diff --git a/src/mapblock_mesh.h b/src/mapblock_mesh.h
index 5b33990c602fd6ce863bfda4b63173f413267a96..c75984021e32f62331e5f1afd114839ba635fa39 100644
--- a/src/mapblock_mesh.h
+++ b/src/mapblock_mesh.h
@@ -143,13 +143,13 @@ class MapBlockMesh
 struct PreMeshBuffer
 {
 	TileSpec tile;
-	core::array<u16> indices;
-	core::array<video::S3DVertex> vertices;
+	std::vector<u16> indices;
+	std::vector<video::S3DVertex> vertices;
 };
 
 struct MeshCollector
 {
-	core::array<PreMeshBuffer> prebuffers;
+	std::vector<PreMeshBuffer> prebuffers;
 
 	void append(const TileSpec &material,
 			const video::S3DVertex *vertices, u32 numVertices,
diff --git a/src/mapgen.cpp b/src/mapgen.cpp
index ef5da6bf1e43d85f1fdb39150ccb3ba6c0de74b1..b5deaae52c5775d4386902686df875be11eb63d8 100644
--- a/src/mapgen.cpp
+++ b/src/mapgen.cpp
@@ -39,914 +39,366 @@ FlagDesc flagdesc_mapgen[] = {
 	{"trees",          MG_TREES},
 	{"caves",          MG_CAVES},
 	{"dungeons",       MG_DUNGEONS},
-	{"v6_forests",     MGV6_FORESTS},
+	{"v6_jungles",     MGV6_JUNGLES},
 	{"v6_biome_blend", MGV6_BIOME_BLEND},
 	{"flat",           MG_FLAT},
 	{NULL,			   0}
 };
 
-///////////////////////////////////////////////////////////////////////////////
-
-/////////////////////
-
-bool MapgenV6Params::readParams(Settings *settings) {
-	freq_desert = settings->getFloat("mgv6_freq_desert");
-	freq_beach  = settings->getFloat("mgv6_freq_beach");
-
-	np_terrain_base   = settings->getNoiseParams("mgv6_np_terrain_base");
-	np_terrain_higher = settings->getNoiseParams("mgv6_np_terrain_higher");
-	np_steepness      = settings->getNoiseParams("mgv6_np_steepness");
-	np_height_select  = settings->getNoiseParams("mgv6_np_height_select");
-	np_trees          = settings->getNoiseParams("mgv6_np_trees");
-	np_mud            = settings->getNoiseParams("mgv6_np_mud");
-	np_beach          = settings->getNoiseParams("mgv6_np_beach");
-	np_biome          = settings->getNoiseParams("mgv6_np_biome");
-	np_cave           = settings->getNoiseParams("mgv6_np_cave");
-
-	bool success =
-		np_terrain_base  && np_terrain_higher && np_steepness &&
-		np_height_select && np_trees          && np_mud       &&
-		np_beach         && np_biome          && np_cave;
-	return success;
-}
-
-
-void MapgenV6Params::writeParams(Settings *settings) {
-	settings->setFloat("mgv6_freq_desert", freq_desert);
-	settings->setFloat("mgv6_freq_beach",  freq_beach);
-	
-	settings->setNoiseParams("mgv6_np_terrain_base",   np_terrain_base);
-	settings->setNoiseParams("mgv6_np_terrain_higher", np_terrain_higher);
-	settings->setNoiseParams("mgv6_np_steepness",      np_steepness);
-	settings->setNoiseParams("mgv6_np_height_select",  np_height_select);
-	settings->setNoiseParams("mgv6_np_trees",          np_trees);
-	settings->setNoiseParams("mgv6_np_mud",            np_mud);
-	settings->setNoiseParams("mgv6_np_beach",          np_beach);
-	settings->setNoiseParams("mgv6_np_biome",          np_biome);
-	settings->setNoiseParams("mgv6_np_cave",           np_cave);
-}
-
-
-/////////////////////////////////// legacy static functions for farmesh
-
-
-s16 Mapgen::find_ground_level_from_noise(u64 seed, v2s16 p2d, s16 precision) {
-	//just need to return something
-	s16 level = 5;
-	return level;
-}
-
-
-bool Mapgen::get_have_beach(u64 seed, v2s16 p2d) {
-	double sandnoise = noise2d_perlin(
-			0.2+(float)p2d.X/250, 0.7+(float)p2d.Y/250,
-			seed+59420, 3, 0.50);
-
-	return (sandnoise > 0.15);
-}
-
-
-double Mapgen::tree_amount_2d(u64 seed, v2s16 p) {
-	double noise = noise2d_perlin(
-			0.5+(float)p.X/125, 0.5+(float)p.Y/125,
-			seed+2, 4, 0.66);
-	double zeroval = -0.39;
-	if(noise < zeroval)
-		return 0;
-	else
-		return 0.04 * (noise-zeroval) / (1.0-zeroval);
-}
-
-
-#if 0 /// BIG COMMENT
-namespace mapgen
-{
-
-/*
-	Some helper functions for the map generator
-*/
-
-#if 1
-// Returns Y one under area minimum if not found
-static s16 find_ground_level(VoxelManipulator &vmanip, v2s16 p2d,
-		INodeDefManager *ndef)
-{
-	v3s16 em = vmanip.m_area.getExtent();
-	s16 y_nodes_max = vmanip.m_area.MaxEdge.Y;
-	s16 y_nodes_min = vmanip.m_area.MinEdge.Y;
-	u32 i = vmanip.m_area.index(v3s16(p2d.X, y_nodes_max, p2d.Y));
-	s16 y;
-	for(y=y_nodes_max; y>=y_nodes_min; y--)
-	{
-		MapNode &n = vmanip.m_data[i];
-		if(ndef->get(n).walkable)
-			break;
-
-		vmanip.m_area.add_y(em, i, -1);
-	}
-	if(y >= y_nodes_min)
-		return y;
-	else
-		return y_nodes_min - 1;
-}
-
-#if 0
-// Returns Y one under area minimum if not found
-static s16 find_ground_level_clever(VoxelManipulator &vmanip, v2s16 p2d,
-		INodeDefManager *ndef)
-{
-	if(!vmanip.m_area.contains(v3s16(p2d.X, vmanip.m_area.MaxEdge.Y, p2d.Y)))
-		return vmanip.m_area.MinEdge.Y-1;
-	v3s16 em = vmanip.m_area.getExtent();
-	s16 y_nodes_max = vmanip.m_area.MaxEdge.Y;
-	s16 y_nodes_min = vmanip.m_area.MinEdge.Y;
-	u32 i = vmanip.m_area.index(v3s16(p2d.X, y_nodes_max, p2d.Y));
-	s16 y;
-	content_t c_tree = ndef->getId("mapgen_tree");
-	content_t c_leaves = ndef->getId("mapgen_leaves");
-	for(y=y_nodes_max; y>=y_nodes_min; y--)
-	{
-		MapNode &n = vmanip.m_data[i];
-		if(ndef->get(n).walkable
-				&& n.getContent() != c_tree
-				&& n.getContent() != c_leaves)
-			break;
 
-		vmanip.m_area.add_y(em, i, -1);
-	}
-	if(y >= y_nodes_min)
-		return y;
-	else
-		return y_nodes_min - 1;
-}
-#endif
-
-// Returns Y one under area minimum if not found
-static s16 find_stone_level(VoxelManipulator &vmanip, v2s16 p2d,
-		INodeDefManager *ndef)
-{
-	v3s16 em = vmanip.m_area.getExtent();
-	s16 y_nodes_max = vmanip.m_area.MaxEdge.Y;
-	s16 y_nodes_min = vmanip.m_area.MinEdge.Y;
-	u32 i = vmanip.m_area.index(v3s16(p2d.X, y_nodes_max, p2d.Y));
-	s16 y;
-	content_t c_stone = ndef->getId("mapgen_stone");
-	content_t c_desert_stone = ndef->getId("mapgen_desert_stone");
-	for(y=y_nodes_max; y>=y_nodes_min; y--)
-	{
-		MapNode &n = vmanip.m_data[i];
-		content_t c = n.getContent();
-		if(c != CONTENT_IGNORE && (
-				c == c_stone || c == c_desert_stone))
-			break;
-
-		vmanip.m_area.add_y(em, i, -1);
-	}
-	if(y >= y_nodes_min)
-		return y;
-	else
-		return y_nodes_min - 1;
-}
-#endif
-
-
-#if 0
-
-static void make_papyrus(VoxelManipulator &vmanip, v3s16 p0,
-		INodeDefManager *ndef)
-{
-	MapNode papyrusnode(ndef->getId("mapgen_papyrus"));
-
-	s16 trunk_h = myrand_range(2, 3);
-	v3s16 p1 = p0;
-	for(s16 ii=0; ii<trunk_h; ii++)
-	{
-		if(vmanip.m_area.contains(p1))
-			vmanip.m_data[vmanip.m_area.index(p1)] = papyrusnode;
-		p1.Y++;
-	}
-}
+///////////////////////////////////////////////////////////////////////////////
 
-static void make_cactus(VoxelManipulator &vmanip, v3s16 p0,
-		INodeDefManager *ndef)
-{
-	MapNode cactusnode(ndef->getId("mapgen_cactus"));
 
-	s16 trunk_h = 3;
-	v3s16 p1 = p0;
-	for(s16 ii=0; ii<trunk_h; ii++)
-	{
-		if(vmanip.m_area.contains(p1))
-			vmanip.m_data[vmanip.m_area.index(p1)] = cactusnode;
-		p1.Y++;
+Ore *createOre(OreType type) {
+	switch (type) {
+		case ORE_SCATTER:
+			return new OreScatter;
+		case ORE_SHEET:
+			return new OreSheet;
+		//case ORE_CLAYLIKE: //TODO: implement this!
+		//	return new OreClaylike;
+		default:
+			return NULL;
 	}
 }
-#endif
-
-#if 0
-/*
-	Dungeon making routines
-*/
 
-#define VMANIP_FLAG_DUNGEON_INSIDE VOXELFLAG_CHECKED1
-#define VMANIP_FLAG_DUNGEON_PRESERVE VOXELFLAG_CHECKED2
-#define VMANIP_FLAG_DUNGEON_UNTOUCHABLE (\
-		VMANIP_FLAG_DUNGEON_INSIDE|VMANIP_FLAG_DUNGEON_PRESERVE)
-
-static void make_room1(VoxelManipulator &vmanip, v3s16 roomsize, v3s16 roomplace,
-		INodeDefManager *ndef)
-{
-	// Make +-X walls
-	for(s16 z=0; z<roomsize.Z; z++)
-	for(s16 y=0; y<roomsize.Y; y++)
-	{
-		{
-			v3s16 p = roomplace + v3s16(0,y,z);
-			if(vmanip.m_area.contains(p) == false)
-				continue;
-			u32 vi = vmanip.m_area.index(p);
-			if(vmanip.m_flags[vi] & VMANIP_FLAG_DUNGEON_UNTOUCHABLE)
-				continue;
-			vmanip.m_data[vi] = MapNode(ndef->getId("mapgen_cobble"));
-		}
-		{
-			v3s16 p = roomplace + v3s16(roomsize.X-1,y,z);
-			if(vmanip.m_area.contains(p) == false)
-				continue;
-			u32 vi = vmanip.m_area.index(p);
-			if(vmanip.m_flags[vi] & VMANIP_FLAG_DUNGEON_UNTOUCHABLE)
-				continue;
-			vmanip.m_data[vi] = MapNode(ndef->getId("mapgen_cobble"));
-		}
-	}
 
-	// Make +-Z walls
-	for(s16 x=0; x<roomsize.X; x++)
-	for(s16 y=0; y<roomsize.Y; y++)
-	{
-		{
-			v3s16 p = roomplace + v3s16(x,y,0);
-			if(vmanip.m_area.contains(p) == false)
-				continue;
-			u32 vi = vmanip.m_area.index(p);
-			if(vmanip.m_flags[vi] & VMANIP_FLAG_DUNGEON_UNTOUCHABLE)
-				continue;
-			vmanip.m_data[vi] = MapNode(ndef->getId("mapgen_cobble"));
-		}
-		{
-			v3s16 p = roomplace + v3s16(x,y,roomsize.Z-1);
-			if(vmanip.m_area.contains(p) == false)
-				continue;
-			u32 vi = vmanip.m_area.index(p);
-			if(vmanip.m_flags[vi] & VMANIP_FLAG_DUNGEON_UNTOUCHABLE)
-				continue;
-			vmanip.m_data[vi] = MapNode(ndef->getId("mapgen_cobble"));
+void Ore::resolveNodeNames(INodeDefManager *ndef) {
+	if (ore == CONTENT_IGNORE) {
+		ore = ndef->getId(ore_name);
+		if (ore == CONTENT_IGNORE) {
+			errorstream << "Ore::resolveNodeNames: ore node '"
+				<< ore_name << "' not defined";
+			ore     = CONTENT_AIR;
+			wherein = CONTENT_AIR;
 		}
 	}
-
-	// Make +-Y walls (floor and ceiling)
-	for(s16 z=0; z<roomsize.Z; z++)
-	for(s16 x=0; x<roomsize.X; x++)
-	{
-		{
-			v3s16 p = roomplace + v3s16(x,0,z);
-			if(vmanip.m_area.contains(p) == false)
-				continue;
-			u32 vi = vmanip.m_area.index(p);
-			if(vmanip.m_flags[vi] & VMANIP_FLAG_DUNGEON_UNTOUCHABLE)
-				continue;
-			vmanip.m_data[vi] = MapNode(ndef->getId("mapgen_cobble"));
-		}
-		{
-			v3s16 p = roomplace + v3s16(x,roomsize.Y-1,z);
-			if(vmanip.m_area.contains(p) == false)
-				continue;
-			u32 vi = vmanip.m_area.index(p);
-			if(vmanip.m_flags[vi] & VMANIP_FLAG_DUNGEON_UNTOUCHABLE)
-				continue;
-			vmanip.m_data[vi] = MapNode(ndef->getId("mapgen_cobble"));
+	
+	if (wherein == CONTENT_IGNORE) {
+		wherein = ndef->getId(wherein_name);
+		if (wherein == CONTENT_IGNORE) {
+			errorstream << "Ore::resolveNodeNames: wherein node '"
+				<< wherein_name << "' not defined";
+			ore     = CONTENT_AIR;
+			wherein = CONTENT_AIR;
 		}
 	}
-
-	// Fill with air
-	for(s16 z=1; z<roomsize.Z-1; z++)
-	for(s16 y=1; y<roomsize.Y-1; y++)
-	for(s16 x=1; x<roomsize.X-1; x++)
-	{
-		v3s16 p = roomplace + v3s16(x,y,z);
-		if(vmanip.m_area.contains(p) == false)
-			continue;
-		u32 vi = vmanip.m_area.index(p);
-		vmanip.m_flags[vi] |= VMANIP_FLAG_DUNGEON_UNTOUCHABLE;
-		vmanip.m_data[vi] = MapNode(CONTENT_AIR);
-	}
 }
 
-static void make_fill(VoxelManipulator &vmanip, v3s16 place, v3s16 size,
-		u8 avoid_flags, MapNode n, u8 or_flags)
-{
-	for(s16 z=0; z<size.Z; z++)
-	for(s16 y=0; y<size.Y; y++)
-	for(s16 x=0; x<size.X; x++)
-	{
-		v3s16 p = place + v3s16(x,y,z);
-		if(vmanip.m_area.contains(p) == false)
-			continue;
-		u32 vi = vmanip.m_area.index(p);
-		if(vmanip.m_flags[vi] & avoid_flags)
+
+void OreScatter::generate(Mapgen *mg, u32 blockseed, v3s16 nmin, v3s16 nmax) {
+	if (nmin.Y > height_max || nmax.Y < height_min)
+		return;
+	
+	resolveNodeNames(mg->ndef);
+	
+	MapNode n_ore(ore);
+	ManualMapVoxelManipulator *vm = mg->vm;
+	PseudoRandom pr(blockseed);
+	
+	int ymin   = MYMAX(nmin.Y, height_min);
+	int ymax   = MYMIN(nmax.Y, height_max);
+	if (clust_size >= ymax - ymin + 1)
+		return;
+	
+	int volume = (nmax.X - nmin.X + 1) *
+				 (nmax.Y - nmin.Y + 1) *
+				 (nmax.Z - nmin.Z + 1);
+	int csize     = clust_size;
+	int orechance = (csize * csize * csize) / clust_num_ores;
+	int nclusters = volume / clust_scarcity;
+
+	for (int i = 0; i != nclusters; i++) {
+		int x0 = pr.range(nmin.X, nmax.X - csize + 1);
+		int y0 = pr.range(ymin,   ymax   - csize + 1);
+		int z0 = pr.range(nmin.Z, nmax.Z - csize + 1);
+		
+		if (np && (NoisePerlin3D(np, x0, y0, z0, mg->seed) < nthresh))
 			continue;
-		vmanip.m_flags[vi] |= or_flags;
-		vmanip.m_data[vi] = n;
+		
+		for (int z1 = 0; z1 != csize; z1++)
+		for (int y1 = 0; y1 != csize; y1++)
+		for (int x1 = 0; x1 != csize; x1++) {
+			if (pr.range(1, orechance) != 1)
+				continue;
+			
+			u32 i = vm->m_area.index(x0 + x1, y0 + y1, z0 + z1);
+			if (vm->m_data[i].getContent() == wherein)
+				vm->m_data[i] = n_ore;
+		}
 	}
 }
 
-static void make_hole1(VoxelManipulator &vmanip, v3s16 place,
-		INodeDefManager *ndef)
-{
-	make_fill(vmanip, place, v3s16(1,2,1), 0, MapNode(CONTENT_AIR),
-			VMANIP_FLAG_DUNGEON_INSIDE);
-}
 
-static void make_door1(VoxelManipulator &vmanip, v3s16 doorplace, v3s16 doordir,
-		INodeDefManager *ndef)
-{
-	make_hole1(vmanip, doorplace, ndef);
-	// Place torch (for testing)
-	//vmanip.m_data[vmanip.m_area.index(doorplace)] = MapNode(ndef->getId("mapgen_torch"));
-}
+void OreSheet::generate(Mapgen *mg, u32 blockseed, v3s16 nmin, v3s16 nmax) {
+	if (nmin.Y > height_max || nmax.Y < height_min)
+		return;
 
-static v3s16 rand_ortho_dir(PseudoRandom &random)
-{
-	if(random.next()%2==0)
-		return random.next()%2 ? v3s16(-1,0,0) : v3s16(1,0,0);
-	else
-		return random.next()%2 ? v3s16(0,0,-1) : v3s16(0,0,1);
-}
+	resolveNodeNames(mg->ndef);
 
-static v3s16 turn_xz(v3s16 olddir, int t)
-{
-	v3s16 dir;
-	if(t == 0)
-	{
-		// Turn right
-		dir.X = olddir.Z;
-		dir.Z = -olddir.X;
-		dir.Y = olddir.Y;
+	MapNode n_ore(ore);
+	ManualMapVoxelManipulator *vm = mg->vm;
+	PseudoRandom pr(blockseed + 4234);
+	
+	int ymin = MYMAX(nmin.Y, height_min);
+	int ymax = MYMIN(nmax.Y, height_max);
+	if (clust_size >= ymax - ymin + 1)
+		return;
+		
+	int x0 = nmin.X;
+	int z0 = nmin.Z;
+	
+	int x1 = nmax.X;
+	int z1 = nmax.Z;
+	
+	int max_height = clust_size;
+	int y_start = pr.range(ymin, ymax - max_height);
+	
+	if (!noise) {
+		int sx = nmax.X - nmin.X + 1;
+		int sz = nmax.Z - nmin.Z + 1;
+		noise = new Noise(np, 0, sx, sz);
 	}
-	else
-	{
-		// Turn left
-		dir.X = -olddir.Z;
-		dir.Z = olddir.X;
-		dir.Y = olddir.Y;
+	noise->seed = mg->seed + y_start;
+	noise->perlinMap2D(x0, z0);
+	
+	int index = 0;
+	for (int z = z0; z != z1; z++)
+	for (int x = x0; x != x1; x++) {
+		float noiseval = noise->result[index++];
+		if (noiseval < nthresh)
+			continue;
+			
+		int height = max_height * (1. / pr.range(1, 3));
+		int y0 = y_start + np->scale * noiseval; //pr.range(1, 3) - 1;
+		int y1 = y0 + height;
+		for (int y = y0; y != y1; y++) {
+			u32 i = vm->m_area.index(x, y, z);
+			if (!vm->m_area.contains(i))
+				continue;
+				
+			if (vm->m_data[i].getContent() == wherein)
+				vm->m_data[i] = n_ore;
+		}
 	}
-	return dir;
 }
 
-static v3s16 random_turn(PseudoRandom &random, v3s16 olddir)
-{
-	int turn = random.range(0,2);
-	v3s16 dir;
-	if(turn == 0)
-	{
-		// Go straight
-		dir = olddir;
-	}
-	else if(turn == 1)
-		// Turn right
-		dir = turn_xz(olddir, 0);
-	else
-		// Turn left
-		dir = turn_xz(olddir, 1);
-	return dir;
-}
 
-static void make_corridor(VoxelManipulator &vmanip, v3s16 doorplace,
-		v3s16 doordir, v3s16 &result_place, v3s16 &result_dir,
-		PseudoRandom &random, INodeDefManager *ndef)
-{
-	make_hole1(vmanip, doorplace, ndef);
-	v3s16 p0 = doorplace;
-	v3s16 dir = doordir;
-	u32 length;
-	if(random.next()%2)
-		length = random.range(1,13);
-	else
-		length = random.range(1,6);
-	length = random.range(1,13);
-	u32 partlength = random.range(1,13);
-	u32 partcount = 0;
-	s16 make_stairs = 0;
-	if(random.next()%2 == 0 && partlength >= 3)
-		make_stairs = random.next()%2 ? 1 : -1;
-	for(u32 i=0; i<length; i++)
-	{
-		v3s16 p = p0 + dir;
-		if(partcount != 0)
-			p.Y += make_stairs;
+void Mapgen::updateLiquid(UniqueQueue<v3s16> *trans_liquid, v3s16 nmin, v3s16 nmax) {
+	bool isliquid, wasliquid;
+	v3s16 em  = vm->m_area.getExtent();
 
-		/*// If already empty
-		if(vmanip.getNodeNoExNoEmerge(p).getContent()
-				== CONTENT_AIR
-		&& vmanip.getNodeNoExNoEmerge(p+v3s16(0,1,0)).getContent()
-				== CONTENT_AIR)
-		{
-		}*/
+	for (s16 z = nmin.Z; z <= nmax.Z; z++) {
+		for (s16 x = nmin.X; x <= nmax.X; x++) {
+			wasliquid = true;
+			
+			u32 i = vm->m_area.index(x, nmax.Y, z);
+			for (s16 y = nmax.Y; y >= nmin.Y; y--) {
+				isliquid = ndef->get(vm->m_data[i]).isLiquid();
+				
+				// there was a change between liquid and nonliquid, add to queue
+				if (isliquid != wasliquid)
+					trans_liquid->push_back(v3s16(x, y, z));
 
-		if(vmanip.m_area.contains(p) == true
-				&& vmanip.m_area.contains(p+v3s16(0,1,0)) == true)
-		{
-			if(make_stairs)
-			{
-				make_fill(vmanip, p+v3s16(-1,-1,-1), v3s16(3,5,3),
-						VMANIP_FLAG_DUNGEON_UNTOUCHABLE, MapNode(ndef->getId("mapgen_cobble")), 0);
-				make_fill(vmanip, p, v3s16(1,2,1), 0, MapNode(CONTENT_AIR),
-						VMANIP_FLAG_DUNGEON_INSIDE);
-				make_fill(vmanip, p-dir, v3s16(1,2,1), 0, MapNode(CONTENT_AIR),
-						VMANIP_FLAG_DUNGEON_INSIDE);
+				wasliquid = isliquid;
+				vm->m_area.add_y(em, i, -1);
 			}
-			else
-			{
-				make_fill(vmanip, p+v3s16(-1,-1,-1), v3s16(3,4,3),
-						VMANIP_FLAG_DUNGEON_UNTOUCHABLE, MapNode(ndef->getId("mapgen_cobble")), 0);
-				make_hole1(vmanip, p, ndef);
-				/*make_fill(vmanip, p, v3s16(1,2,1), 0, MapNode(CONTENT_AIR),
-						VMANIP_FLAG_DUNGEON_INSIDE);*/
-			}
-
-			p0 = p;
-		}
-		else
-		{
-			// Can't go here, turn away
-			dir = turn_xz(dir, random.range(0,1));
-			make_stairs = -make_stairs;
-			partcount = 0;
-			partlength = random.range(1,length);
-			continue;
-		}
-
-		partcount++;
-		if(partcount >= partlength)
-		{
-			partcount = 0;
-
-			dir = random_turn(random, dir);
-
-			partlength = random.range(1,length);
-
-			make_stairs = 0;
-			if(random.next()%2 == 0 && partlength >= 3)
-				make_stairs = random.next()%2 ? 1 : -1;
 		}
 	}
-	result_place = p0;
-	result_dir = dir;
 }
 
-class RoomWalker
-{
-public:
 
-	RoomWalker(VoxelManipulator &vmanip_, v3s16 pos, PseudoRandom &random,
-			INodeDefManager *ndef):
-			vmanip(vmanip_),
-			m_pos(pos),
-			m_random(random),
-			m_ndef(ndef)
-	{
-		randomizeDir();
-	}
+void Mapgen::setLighting(v3s16 nmin, v3s16 nmax, u8 light) {
+	ScopeProfiler sp(g_profiler, "EmergeThread: mapgen lighting update", SPT_AVG);
+	VoxelArea a(nmin - v3s16(1,0,1) * MAP_BLOCKSIZE,
+				nmax + v3s16(1,0,1) * MAP_BLOCKSIZE);
 
-	void randomizeDir()
-	{
-		m_dir = rand_ortho_dir(m_random);
+	for (int z = a.MinEdge.Z; z <= a.MaxEdge.Z; z++) {
+		for (int y = a.MinEdge.Y; y <= a.MaxEdge.Y; y++) {
+			u32 i = vm->m_area.index(a.MinEdge.X, y, z);
+			for (int x = a.MinEdge.X; x <= a.MaxEdge.X; x++, i++)
+				vm->m_data[i].param1 = light;
+		}
 	}
+}
 
-	void setPos(v3s16 pos)
-	{
-		m_pos = pos;
-	}
 
-	void setDir(v3s16 dir)
-	{
-		m_dir = dir;
-	}
+void Mapgen::lightSpread(VoxelArea &a, v3s16 p, u8 light) {
+	if (light <= 1 || !a.contains(p))
+		return;
+		
+	u32 vi = vm->m_area.index(p);
+	MapNode &nn = vm->m_data[vi];
 
-	bool findPlaceForDoor(v3s16 &result_place, v3s16 &result_dir)
-	{
-		for(u32 i=0; i<100; i++)
-		{
-			v3s16 p = m_pos + m_dir;
-			v3s16 p1 = p + v3s16(0,1,0);
-			if(vmanip.m_area.contains(p) == false
-					|| vmanip.m_area.contains(p1) == false
-					|| i % 4 == 0)
-			{
-				randomizeDir();
-				continue;
-			}
-			if(vmanip.getNodeNoExNoEmerge(p).getContent()
-					== m_ndef->getId("mapgen_cobble")
-			&& vmanip.getNodeNoExNoEmerge(p1).getContent()
-					== m_ndef->getId("mapgen_cobble"))
-			{
-				// Found wall, this is a good place!
-				result_place = p;
-				result_dir = m_dir;
-				// Randomize next direction
-				randomizeDir();
-				return true;
-			}
-			/*
-				Determine where to move next
-			*/
-			// Jump one up if the actual space is there
-			if(vmanip.getNodeNoExNoEmerge(p+v3s16(0,0,0)).getContent()
-					== m_ndef->getId("mapgen_cobble")
-			&& vmanip.getNodeNoExNoEmerge(p+v3s16(0,1,0)).getContent()
-					== CONTENT_AIR
-			&& vmanip.getNodeNoExNoEmerge(p+v3s16(0,2,0)).getContent()
-					== CONTENT_AIR)
-				p += v3s16(0,1,0);
-			// Jump one down if the actual space is there
-			if(vmanip.getNodeNoExNoEmerge(p+v3s16(0,1,0)).getContent()
-					== m_ndef->getId("mapgen_cobble")
-			&& vmanip.getNodeNoExNoEmerge(p+v3s16(0,0,0)).getContent()
-					== CONTENT_AIR
-			&& vmanip.getNodeNoExNoEmerge(p+v3s16(0,-1,0)).getContent()
-					== CONTENT_AIR)
-				p += v3s16(0,-1,0);
-			// Check if walking is now possible
-			if(vmanip.getNodeNoExNoEmerge(p).getContent()
-					!= CONTENT_AIR
-			|| vmanip.getNodeNoExNoEmerge(p+v3s16(0,1,0)).getContent()
-					!= CONTENT_AIR)
-			{
-				// Cannot continue walking here
-				randomizeDir();
+	light--;
+	// should probably compare masked, but doesn't seem to make a difference
+	if (light <= nn.param1 || !ndef->get(nn).light_propagates)
+		return;
+	
+	nn.param1 = light;
+	
+	lightSpread(a, p + v3s16(0, 0, 1), light);
+	lightSpread(a, p + v3s16(0, 1, 0), light);
+	lightSpread(a, p + v3s16(1, 0, 0), light);
+	lightSpread(a, p - v3s16(0, 0, 1), light);
+	lightSpread(a, p - v3s16(0, 1, 0), light);
+	lightSpread(a, p - v3s16(1, 0, 0), light);
+}
+
+
+void Mapgen::calcLighting(v3s16 nmin, v3s16 nmax) {
+	VoxelArea a(nmin - v3s16(1,0,1) * MAP_BLOCKSIZE,
+				nmax + v3s16(1,0,1) * MAP_BLOCKSIZE);
+	bool block_is_underground = (water_level >= nmax.Y);
+
+	ScopeProfiler sp(g_profiler, "EmergeThread: mapgen lighting update", SPT_AVG);
+	//TimeTaker t("updateLighting");
+
+	// first, send vertical rays of sunshine downward
+	v3s16 em = vm->m_area.getExtent();
+	for (int z = a.MinEdge.Z; z <= a.MaxEdge.Z; z++) {
+		for (int x = a.MinEdge.X; x <= a.MaxEdge.X; x++) {
+			// see if we can get a light value from the overtop
+			u32 i = vm->m_area.index(x, a.MaxEdge.Y + 1, z);
+			if (vm->m_data[i].getContent() == CONTENT_IGNORE) {
+				if (block_is_underground)
+					continue;
+			} else if ((vm->m_data[i].param1 & 0x0F) != LIGHT_SUN) {
 				continue;
 			}
-			// Move there
-			m_pos = p;
-		}
-		return false;
-	}
-
-	bool findPlaceForRoomDoor(v3s16 roomsize, v3s16 &result_doorplace,
-			v3s16 &result_doordir, v3s16 &result_roomplace)
-	{
-		for(s16 trycount=0; trycount<30; trycount++)
-		{
-			v3s16 doorplace;
-			v3s16 doordir;
-			bool r = findPlaceForDoor(doorplace, doordir);
-			if(r == false)
-				continue;
-			v3s16 roomplace;
-			// X east, Z north, Y up
-#if 1
-			if(doordir == v3s16(1,0,0)) // X+
-				roomplace = doorplace +
-						v3s16(0,-1,m_random.range(-roomsize.Z+2,-2));
-			if(doordir == v3s16(-1,0,0)) // X-
-				roomplace = doorplace +
-						v3s16(-roomsize.X+1,-1,m_random.range(-roomsize.Z+2,-2));
-			if(doordir == v3s16(0,0,1)) // Z+
-				roomplace = doorplace +
-						v3s16(m_random.range(-roomsize.X+2,-2),-1,0);
-			if(doordir == v3s16(0,0,-1)) // Z-
-				roomplace = doorplace +
-						v3s16(m_random.range(-roomsize.X+2,-2),-1,-roomsize.Z+1);
-#endif
-#if 0
-			if(doordir == v3s16(1,0,0)) // X+
-				roomplace = doorplace + v3s16(0,-1,-roomsize.Z/2);
-			if(doordir == v3s16(-1,0,0)) // X-
-				roomplace = doorplace + v3s16(-roomsize.X+1,-1,-roomsize.Z/2);
-			if(doordir == v3s16(0,0,1)) // Z+
-				roomplace = doorplace + v3s16(-roomsize.X/2,-1,0);
-			if(doordir == v3s16(0,0,-1)) // Z-
-				roomplace = doorplace + v3s16(-roomsize.X/2,-1,-roomsize.Z+1);
-#endif
+			vm->m_area.add_y(em, i, -1);
 
-			// Check fit
-			bool fits = true;
-			for(s16 z=1; z<roomsize.Z-1; z++)
-			for(s16 y=1; y<roomsize.Y-1; y++)
-			for(s16 x=1; x<roomsize.X-1; x++)
-			{
-				v3s16 p = roomplace + v3s16(x,y,z);
-				if(vmanip.m_area.contains(p) == false)
-				{
-					fits = false;
+			for (int y = a.MaxEdge.Y; y >= a.MinEdge.Y; y--) {
+				MapNode &n = vm->m_data[i];
+				if (!ndef->get(n).sunlight_propagates)
 					break;
-				}
-				if(vmanip.m_flags[vmanip.m_area.index(p)]
-						& VMANIP_FLAG_DUNGEON_INSIDE)
-				{
-					fits = false;
-					break;
-				}
-			}
-			if(fits == false)
-			{
-				// Find new place
-				continue;
+				n.param1 = LIGHT_SUN;
+				vm->m_area.add_y(em, i, -1);
 			}
-			result_doorplace = doorplace;
-			result_doordir = doordir;
-			result_roomplace = roomplace;
-			return true;
 		}
-		return false;
 	}
-
-private:
-	VoxelManipulator &vmanip;
-	v3s16 m_pos;
-	v3s16 m_dir;
-	PseudoRandom &m_random;
-	INodeDefManager *m_ndef;
-};
-
-static void make_dungeon1(VoxelManipulator &vmanip, PseudoRandom &random,
-		INodeDefManager *ndef)
-{
-	v3s16 areasize = vmanip.m_area.getExtent();
-	v3s16 roomsize;
-	v3s16 roomplace;
-
-	/*
-		Find place for first room
-	*/
-	bool fits = false;
-	for(u32 i=0; i<100; i++)
-	{
-		roomsize = v3s16(random.range(4,8),random.range(4,6),random.range(4,8));
-		roomplace = vmanip.m_area.MinEdge + v3s16(
-				random.range(0,areasize.X-roomsize.X-1),
-				random.range(0,areasize.Y-roomsize.Y-1),
-				random.range(0,areasize.Z-roomsize.Z-1));
-		/*
-			Check that we're not putting the room to an unknown place,
-			otherwise it might end up floating in the air
-		*/
-		fits = true;
-		for(s16 z=1; z<roomsize.Z-1; z++)
-		for(s16 y=1; y<roomsize.Y-1; y++)
-		for(s16 x=1; x<roomsize.X-1; x++)
-		{
-			v3s16 p = roomplace + v3s16(x,y,z);
-			u32 vi = vmanip.m_area.index(p);
-			if(vmanip.m_flags[vi] & VMANIP_FLAG_DUNGEON_INSIDE)
-			{
-				fits = false;
-				break;
-			}
-			if(vmanip.m_data[vi].getContent() == CONTENT_IGNORE)
-			{
-				fits = false;
-				break;
+	
+	// now spread the sunlight and light up any sources
+	for (int z = a.MinEdge.Z; z <= a.MaxEdge.Z; z++) {
+		for (int y = a.MinEdge.Y; y <= a.MaxEdge.Y; y++) {
+			u32 i = vm->m_area.index(a.MinEdge.X, y, z);
+			for (int x = a.MinEdge.X; x <= a.MaxEdge.X; x++, i++) {
+				MapNode &n = vm->m_data[i];
+				if (n.getContent() == CONTENT_IGNORE ||
+					!ndef->get(n).light_propagates)
+					continue;
+				
+				u8 light_produced = ndef->get(n).light_source & 0x0F;
+				if (light_produced)
+					n.param1 = light_produced;
+				
+				u8 light = n.param1 & 0x0F;
+				if (light) {
+					lightSpread(a, v3s16(x,     y,     z + 1), light);
+					lightSpread(a, v3s16(x,     y + 1, z    ), light);
+					lightSpread(a, v3s16(x + 1, y,     z    ), light);
+					lightSpread(a, v3s16(x,     y,     z - 1), light);
+					lightSpread(a, v3s16(x,     y - 1, z    ), light);
+					lightSpread(a, v3s16(x - 1, y,     z    ), light);
+				}
 			}
 		}
-		if(fits)
-			break;
 	}
-	// No place found
-	if(fits == false)
-		return;
-
-	/*
-		Stores the center position of the last room made, so that
-		a new corridor can be started from the last room instead of
-		the new room, if chosen so.
-	*/
-	v3s16 last_room_center = roomplace+v3s16(roomsize.X/2,1,roomsize.Z/2);
-
-	u32 room_count = random.range(2,7);
-	for(u32 i=0; i<room_count; i++)
-	{
-		// Make a room to the determined place
-		make_room1(vmanip, roomsize, roomplace, ndef);
-
-		v3s16 room_center = roomplace + v3s16(roomsize.X/2,1,roomsize.Z/2);
-
-		// Place torch at room center (for testing)
-		//vmanip.m_data[vmanip.m_area.index(room_center)] = MapNode(ndef->getId("mapgen_torch"));
+	
+	//printf("updateLighting: %dms\n", t.stop());
+}
 
-		// Quit if last room
-		if(i == room_count-1)
-			break;
 
-		// Determine walker start position
+void Mapgen::calcLightingOld(v3s16 nmin, v3s16 nmax) {
+	enum LightBank banks[2] = {LIGHTBANK_DAY, LIGHTBANK_NIGHT};
 
-		bool start_in_last_room = (random.range(0,2)!=0);
-		//bool start_in_last_room = true;
+	VoxelArea a(nmin - v3s16(1,0,1) * MAP_BLOCKSIZE,
+				nmax + v3s16(1,0,1) * MAP_BLOCKSIZE);
+	bool block_is_underground = (water_level > nmax.Y);
+	bool sunlight = !block_is_underground;
 
-		v3s16 walker_start_place;
+	ScopeProfiler sp(g_profiler, "EmergeThread: mapgen lighting update", SPT_AVG);
+	
+	for (int i = 0; i < 2; i++) {
+		enum LightBank bank = banks[i];
+		std::set<v3s16> light_sources;
+		std::map<v3s16, u8> unlight_from;
 
-		if(start_in_last_room)
-		{
-			walker_start_place = last_room_center;
-		}
-		else
-		{
-			walker_start_place = room_center;
-			// Store center of current room as the last one
-			last_room_center = room_center;
-		}
+		voxalgo::clearLightAndCollectSources(*vm, a, bank, ndef,
+                                        light_sources, unlight_from);
+		voxalgo::propagateSunlight(*vm, a, sunlight, light_sources, ndef);
 
-		// Create walker and find a place for a door
-		RoomWalker walker(vmanip, walker_start_place, random, ndef);
-		v3s16 doorplace;
-		v3s16 doordir;
-		bool r = walker.findPlaceForDoor(doorplace, doordir);
-		if(r == false)
-			return;
+		vm->unspreadLight(bank, unlight_from, light_sources, ndef);
+		vm->spreadLight(bank, light_sources, ndef);
+	}
+}
 
-		if(random.range(0,1)==0)
-			// Make the door
-			make_door1(vmanip, doorplace, doordir, ndef);
-		else
-			// Don't actually make a door
-			doorplace -= doordir;
 
-		// Make a random corridor starting from the door
-		v3s16 corridor_end;
-		v3s16 corridor_end_dir;
-		make_corridor(vmanip, doorplace, doordir, corridor_end,
-				corridor_end_dir, random, ndef);
+//////////////////////// Mapgen V6 parameter read/write
 
-		// Find a place for a random sized room
-		roomsize = v3s16(random.range(4,8),random.range(4,6),random.range(4,8));
-		walker.setPos(corridor_end);
-		walker.setDir(corridor_end_dir);
-		r = walker.findPlaceForRoomDoor(roomsize, doorplace, doordir, roomplace);
-		if(r == false)
-			return;
+bool MapgenV6Params::readParams(Settings *settings) {
+	freq_desert = settings->getFloat("mgv6_freq_desert");
+	freq_beach  = settings->getFloat("mgv6_freq_beach");
 
-		if(random.range(0,1)==0)
-			// Make the door
-			make_door1(vmanip, doorplace, doordir, ndef);
-		else
-			// Don't actually make a door
-			roomplace -= doordir;
+	np_terrain_base   = settings->getNoiseParams("mgv6_np_terrain_base");
+	np_terrain_higher = settings->getNoiseParams("mgv6_np_terrain_higher");
+	np_steepness      = settings->getNoiseParams("mgv6_np_steepness");
+	np_height_select  = settings->getNoiseParams("mgv6_np_height_select");
+	np_mud            = settings->getNoiseParams("mgv6_np_mud");
+	np_beach          = settings->getNoiseParams("mgv6_np_beach");
+	np_biome          = settings->getNoiseParams("mgv6_np_biome");
+	np_cave           = settings->getNoiseParams("mgv6_np_cave");
+	np_humidity       = settings->getNoiseParams("mgv6_np_humidity");
+	np_trees          = settings->getNoiseParams("mgv6_np_trees");
+	np_apple_trees    = settings->getNoiseParams("mgv6_np_apple_trees");
 
-	}
+	bool success =
+		np_terrain_base  && np_terrain_higher && np_steepness &&
+		np_height_select && np_trees          && np_mud       &&
+		np_beach         && np_biome          && np_cave      &&
+		np_humidity      && np_apple_trees;
+	return success;
 }
-#endif
 
-#if 0
-static void make_nc(VoxelManipulator &vmanip, PseudoRandom &random,
-		INodeDefManager *ndef)
-{
-	v3s16 dir;
-	u8 facedir_i = 0;
-	s32 r = random.range(0, 3);
-	if(r == 0){
-		dir = v3s16( 1, 0, 0);
-		facedir_i = 3;
-	}
-	if(r == 1){
-		dir = v3s16(-1, 0, 0);
-		facedir_i = 1;
-	}
-	if(r == 2){
-		dir = v3s16( 0, 0, 1);
-		facedir_i = 2;
-	}
-	if(r == 3){
-		dir = v3s16( 0, 0,-1);
-		facedir_i = 0;
-	}
-	v3s16 p = vmanip.m_area.MinEdge + v3s16(
-			16+random.range(0,15),
-			16+random.range(0,15),
-			16+random.range(0,15));
-	vmanip.m_data[vmanip.m_area.index(p)] = MapNode(ndef->getId("mapgen_nyancat"), facedir_i);
-	u32 length = random.range(3,15);
-	for(u32 j=0; j<length; j++)
-	{
-		p -= dir;
-		vmanip.m_data[vmanip.m_area.index(p)] = MapNode(ndef->getId("mapgen_nyancat_rainbow"));
-	}
+
+void MapgenV6Params::writeParams(Settings *settings) {
+	settings->setFloat("mgv6_freq_desert", freq_desert);
+	settings->setFloat("mgv6_freq_beach",  freq_beach);
+	
+	settings->setNoiseParams("mgv6_np_terrain_base",   np_terrain_base);
+	settings->setNoiseParams("mgv6_np_terrain_higher", np_terrain_higher);
+	settings->setNoiseParams("mgv6_np_steepness",      np_steepness);
+	settings->setNoiseParams("mgv6_np_height_select",  np_height_select);
+	settings->setNoiseParams("mgv6_np_mud",            np_mud);
+	settings->setNoiseParams("mgv6_np_beach",          np_beach);
+	settings->setNoiseParams("mgv6_np_biome",          np_biome);
+	settings->setNoiseParams("mgv6_np_cave",           np_cave);
+	settings->setNoiseParams("mgv6_np_humidity",       np_humidity);
+	settings->setNoiseParams("mgv6_np_trees",          np_trees);
+	settings->setNoiseParams("mgv6_np_apple_trees",    np_apple_trees);
 }
-#endif
 
-/*
-	Noise functions. Make sure seed is mangled differently in each one.
-*/
 
-#if 0
-/*
-	Scaling the output of the noise function affects the overdrive of the
-	contour function, which affects the shape of the output considerably.
-*/
-#define CAVE_NOISE_SCALE 12.0
-//#define CAVE_NOISE_SCALE 10.0
-//#define CAVE_NOISE_SCALE 7.5
-//#define CAVE_NOISE_SCALE 5.0
-//#define CAVE_NOISE_SCALE 1.0
+/////////////////////////////////// legacy static functions for farmesh
 
-//#define CAVE_NOISE_THRESHOLD (2.5/CAVE_NOISE_SCALE)
-#define CAVE_NOISE_THRESHOLD (1.5/CAVE_NOISE_SCALE)
 
-NoiseParams get_cave_noise1_params(u64 seed)
-{
-	/*return NoiseParams(NOISE_PERLIN_CONTOUR, seed+52534, 5, 0.7,
-			200, CAVE_NOISE_SCALE);*/
-	/*return NoiseParams(NOISE_PERLIN_CONTOUR, seed+52534, 4, 0.7,
-			100, CAVE_NOISE_SCALE);*/
-	/*return NoiseParams(NOISE_PERLIN_CONTOUR, seed+52534, 5, 0.6,
-			100, CAVE_NOISE_SCALE);*/
-	/*return NoiseParams(NOISE_PERLIN_CONTOUR, seed+52534, 5, 0.3,
-			100, CAVE_NOISE_SCALE);*/
-	return NoiseParams(NOISE_PERLIN_CONTOUR, seed+52534, 4, 0.5,
-			50, CAVE_NOISE_SCALE);
-	//return NoiseParams(NOISE_CONSTANT_ONE);
+s16 Mapgen::find_ground_level_from_noise(u64 seed, v2s16 p2d, s16 precision) {
+	//just need to return something
+	s16 level = 5;
+	return level;
 }
 
-NoiseParams get_cave_noise2_params(u64 seed)
-{
-	/*return NoiseParams(NOISE_PERLIN_CONTOUR_FLIP_YZ, seed+10325, 5, 0.7,
-			200, CAVE_NOISE_SCALE);*/
-	/*return NoiseParams(NOISE_PERLIN_CONTOUR_FLIP_YZ, seed+10325, 4, 0.7,
-			100, CAVE_NOISE_SCALE);*/
-	/*return NoiseParams(NOISE_PERLIN_CONTOUR_FLIP_YZ, seed+10325, 5, 0.3,
-			100, CAVE_NOISE_SCALE);*/
-	return NoiseParams(NOISE_PERLIN_CONTOUR_FLIP_YZ, seed+10325, 4, 0.5,
-			50, CAVE_NOISE_SCALE);
-	//return NoiseParams(NOISE_CONSTANT_ONE);
-}
 
-NoiseParams get_ground_noise1_params(u64 seed)
-{
-	return NoiseParams(NOISE_PERLIN, seed+983240, 4,
-			0.55, 80.0, 40.0);
-}
+bool Mapgen::get_have_beach(u64 seed, v2s16 p2d) {
+	double sandnoise = noise2d_perlin(
+			0.2+(float)p2d.X/250, 0.7+(float)p2d.Y/250,
+			seed+59420, 3, 0.50);
 
-NoiseParams get_ground_crumbleness_params(u64 seed)
-{
-	return NoiseParams(NOISE_PERLIN, seed+34413, 3,
-			1.3, 20.0, 1.0);
+	return (sandnoise > 0.15);
 }
 
-NoiseParams get_ground_wetness_params(u64 seed)
-{
-	return NoiseParams(NOISE_PERLIN, seed+32474, 4,
-			1.1, 40.0, 1.0);
-}
 
-bool is_cave(u64 seed, v3s16 p)
-{
-	double d1 = noise3d_param(get_cave_noise1_params(seed), p.X,p.Y,p.Z);
-	double d2 = noise3d_param(get_cave_noise2_params(seed), p.X,p.Y,p.Z);
-	return d1*d2 > CAVE_NOISE_THRESHOLD;
-}
-
-/*
-	Ground density noise shall be interpreted by using this.
-
-	TODO: No perlin noises here, they should be outsourced
-	      and buffered
-		  NOTE: The speed of these actually isn't terrible
-*/
-bool val_is_ground(double ground_noise1_val, v3s16 p, u64 seed)
-{
-	//return ((double)p.Y < ground_noise1_val);
-
-	double f = 0.55 + noise2d_perlin(
-			0.5+(float)p.X/250, 0.5+(float)p.Z/250,
-			seed+920381, 3, 0.45);
-	if(f < 0.01)
-		f = 0.01;
-	else if(f >= 1.0)
-		f *= 1.6;
-	double h = WATER_LEVEL + 10 * noise2d_perlin(
-			0.5+(float)p.X/250, 0.5+(float)p.Z/250,
-			seed+84174, 4, 0.5);
-	/*double f = 1;
-	double h = 0;*/
-	return ((double)p.Y - h < ground_noise1_val * f);
-}
-
-/*
-	Queries whether a position is ground or not.
-*/
-bool is_ground(u64 seed, v3s16 p)
-{
-	double val1 = noise3d_param(get_ground_noise1_params(seed), p.X,p.Y,p.Z);
-	return val_is_ground(val1, p, seed);
-}
-#endif
-
-// Amount of trees per area in nodes
-double tree_amount_2d(u64 seed, v2s16 p)
-{
-	/*double noise = noise2d_perlin(
-			0.5+(float)p.X/250, 0.5+(float)p.Y/250,
-			seed+2, 5, 0.66);*/
+double Mapgen::tree_amount_2d(u64 seed, v2s16 p) {
 	double noise = noise2d_perlin(
 			0.5+(float)p.X/125, 0.5+(float)p.Y/125,
 			seed+2, 4, 0.66);
@@ -956,1895 +408,3 @@ double tree_amount_2d(u64 seed, v2s16 p)
 	else
 		return 0.04 * (noise-zeroval) / (1.0-zeroval);
 }
-
-#if 0
-double surface_humidity_2d(u64 seed, v2s16 p)
-{
-	double noise = noise2d_perlin(
-			0.5+(float)p.X/500, 0.5+(float)p.Y/500,
-			seed+72384, 4, 0.66);
-	noise = (noise + 1.0)/2.0;
-	if(noise < 0.0)
-		noise = 0.0;
-	if(noise > 1.0)
-		noise = 1.0;
-	return noise;
-}
-
-/*
-	Incrementally find ground level from 3d noise
-*/
-s16 find_ground_level_from_noise(u64 seed, v2s16 p2d, s16 precision)
-{
-	// Start a bit fuzzy to make averaging lower precision values
-	// more useful
-	s16 level = myrand_range(-precision/2, precision/2);
-	s16 dec[] = {31000, 100, 20, 4, 1, 0};
-	s16 i;
-	for(i = 1; dec[i] != 0 && precision <= dec[i]; i++)
-	{
-		// First find non-ground by going upwards
-		// Don't stop in caves.
-		{
-			s16 max = level+dec[i-1]*2;
-			v3s16 p(p2d.X, level, p2d.Y);
-			for(; p.Y < max; p.Y += dec[i])
-			{
-				if(!is_ground(seed, p))
-				{
-					level = p.Y;
-					break;
-				}
-			}
-		}
-		// Then find ground by going downwards from there.
-		// Go in caves, too, when precision is 1.
-		{
-			s16 min = level-dec[i-1]*2;
-			v3s16 p(p2d.X, level, p2d.Y);
-			for(; p.Y>min; p.Y-=dec[i])
-			{
-				bool ground = is_ground(seed, p);
-				/*if(dec[i] == 1 && is_cave(seed, p))
-					ground = false;*/
-				if(ground)
-				{
-					level = p.Y;
-					break;
-				}
-			}
-		}
-	}
-
-	// This is more like the actual ground level
-	level += dec[i-1]/2;
-
-	return level;
-}
-
-double get_sector_average_ground_level(u64 seed, v2s16 sectorpos, double p=4);
-
-double get_sector_average_ground_level(u64 seed, v2s16 sectorpos, double p)
-{
-	v2s16 node_min = sectorpos*MAP_BLOCKSIZE;
-	v2s16 node_max = (sectorpos+v2s16(1,1))*MAP_BLOCKSIZE-v2s16(1,1);
-	double a = 0;
-	a += find_ground_level_from_noise(seed,
-			v2s16(node_min.X, node_min.Y), p);
-	a += find_ground_level_from_noise(seed,
-			v2s16(node_min.X, node_max.Y), p);
-	a += find_ground_level_from_noise(seed,
-			v2s16(node_max.X, node_max.Y), p);
-	a += find_ground_level_from_noise(seed,
-			v2s16(node_max.X, node_min.Y), p);
-	a += find_ground_level_from_noise(seed,
-			v2s16(node_min.X+MAP_BLOCKSIZE/2, node_min.Y+MAP_BLOCKSIZE/2), p);
-	a /= 5;
-	return a;
-}
-
-double get_sector_maximum_ground_level(u64 seed, v2s16 sectorpos, double p=4);
-
-double get_sector_maximum_ground_level(u64 seed, v2s16 sectorpos, double p)
-{
-	v2s16 node_min = sectorpos*MAP_BLOCKSIZE;
-	v2s16 node_max = (sectorpos+v2s16(1,1))*MAP_BLOCKSIZE-v2s16(1,1);
-	double a = -31000;
-	// Corners
-	a = MYMAX(a, find_ground_level_from_noise(seed,
-			v2s16(node_min.X, node_min.Y), p));
-	a = MYMAX(a, find_ground_level_from_noise(seed,
-			v2s16(node_min.X, node_max.Y), p));
-	a = MYMAX(a, find_ground_level_from_noise(seed,
-			v2s16(node_max.X, node_max.Y), p));
-	a = MYMAX(a, find_ground_level_from_noise(seed,
-			v2s16(node_min.X, node_min.Y), p));
-	// Center
-	a = MYMAX(a, find_ground_level_from_noise(seed,
-			v2s16(node_min.X+MAP_BLOCKSIZE/2, node_min.Y+MAP_BLOCKSIZE/2), p));
-	// Side middle points
-	a = MYMAX(a, find_ground_level_from_noise(seed,
-			v2s16(node_min.X+MAP_BLOCKSIZE/2, node_min.Y), p));
-	a = MYMAX(a, find_ground_level_from_noise(seed,
-			v2s16(node_min.X+MAP_BLOCKSIZE/2, node_max.Y), p));
-	a = MYMAX(a, find_ground_level_from_noise(seed,
-			v2s16(node_min.X, node_min.Y+MAP_BLOCKSIZE/2), p));
-	a = MYMAX(a, find_ground_level_from_noise(seed,
-			v2s16(node_max.X, node_min.Y+MAP_BLOCKSIZE/2), p));
-	return a;
-}
-
-double get_sector_minimum_ground_level(u64 seed, v2s16 sectorpos, double p=4);
-
-double get_sector_minimum_ground_level(u64 seed, v2s16 sectorpos, double p)
-{
-	v2s16 node_min = sectorpos*MAP_BLOCKSIZE;
-	v2s16 node_max = (sectorpos+v2s16(1,1))*MAP_BLOCKSIZE-v2s16(1,1);
-	double a = 31000;
-	// Corners
-	a = MYMIN(a, find_ground_level_from_noise(seed,
-			v2s16(node_min.X, node_min.Y), p));
-	a = MYMIN(a, find_ground_level_from_noise(seed,
-			v2s16(node_min.X, node_max.Y), p));
-	a = MYMIN(a, find_ground_level_from_noise(seed,
-			v2s16(node_max.X, node_max.Y), p));
-	a = MYMIN(a, find_ground_level_from_noise(seed,
-			v2s16(node_min.X, node_min.Y), p));
-	// Center
-	a = MYMIN(a, find_ground_level_from_noise(seed,
-			v2s16(node_min.X+MAP_BLOCKSIZE/2, node_min.Y+MAP_BLOCKSIZE/2), p));
-	// Side middle points
-	a = MYMIN(a, find_ground_level_from_noise(seed,
-			v2s16(node_min.X+MAP_BLOCKSIZE/2, node_min.Y), p));
-	a = MYMIN(a, find_ground_level_from_noise(seed,
-			v2s16(node_min.X+MAP_BLOCKSIZE/2, node_max.Y), p));
-	a = MYMIN(a, find_ground_level_from_noise(seed,
-			v2s16(node_min.X, node_min.Y+MAP_BLOCKSIZE/2), p));
-	a = MYMIN(a, find_ground_level_from_noise(seed,
-			v2s16(node_max.X, node_min.Y+MAP_BLOCKSIZE/2), p));
-	return a;
-}
-#endif
-
-// Required by mapgen.h
-bool block_is_underground(u64 seed, v3s16 blockpos)
-{
-	/*s16 minimum_groundlevel = (s16)get_sector_minimum_ground_level(
-			seed, v2s16(blockpos.X, blockpos.Z));*/
-	// Nah, this is just a heuristic, just return something
-	s16 minimum_groundlevel = WATER_LEVEL;
-
-	if(blockpos.Y*MAP_BLOCKSIZE + MAP_BLOCKSIZE <= minimum_groundlevel)
-		return true;
-	else
-		return false;
-}
-
-#define AVERAGE_MUD_AMOUNT 4
-
-double base_rock_level_2d(u64 seed, v2s16 p)
-{
-	// The base ground level
-	double base = (double)WATER_LEVEL - (double)AVERAGE_MUD_AMOUNT
-			+ 20. * noise2d_perlin(
-			0.5+(float)p.X/250., 0.5+(float)p.Y/250.,
-			seed+82341, 5, 0.6);
-
-	/*// A bit hillier one
-	double base2 = WATER_LEVEL - 4.0 + 40. * noise2d_perlin(
-			0.5+(float)p.X/250., 0.5+(float)p.Y/250.,
-			seed+93413, 6, 0.69);
-	if(base2 > base)
-		base = base2;*/
-#if 1
-	// Higher ground level
-	double higher = (double)WATER_LEVEL + 20. + 16. * noise2d_perlin(
-			0.5+(float)p.X/500., 0.5+(float)p.Y/500.,
-			seed+85039, 5, 0.6);
-	//higher = 30; // For debugging
-
-	// Limit higher to at least base
-	if(higher < base)
-		higher = base;
-
-	// Steepness factor of cliffs
-	double b = 0.85 + 0.5 * noise2d_perlin(
-			0.5+(float)p.X/125., 0.5+(float)p.Y/125.,
-			seed-932, 5, 0.7);
-	b = rangelim(b, 0.0, 1000.0);
-	b = b*b*b*b*b*b*b;
-	b *= 5;
-	b = rangelim(b, 0.5, 1000.0);
-	// Values 1.5...100 give quite horrible looking slopes
-	if(b > 1.5 && b < 100.0){
-		if(b < 10.0)
-			b = 1.5;
-		else
-			b = 100.0;
-	}
-	//dstream<<"b="<<b<<std::endl;
-	//double b = 20;
-	//b = 0.25;
-
-	// Offset to more low
-	double a_off = -0.20;
-	// High/low selector
-	/*double a = 0.5 + b * (a_off + noise2d_perlin(
-			0.5+(float)p.X/500., 0.5+(float)p.Y/500.,
-			seed+4213, 6, 0.7));*/
-	double a = (double)0.5 + b * (a_off + noise2d_perlin(
-			0.5+(float)p.X/250., 0.5+(float)p.Y/250.,
-			seed+4213, 5, 0.69));
-	// Limit
-	a = rangelim(a, 0.0, 1.0);
-
-	//dstream<<"a="<<a<<std::endl;
-
-	double h = base*(1.0-a) + higher*a;
-#else
-	double h = base;
-#endif
-	return h;
-}
-
-s16 find_ground_level_from_noise(u64 seed, v2s16 p2d, s16 precision)
-{
-	return base_rock_level_2d(seed, p2d) + AVERAGE_MUD_AMOUNT;
-}
-
-double get_mud_add_amount(u64 seed, v2s16 p)
-{
-	return ((float)AVERAGE_MUD_AMOUNT + 2.0 * noise2d_perlin(
-			0.5+(float)p.X/200, 0.5+(float)p.Y/200,
-			seed+91013, 3, 0.55));
-}
-
-bool get_have_beach(u64 seed, v2s16 p2d)
-{
-	// Determine whether to have sand here
-	double sandnoise = noise2d_perlin(
-			0.2+(float)p2d.X/250, 0.7+(float)p2d.Y/250,
-			seed+59420, 3, 0.50);
-
-	return (sandnoise > 0.15);
-}
-
-enum BiomeType
-{
-	BT_NORMAL,
-	BT_DESERT
-};
-
-BiomeType get_biome(u64 seed, v2s16 p2d)
-{
-	// Just do something very simple as for now
-	double d = noise2d_perlin(
-			0.6+(float)p2d.X/250, 0.2+(float)p2d.Y/250,
-			seed+9130, 3, 0.50);
-	if(d > 0.45)
-		return BT_DESERT;
-	if(d > 0.35 && (noise2d( p2d.X, p2d.Y, int(seed) ) + 1.0) > ( 0.45 - d ) * 20.0  )
-		return BT_DESERT;
-	return BT_NORMAL;
-};
-
-u32 get_blockseed(u64 seed, v3s16 p)
-{
-	s32 x=p.X, y=p.Y, z=p.Z;
-	return (u32)(seed%0x100000000ULL) + z*38134234 + y*42123 + x*23;
-}
-
-#define VMANIP_FLAG_CAVE VOXELFLAG_CHECKED1
-
-void make_block(BlockMakeData *data)
-{
-	if(data->no_op)
-	{
-		//dstream<<"makeBlock: no-op"<<std::endl;
-		return;
-	}
-
-	assert(data->vmanip);
-	assert(data->nodedef);
-	assert(data->blockpos_requested.X >= data->blockpos_min.X &&
-			data->blockpos_requested.Y >= data->blockpos_min.Y &&
-			data->blockpos_requested.Z >= data->blockpos_min.Z);
-	assert(data->blockpos_requested.X <= data->blockpos_max.X &&
-			data->blockpos_requested.Y <= data->blockpos_max.Y &&
-			data->blockpos_requested.Z <= data->blockpos_max.Z);
-
-	INodeDefManager *ndef = data->nodedef;
-
-	// Hack: use minimum block coordinates for old code that assumes
-	// a single block
-	v3s16 blockpos = data->blockpos_requested;
-
-	/*dstream<<"makeBlock(): ("<<blockpos.X<<","<<blockpos.Y<<","
-			<<blockpos.Z<<")"<<std::endl;*/
-
-	v3s16 blockpos_min = data->blockpos_min;
-	v3s16 blockpos_max = data->blockpos_max;
-	v3s16 blockpos_full_min = blockpos_min - v3s16(1,1,1);
-	v3s16 blockpos_full_max = blockpos_max + v3s16(1,1,1);
-
-	ManualMapVoxelManipulator &vmanip = *(data->vmanip);
-	// Area of central chunk
-	v3s16 node_min = blockpos_min*MAP_BLOCKSIZE;
-	v3s16 node_max = (blockpos_max+v3s16(1,1,1))*MAP_BLOCKSIZE-v3s16(1,1,1);
-	// Full allocated area
-	v3s16 full_node_min = (blockpos_min-1)*MAP_BLOCKSIZE;
-	v3s16 full_node_max = (blockpos_max+2)*MAP_BLOCKSIZE-v3s16(1,1,1);
-
-	v3s16 central_area_size = node_max - node_min + v3s16(1,1,1);
-
-	const s16 max_spread_amount = MAP_BLOCKSIZE;
-
-	int volume_blocks = (blockpos_max.X - blockpos_min.X + 1)
-			* (blockpos_max.Y - blockpos_min.Y + 1)
-			* (blockpos_max.Z - blockpos_max.Z + 1);
-
-	int volume_nodes = volume_blocks *
-			MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
-
-	// Generated surface area
-	//double gen_area_nodes = MAP_BLOCKSIZE*MAP_BLOCKSIZE * rel_volume;
-
-	// Horribly wrong heuristic, but better than nothing
-	bool block_is_underground = (WATER_LEVEL > node_max.Y);
-
-	/*
-		Create a block-specific seed
-	*/
-	u32 blockseed = get_blockseed(data->seed, full_node_min);
-
-	/*
-		Cache some ground type values for speed
-	*/
-
-// Creates variables c_name=id and n_name=node
-#define CONTENT_VARIABLE(ndef, name)\
-	content_t c_##name = ndef->getId("mapgen_" #name);\
-	MapNode n_##name(c_##name);
-// Default to something else if was CONTENT_IGNORE
-#define CONTENT_VARIABLE_FALLBACK(name, dname)\
-	if(c_##name == CONTENT_IGNORE){\
-		c_##name = c_##dname;\
-		n_##name = n_##dname;\
-	}
-
-	CONTENT_VARIABLE(ndef, stone);
-	CONTENT_VARIABLE(ndef, air);
-	CONTENT_VARIABLE(ndef, water_source);
-	CONTENT_VARIABLE(ndef, dirt);
-	CONTENT_VARIABLE(ndef, sand);
-	CONTENT_VARIABLE(ndef, gravel);
-	CONTENT_VARIABLE(ndef, clay);
-	CONTENT_VARIABLE(ndef, lava_source);
-	CONTENT_VARIABLE(ndef, cobble);
-	CONTENT_VARIABLE(ndef, mossycobble);
-	CONTENT_VARIABLE(ndef, dirt_with_grass);
-	CONTENT_VARIABLE(ndef, junglegrass);
-	CONTENT_VARIABLE(ndef, stone_with_coal);
-	CONTENT_VARIABLE(ndef, stone_with_iron);
-	CONTENT_VARIABLE(ndef, mese);
-	CONTENT_VARIABLE(ndef, desert_sand);
-	CONTENT_VARIABLE_FALLBACK(desert_sand, sand);
-	CONTENT_VARIABLE(ndef, desert_stone);
-	CONTENT_VARIABLE_FALLBACK(desert_stone, stone);
-
-	// Maximum height of the stone surface and obstacles.
-	// This is used to guide the cave generation
-	s16 stone_surface_max_y = 0;
-
-	/*
-		Generate general ground level to full area
-	*/
-	{
-#if 1
-	TimeTaker timer1("Generating ground level");
-
-	for(s16 x=node_min.X; x<=node_max.X; x++)
-	for(s16 z=node_min.Z; z<=node_max.Z; z++)
-	{
-		// Node position
-		v2s16 p2d = v2s16(x,z);
-
-		/*
-			Skip of already generated
-		*/
-		/*{
-			v3s16 p(p2d.X, node_min.Y, p2d.Y);
-			if(vmanip.m_data[vmanip.m_area.index(p)].d != CONTENT_AIR)
-				continue;
-		}*/
-
-		// Ground height at this point
-		float surface_y_f = 0.0;
-
-		// Use perlin noise for ground height
-		surface_y_f = base_rock_level_2d(data->seed, p2d);
-
-		/*// Experimental stuff
-		{
-			float a = highlands_level_2d(data->seed, p2d);
-			if(a > surface_y_f)
-				surface_y_f = a;
-		}*/
-
-		// Convert to integer
-		s16 surface_y = (s16)surface_y_f;
-
-		// Log it
-		if(surface_y > stone_surface_max_y)
-			stone_surface_max_y = surface_y;
-
-		BiomeType bt = get_biome(data->seed, p2d);
-		/*
-			Fill ground with stone
-		*/
-		{
-			// Use fast index incrementing
-			v3s16 em = vmanip.m_area.getExtent();
-			u32 i = vmanip.m_area.index(v3s16(p2d.X, node_min.Y, p2d.Y));
-			for(s16 y=node_min.Y; y<=node_max.Y; y++)
-			{
-				if(vmanip.m_data[i].getContent() == CONTENT_IGNORE){
-					if(y <= surface_y){
-						if(y > WATER_LEVEL && bt == BT_DESERT)
-							vmanip.m_data[i] = n_desert_stone;
-						else
-							vmanip.m_data[i] = n_stone;
-					} else if(y <= WATER_LEVEL){
-						vmanip.m_data[i] = MapNode(c_water_source);
-					} else {
-						vmanip.m_data[i] = MapNode(c_air);
-					}
-				}
-				vmanip.m_area.add_y(em, i, 1);
-			}
-		}
-	}
-#endif
-
-	}//timer1
-
-	// Limit dirt flow area by 1 because mud is flown into neighbors.
-	assert(central_area_size.X == central_area_size.Z);
-	s16 mudflow_minpos = 0-max_spread_amount+1;
-	s16 mudflow_maxpos = central_area_size.X+max_spread_amount-2;
-
-	/*
-		Loop this part, it will make stuff look older and newer nicely
-	*/
-
-	const u32 age_loops = 2;
-	for(u32 i_age=0; i_age<age_loops; i_age++)
-	{ // Aging loop
-	/******************************
-		BEGINNING OF AGING LOOP
-	******************************/
-
-#if 1
-	{
-	// 24ms @cs=8
-	//TimeTaker timer1("caves");
-
-	/*
-		Make caves (this code is relatively horrible)
-	*/
-	double cave_amount = 6.0 + 6.0 * noise2d_perlin(
-			0.5+(double)node_min.X/250, 0.5+(double)node_min.Y/250,
-			data->seed+34329, 3, 0.50);
-	cave_amount = MYMAX(0.0, cave_amount);
-	u32 caves_count = cave_amount * volume_nodes / 50000;
-	u32 bruises_count = 1;
-	PseudoRandom ps(blockseed+21343);
-	PseudoRandom ps2(blockseed+1032);
-	if(ps.range(1, 6) == 1)
-		bruises_count = ps.range(0, ps.range(0, 2));
-	if(get_biome(data->seed, v2s16(node_min.X, node_min.Y)) == BT_DESERT){
-		caves_count /= 3;
-		bruises_count /= 3;
-	}
-	for(u32 jj=0; jj<caves_count+bruises_count; jj++)
-	{
-		bool large_cave = (jj >= caves_count);
-		s16 min_tunnel_diameter = 2;
-		s16 max_tunnel_diameter = ps.range(2,6);
-		int dswitchint = ps.range(1,14);
-		u16 tunnel_routepoints = 0;
-		int part_max_length_rs = 0;
-		if(large_cave){
-			part_max_length_rs = ps.range(2,4);
-			tunnel_routepoints = ps.range(5, ps.range(15,30));
-			min_tunnel_diameter = 5;
-			max_tunnel_diameter = ps.range(7, ps.range(8,24));
-		} else {
-			part_max_length_rs = ps.range(2,9);
-			tunnel_routepoints = ps.range(10, ps.range(15,30));
-		}
-		bool large_cave_is_flat = (ps.range(0,1) == 0);
-
-		v3f main_direction(0,0,0);
-
-		// Allowed route area size in nodes
-		v3s16 ar = central_area_size;
-
-		// Area starting point in nodes
-		v3s16 of = node_min;
-
-		// Allow a bit more
-		//(this should be more than the maximum radius of the tunnel)
-		//s16 insure = 5; // Didn't work with max_d = 20
-		s16 insure = 10;
-		s16 more = max_spread_amount - max_tunnel_diameter/2 - insure;
-		ar += v3s16(1,0,1) * more * 2;
-		of -= v3s16(1,0,1) * more;
-
-		s16 route_y_min = 0;
-		// Allow half a diameter + 7 over stone surface
-		s16 route_y_max = -of.Y + stone_surface_max_y + max_tunnel_diameter/2 + 7;
-
-		/*// If caves, don't go through surface too often
-		if(large_cave == false)
-			route_y_max -= ps.range(0, max_tunnel_diameter*2);*/
-
-		// Limit maximum to area
-		route_y_max = rangelim(route_y_max, 0, ar.Y-1);
-
-		if(large_cave)
-		{
-			/*// Minimum is at y=0
-			route_y_min = -of.Y - 0;*/
-			// Minimum is at y=max_tunnel_diameter/4
-			//route_y_min = -of.Y + max_tunnel_diameter/4;
-			//s16 min = -of.Y + max_tunnel_diameter/4;
-			//s16 min = -of.Y + 0;
-			s16 min = 0;
-			if(node_min.Y < WATER_LEVEL && node_max.Y > WATER_LEVEL)
-			{
-				min = WATER_LEVEL - max_tunnel_diameter/3 - of.Y;
-				route_y_max = WATER_LEVEL + max_tunnel_diameter/3 - of.Y;
-			}
-			route_y_min = ps.range(min, min + max_tunnel_diameter);
-			route_y_min = rangelim(route_y_min, 0, route_y_max);
-		}
-
-		/*dstream<<"route_y_min = "<<route_y_min
-				<<", route_y_max = "<<route_y_max<<std::endl;*/
-
-		s16 route_start_y_min = route_y_min;
-		s16 route_start_y_max = route_y_max;
-
-		// Start every 4th cave from surface when applicable
-		/*bool coming_from_surface = false;
-		if(node_min.Y <= 0 && node_max.Y >= 0){
-			coming_from_surface = (jj % 4 == 0 && large_cave == false);
-			if(coming_from_surface)
-				route_start_y_min = -of.Y + stone_surface_max_y + 10;
-		}*/
-
-		route_start_y_min = rangelim(route_start_y_min, 0, ar.Y-1);
-		route_start_y_max = rangelim(route_start_y_max, route_start_y_min, ar.Y-1);
-
-		// Randomize starting position
-		v3f orp(
-			(float)(ps.next()%ar.X)+0.5,
-			(float)(ps.range(route_start_y_min, route_start_y_max))+0.5,
-			(float)(ps.next()%ar.Z)+0.5
-		);
-
-		v3s16 startp(orp.X, orp.Y, orp.Z);
-		startp += of;
-
-		MapNode airnode(CONTENT_AIR);
-		MapNode waternode(c_water_source);
-		MapNode lavanode(c_lava_source);
-
-		/*
-			Generate some tunnel starting from orp
-		*/
-
-		for(u16 j=0; j<tunnel_routepoints; j++)
-		{
-			if(j%dswitchint==0 && large_cave == false)
-			{
-				main_direction = v3f(
-					((float)(ps.next()%20)-(float)10)/10,
-					((float)(ps.next()%20)-(float)10)/30,
-					((float)(ps.next()%20)-(float)10)/10
-				);
-				main_direction *= (float)ps.range(0, 10)/10;
-			}
-
-			// Randomize size
-			s16 min_d = min_tunnel_diameter;
-			s16 max_d = max_tunnel_diameter;
-			s16 rs = ps.range(min_d, max_d);
-
-			// Every second section is rough
-			bool randomize_xz = (ps2.range(1,2) == 1);
-
-			v3s16 maxlen;
-			if(large_cave)
-			{
-				maxlen = v3s16(
-					rs*part_max_length_rs,
-					rs*part_max_length_rs/2,
-					rs*part_max_length_rs
-				);
-			}
-			else
-			{
-				maxlen = v3s16(
-					rs*part_max_length_rs,
-					ps.range(1, rs*part_max_length_rs),
-					rs*part_max_length_rs
-				);
-			}
-
-			v3f vec;
-
-			vec = v3f(
-				(float)(ps.next()%(maxlen.X*1))-(float)maxlen.X/2,
-				(float)(ps.next()%(maxlen.Y*1))-(float)maxlen.Y/2,
-				(float)(ps.next()%(maxlen.Z*1))-(float)maxlen.Z/2
-			);
-
-			// Jump downward sometimes
-			if(!large_cave && ps.range(0,12) == 0)
-			{
-				vec = v3f(
-					(float)(ps.next()%(maxlen.X*1))-(float)maxlen.X/2,
-					(float)(ps.next()%(maxlen.Y*2))-(float)maxlen.Y*2/2,
-					(float)(ps.next()%(maxlen.Z*1))-(float)maxlen.Z/2
-				);
-			}
-
-			/*if(large_cave){
-				v3f p = orp + vec;
-				s16 h = find_ground_level_clever(vmanip,
-						v2s16(p.X, p.Z), ndef);
-				route_y_min = h - rs/3;
-				route_y_max = h + rs;
-			}*/
-
-			vec += main_direction;
-
-			v3f rp = orp + vec;
-			if(rp.X < 0)
-				rp.X = 0;
-			else if(rp.X >= ar.X)
-				rp.X = ar.X-1;
-			if(rp.Y < route_y_min)
-				rp.Y = route_y_min;
-			else if(rp.Y >= route_y_max)
-				rp.Y = route_y_max-1;
-			if(rp.Z < 0)
-				rp.Z = 0;
-			else if(rp.Z >= ar.Z)
-				rp.Z = ar.Z-1;
-			vec = rp - orp;
-
-			for(float f=0; f<1.0; f+=1.0/vec.getLength())
-			{
-				v3f fp = orp + vec * f;
-				fp.X += 0.1*ps.range(-10,10);
-				fp.Z += 0.1*ps.range(-10,10);
-				v3s16 cp(fp.X, fp.Y, fp.Z);
-
-				s16 d0 = -rs/2;
-				s16 d1 = d0 + rs;
-				if(randomize_xz){
-					d0 += ps.range(-1,1);
-					d1 += ps.range(-1,1);
-				}
-				for(s16 z0=d0; z0<=d1; z0++)
-				{
-					s16 si = rs/2 - MYMAX(0, abs(z0)-rs/7-1);
-					for(s16 x0=-si-ps.range(0,1); x0<=si-1+ps.range(0,1); x0++)
-					{
-						s16 maxabsxz = MYMAX(abs(x0), abs(z0));
-						s16 si2 = rs/2 - MYMAX(0, maxabsxz-rs/7-1);
-						for(s16 y0=-si2; y0<=si2; y0++)
-						{
-							/*// Make better floors in small caves
-							if(y0 <= -rs/2 && rs<=7)
-								continue;*/
-							if(large_cave_is_flat){
-								// Make large caves not so tall
-								if(rs > 7 && abs(y0) >= rs/3)
-									continue;
-							}
-
-							s16 z = cp.Z + z0;
-							s16 y = cp.Y + y0;
-							s16 x = cp.X + x0;
-							v3s16 p(x,y,z);
-							p += of;
-
-							if(vmanip.m_area.contains(p) == false)
-								continue;
-
-							u32 i = vmanip.m_area.index(p);
-
-							if(large_cave)
-							{
-								if(full_node_min.Y < WATER_LEVEL &&
-									full_node_max.Y > WATER_LEVEL){
-									if(p.Y <= WATER_LEVEL)
-										vmanip.m_data[i] = waternode;
-									else
-										vmanip.m_data[i] = airnode;
-								} else if(full_node_max.Y < WATER_LEVEL){
-									if(p.Y < startp.Y - 2)
-										vmanip.m_data[i] = lavanode;
-									else
-										vmanip.m_data[i] = airnode;
-								} else {
-									vmanip.m_data[i] = airnode;
-								}
-							} else {
-								// Don't replace air or water or lava or ignore
-								if(vmanip.m_data[i].getContent() == CONTENT_IGNORE ||
-								vmanip.m_data[i].getContent() == CONTENT_AIR ||
-								vmanip.m_data[i].getContent() == c_water_source ||
-								vmanip.m_data[i].getContent() == c_lava_source)
-									continue;
-
-								vmanip.m_data[i] = airnode;
-
-								// Set tunnel flag
-								vmanip.m_flags[i] |= VMANIP_FLAG_CAVE;
-							}
-						}
-					}
-				}
-			}
-
-			orp = rp;
-		}
-
-	}
-
-	}//timer1
-#endif
-
-#if 1
-	{
-	// 15ms @cs=8
-	TimeTaker timer1("add mud");
-
-	/*
-		Add mud to the central chunk
-	*/
-
-	for(s16 x=node_min.X; x<=node_max.X; x++)
-	for(s16 z=node_min.Z; z<=node_max.Z; z++)
-	{
-		// Node position in 2d
-		v2s16 p2d = v2s16(x,z);
-
-		// Randomize mud amount
-		s16 mud_add_amount = get_mud_add_amount(data->seed, p2d) / 2.0 + 0.5;
-
-		// Find ground level
-		s16 surface_y = find_stone_level(vmanip, p2d, ndef);
-		// Handle area not found
-		if(surface_y == vmanip.m_area.MinEdge.Y - 1)
-			continue;
-
-		MapNode addnode(c_dirt);
-		BiomeType bt = get_biome(data->seed, p2d);
-
-		if(bt == BT_DESERT)
-			addnode = MapNode(c_desert_sand);
-
-		if(bt == BT_DESERT && surface_y + mud_add_amount <= WATER_LEVEL+1){
-			addnode = MapNode(c_sand);
-		} else if(mud_add_amount <= 0){
-			mud_add_amount = 1 - mud_add_amount;
-			addnode = MapNode(c_gravel);
-		} else if(bt == BT_NORMAL && get_have_beach(data->seed, p2d) &&
-				surface_y + mud_add_amount <= WATER_LEVEL+2){
-			addnode = MapNode(c_sand);
-		}
-
-		if(bt == BT_DESERT){
-			if(surface_y > 20){
-				mud_add_amount = MYMAX(0, mud_add_amount - (surface_y - 20)/5);
-			}
-		}
-
-		/*
-			If topmost node is grass, change it to mud.
-			It might be if it was flown to there from a neighboring
-			chunk and then converted.
-		*/
-		{
-			u32 i = vmanip.m_area.index(v3s16(p2d.X, surface_y, p2d.Y));
-			MapNode *n = &vmanip.m_data[i];
-			if(n->getContent() == c_dirt_with_grass)
-				*n = MapNode(c_dirt);
-		}
-
-		/*
-			Add mud on ground
-		*/
-		{
-			s16 mudcount = 0;
-			v3s16 em = vmanip.m_area.getExtent();
-			s16 y_start = surface_y+1;
-			u32 i = vmanip.m_area.index(v3s16(p2d.X, y_start, p2d.Y));
-			for(s16 y=y_start; y<=node_max.Y; y++)
-			{
-				if(mudcount >= mud_add_amount)
-					break;
-
-				MapNode &n = vmanip.m_data[i];
-				n = addnode;
-				mudcount++;
-
-				vmanip.m_area.add_y(em, i, 1);
-			}
-		}
-
-	}
-
-	}//timer1
-#endif
-
-	/*
-		Add blobs of dirt and gravel underground
-	*/
-	if(get_biome(data->seed, v2s16(node_min.X, node_min.Y)) == BT_NORMAL)
-	{
-	PseudoRandom pr(blockseed+983);
-	for(int i=0; i<volume_nodes/10/10/10; i++)
-	{
-		bool only_fill_cave = (myrand_range(0,1) != 0);
-		v3s16 size(
-			pr.range(1, 8),
-			pr.range(1, 8),
-			pr.range(1, 8)
-		);
-		v3s16 p0(
-			pr.range(node_min.X, node_max.X)-size.X/2,
-			pr.range(node_min.Y, node_max.Y)-size.Y/2,
-			pr.range(node_min.Z, node_max.Z)-size.Z/2
-		);
-		MapNode n1;
-		if(p0.Y > -32 && pr.range(0,1) == 0)
-			n1 = MapNode(c_dirt);
-		else
-			n1 = MapNode(c_gravel);
-		for(int x1=0; x1<size.X; x1++)
-		for(int y1=0; y1<size.Y; y1++)
-		for(int z1=0; z1<size.Z; z1++)
-		{
-			v3s16 p = p0 + v3s16(x1,y1,z1);
-			u32 i = vmanip.m_area.index(p);
-			if(!vmanip.m_area.contains(i))
-				continue;
-			// Cancel if not stone and not cave air
-			if(vmanip.m_data[i].getContent() != c_stone &&
-					!(vmanip.m_flags[i] & VMANIP_FLAG_CAVE))
-				continue;
-			if(only_fill_cave && !(vmanip.m_flags[i] & VMANIP_FLAG_CAVE))
-				continue;
-			vmanip.m_data[i] = n1;
-		}
-	}
-	}
-
-#if 1
-	{
-	// 340ms @cs=8
-	TimeTaker timer1("flow mud");
-
-	/*
-		Flow mud away from steep edges
-	*/
-
-	// Iterate a few times
-	for(s16 k=0; k<3; k++)
-	{
-
-	for(s16 x=mudflow_minpos; x<=mudflow_maxpos; x++)
-	for(s16 z=mudflow_minpos; z<=mudflow_maxpos; z++)
-	{
-		// Invert coordinates every 2nd iteration
-		if(k%2 == 0)
-		{
-			x = mudflow_maxpos - (x-mudflow_minpos);
-			z = mudflow_maxpos - (z-mudflow_minpos);
-		}
-
-		// Node position in 2d
-		v2s16 p2d = v2s16(node_min.X, node_min.Z) + v2s16(x,z);
-
-		v3s16 em = vmanip.m_area.getExtent();
-		u32 i = vmanip.m_area.index(v3s16(p2d.X, node_max.Y, p2d.Y));
-		s16 y=node_max.Y;
-
-		while(y >= node_min.Y)
-		{
-
-		for(;; y--)
-		{
-			MapNode *n = NULL;
-			// Find mud
-			for(; y>=node_min.Y; y--)
-			{
-				n = &vmanip.m_data[i];
-				//if(content_walkable(n->d))
-				//	break;
-				if(n->getContent() == c_dirt ||
-						n->getContent() == c_dirt_with_grass ||
-						n->getContent() == c_gravel)
-					break;
-
-				vmanip.m_area.add_y(em, i, -1);
-			}
-
-			// Stop if out of area
-			//if(vmanip.m_area.contains(i) == false)
-			if(y < node_min.Y)
-				break;
-
-			/*// If not mud, do nothing to it
-			MapNode *n = &vmanip.m_data[i];
-			if(n->d != CONTENT_MUD && n->d != CONTENT_GRASS)
-				continue;*/
-
-			if(n->getContent() == c_dirt ||
-					n->getContent() == c_dirt_with_grass)
-			{
-				// Make it exactly mud
-				n->setContent(c_dirt);
-
-				/*
-					Don't flow it if the stuff under it is not mud
-				*/
-				{
-					u32 i2 = i;
-					vmanip.m_area.add_y(em, i2, -1);
-					// Cancel if out of area
-					if(vmanip.m_area.contains(i2) == false)
-						continue;
-					MapNode *n2 = &vmanip.m_data[i2];
-					if(n2->getContent() != c_dirt &&
-							n2->getContent() != c_dirt_with_grass)
-						continue;
-				}
-			}
-
-			/*s16 recurse_count = 0;
-	mudflow_recurse:*/
-
-			v3s16 dirs4[4] = {
-				v3s16(0,0,1), // back
-				v3s16(1,0,0), // right
-				v3s16(0,0,-1), // front
-				v3s16(-1,0,0), // left
-			};
-
-			// Theck that upper is air or doesn't exist.
-			// Cancel dropping if upper keeps it in place
-			u32 i3 = i;
-			vmanip.m_area.add_y(em, i3, 1);
-			if(vmanip.m_area.contains(i3) == true
-					&& ndef->get(vmanip.m_data[i3]).walkable)
-			{
-				continue;
-			}
-
-			// Drop mud on side
-
-			for(u32 di=0; di<4; di++)
-			{
-				v3s16 dirp = dirs4[di];
-				u32 i2 = i;
-				// Move to side
-				vmanip.m_area.add_p(em, i2, dirp);
-				// Fail if out of area
-				if(vmanip.m_area.contains(i2) == false)
-					continue;
-				// Check that side is air
-				MapNode *n2 = &vmanip.m_data[i2];
-				if(ndef->get(*n2).walkable)
-					continue;
-				// Check that under side is air
-				vmanip.m_area.add_y(em, i2, -1);
-				if(vmanip.m_area.contains(i2) == false)
-					continue;
-				n2 = &vmanip.m_data[i2];
-				if(ndef->get(*n2).walkable)
-					continue;
-				/*// Check that under that is air (need a drop of 2)
-				vmanip.m_area.add_y(em, i2, -1);
-				if(vmanip.m_area.contains(i2) == false)
-					continue;
-				n2 = &vmanip.m_data[i2];
-				if(content_walkable(n2->d))
-					continue;*/
-				// Loop further down until not air
-				bool dropped_to_unknown = false;
-				do{
-					vmanip.m_area.add_y(em, i2, -1);
-					n2 = &vmanip.m_data[i2];
-					// if out of known area
-					if(vmanip.m_area.contains(i2) == false
-							|| n2->getContent() == CONTENT_IGNORE){
-						dropped_to_unknown = true;
-						break;
-					}
-				}while(ndef->get(*n2).walkable == false);
-				// Loop one up so that we're in air
-				vmanip.m_area.add_y(em, i2, 1);
-				n2 = &vmanip.m_data[i2];
-
-				bool old_is_water = (n->getContent() == c_water_source);
-				// Move mud to new place
-				if(!dropped_to_unknown) {
-					*n2 = *n;
-					// Set old place to be air (or water)
-					if(old_is_water)
-						*n = MapNode(c_water_source);
-					else
-						*n = MapNode(CONTENT_AIR);
-				}
-
-				// Done
-				break;
-			}
-		}
-		}
-	}
-
-	}
-
-	}//timer1
-#endif
-
-	} // Aging loop
-	/***********************
-		END OF AGING LOOP
-	************************/
-
-	/*
-		Add top and bottom side of water to transforming_liquid queue
-	*/
-
-	for(s16 x=full_node_min.X; x<=full_node_max.X; x++)
-	for(s16 z=full_node_min.Z; z<=full_node_max.Z; z++)
-	{
-		// Node position
-		v2s16 p2d(x,z);
-		{
-			bool water_found = false;
-			// Use fast index incrementing
-			v3s16 em = vmanip.m_area.getExtent();
-			u32 i = vmanip.m_area.index(v3s16(p2d.X, full_node_max.Y, p2d.Y));
-			for(s16 y=full_node_max.Y; y>=full_node_min.Y; y--)
-			{
-				if(y == full_node_max.Y){
-					water_found =
-						(vmanip.m_data[i].getContent() == c_water_source ||
-						vmanip.m_data[i].getContent() == c_lava_source);
-				}
-				else if(water_found == false)
-				{
-					if(vmanip.m_data[i].getContent() == c_water_source ||
-							vmanip.m_data[i].getContent() == c_lava_source)
-					{
-						v3s16 p = v3s16(p2d.X, y, p2d.Y);
-						data->transforming_liquid.push_back(p);
-						water_found = true;
-					}
-				}
-				else
-				{
-					// This can be done because water_found can only
-					// turn to true and end up here after going through
-					// a single block.
-					if(vmanip.m_data[i+1].getContent() != c_water_source ||
-							vmanip.m_data[i+1].getContent() != c_lava_source)
-					{
-						v3s16 p = v3s16(p2d.X, y+1, p2d.Y);
-						data->transforming_liquid.push_back(p);
-						water_found = false;
-					}
-				}
-
-				vmanip.m_area.add_y(em, i, -1);
-			}
-		}
-	}
-
-	/*
-		Grow grass
-	*/
-
-	for(s16 x=full_node_min.X; x<=full_node_max.X; x++)
-	for(s16 z=full_node_min.Z; z<=full_node_max.Z; z++)
-	{
-		// Node position in 2d
-		v2s16 p2d = v2s16(x,z);
-
-		/*
-			Find the lowest surface to which enough light ends up
-			to make grass grow.
-
-			Basically just wait until not air and not leaves.
-		*/
-		s16 surface_y = 0;
-		{
-			v3s16 em = vmanip.m_area.getExtent();
-			u32 i = vmanip.m_area.index(v3s16(p2d.X, node_max.Y, p2d.Y));
-			s16 y;
-			// Go to ground level
-			for(y=node_max.Y; y>=full_node_min.Y; y--)
-			{
-				MapNode &n = vmanip.m_data[i];
-				if(ndef->get(n).param_type != CPT_LIGHT
-						|| ndef->get(n).liquid_type != LIQUID_NONE)
-					break;
-				vmanip.m_area.add_y(em, i, -1);
-			}
-			if(y >= full_node_min.Y)
-				surface_y = y;
-			else
-				surface_y = full_node_min.Y;
-		}
-
-		u32 i = vmanip.m_area.index(p2d.X, surface_y, p2d.Y);
-		MapNode *n = &vmanip.m_data[i];
-		if(n->getContent() == c_dirt){
-			// Well yeah, this can't be overground...
-			if(surface_y < WATER_LEVEL - 20)
-				continue;
-			n->setContent(c_dirt_with_grass);
-		}
-	}
-
-	/*
-		Generate some trees
-	*/
-	assert(central_area_size.X == central_area_size.Z);
-	{
-		PseudoRandom ps (blockseed);
-		// Divide area into parts
-		s16 div = 8;
-		s16 sidelen = central_area_size.X / div;
-		double area = sidelen * sidelen;
-		for(s16 x0=0; x0<div; x0++)
-		for(s16 z0=0; z0<div; z0++)
-		{
-			// Center position of part of division
-			v2s16 p2d_center(
-				node_min.X + sidelen/2 + sidelen*x0,
-				node_min.Z + sidelen/2 + sidelen*z0
-			);
-			// Minimum edge of part of division
-			v2s16 p2d_min(
-				node_min.X + sidelen*x0,
-				node_min.Z + sidelen*z0
-			);
-			// Maximum edge of part of division
-			v2s16 p2d_max(
-				node_min.X + sidelen + sidelen*x0 - 1,
-				node_min.Z + sidelen + sidelen*z0 - 1
-			);
-			// Amount of trees
-			u32 tree_count = area * tree_amount_2d(data->seed, p2d_center);
-			// Put trees in random places on part of division
-			for(u32 i=0; i<tree_count; i++)
-			{
-				s16 x = ps.range(p2d_min.X, p2d_max.X);
-				s16 z = ps.range(p2d_min.Y, p2d_max.Y);
-				s16 y = find_ground_level(vmanip, v2s16(x,z), ndef);
-				// Don't make a tree under water level
-				if(y < WATER_LEVEL)
-					continue;
-				// Don't make a tree so high that it doesn't fit
-				if(y > node_max.Y - 6)
-					continue;
-				v3s16 p(x,y,z);
-				/*
-					Trees grow only on mud and grass
-				*/
-				{
-					u32 i = vmanip.m_area.index(v3s16(p));
-					MapNode *n = &vmanip.m_data[i];
-					if(n->getContent() != c_dirt
-							&& n->getContent() != c_dirt_with_grass)
-						continue;
-				}
-				p.Y++;
-				// Make a tree
-				treegen::make_tree(vmanip, p, false, ndef, ps.next());
-			}
-		}
-	}
-
-#if 0
-	/*
-		Make base ground level
-	*/
-
-	for(s16 x=node_min.X; x<=node_max.X; x++)
-	for(s16 z=node_min.Z; z<=node_max.Z; z++)
-	{
-		// Node position
-		v2s16 p2d(x,z);
-		{
-			// Use fast index incrementing
-			v3s16 em = vmanip.m_area.getExtent();
-			u32 i = vmanip.m_area.index(v3s16(p2d.X, node_min.Y, p2d.Y));
-			for(s16 y=node_min.Y; y<=node_max.Y; y++)
-			{
-				// Only modify places that have no content
-				if(vmanip.m_data[i].getContent() == CONTENT_IGNORE)
-				{
-					// First priority: make air and water.
-					// This avoids caves inside water.
-					if(all_is_ground_except_caves == false
-							&& val_is_ground(noisebuf_ground.get(x,y,z),
-							v3s16(x,y,z), data->seed) == false)
-					{
-						if(y <= WATER_LEVEL)
-							vmanip.m_data[i] = n_water_source;
-						else
-							vmanip.m_data[i] = n_air;
-					}
-					else if(noisebuf_cave.get(x,y,z) > CAVE_NOISE_THRESHOLD)
-						vmanip.m_data[i] = n_air;
-					else
-						vmanip.m_data[i] = n_stone;
-				}
-
-				vmanip->m_area.add_y(em, i, 1);
-			}
-		}
-	}
-
-	/*
-		Add mud and sand and others underground (in place of stone)
-	*/
-
-	for(s16 x=node_min.X; x<=node_max.X; x++)
-	for(s16 z=node_min.Z; z<=node_max.Z; z++)
-	{
-		// Node position
-		v2s16 p2d(x,z);
-		{
-			// Use fast index incrementing
-			v3s16 em = vmanip.m_area.getExtent();
-			u32 i = vmanip.m_area.index(v3s16(p2d.X, node_max.Y, p2d.Y));
-			for(s16 y=node_max.Y; y>=node_min.Y; y--)
-			{
-				if(vmanip.m_data[i].getContent() == c_stone)
-				{
-					if(noisebuf_ground_crumbleness.get(x,y,z) > 1.3)
-					{
-						if(noisebuf_ground_wetness.get(x,y,z) > 0.0)
-							vmanip.m_data[i] = n_dirt;
-						else
-							vmanip.m_data[i] = n_sand;
-					}
-					else if(noisebuf_ground_crumbleness.get(x,y,z) > 0.7)
-					{
-						if(noisebuf_ground_wetness.get(x,y,z) < -0.6)
-							vmanip.m_data[i] = n_gravel;
-					}
-					else if(noisebuf_ground_crumbleness.get(x,y,z) <
-							-3.0 + MYMIN(0.1 * sqrt((float)MYMAX(0, -y)), 1.5))
-					{
-						vmanip.m_data[i] = n_lava_source;
-						for(s16 x1=-1; x1<=1; x1++)
-						for(s16 y1=-1; y1<=1; y1++)
-						for(s16 z1=-1; z1<=1; z1++)
-							data->transforming_liquid.push_back(
-									v3s16(p2d.X+x1, y+y1, p2d.Y+z1));
-					}
-				}
-
-				vmanip->m_area.add_y(em, i, -1);
-			}
-		}
-	}
-
-	/*
-		Add dungeons
-	*/
-
-	//if(node_min.Y < approx_groundlevel)
-	//if(myrand() % 3 == 0)
-	//if(myrand() % 3 == 0 && node_min.Y < approx_groundlevel)
-	//if(myrand() % 100 == 0 && node_min.Y < approx_groundlevel)
-	//float dungeon_rarity = g_settings.getFloat("dungeon_rarity");
-	float dungeon_rarity = 0.02;
-	if(((noise3d(blockpos.X,blockpos.Y,blockpos.Z,data->seed)+1.0)/2.0)
-			< dungeon_rarity
-			&& node_min.Y < approx_groundlevel)
-	{
-		// Dungeon generator doesn't modify places which have this set
-		vmanip->clearFlag(VMANIP_FLAG_DUNGEON_INSIDE
-				| VMANIP_FLAG_DUNGEON_PRESERVE);
-
-		// Set all air and water to be untouchable to make dungeons open
-		// to caves and open air
-		for(s16 x=full_node_min.X; x<=full_node_max.X; x++)
-		for(s16 z=full_node_min.Z; z<=full_node_max.Z; z++)
-		{
-			// Node position
-			v2s16 p2d(x,z);
-			{
-				// Use fast index incrementing
-				v3s16 em = vmanip.m_area.getExtent();
-				u32 i = vmanip.m_area.index(v3s16(p2d.X, full_node_max.Y, p2d.Y));
-				for(s16 y=full_node_max.Y; y>=full_node_min.Y; y--)
-				{
-					if(vmanip.m_data[i].getContent() == CONTENT_AIR)
-						vmanip.m_flags[i] |= VMANIP_FLAG_DUNGEON_PRESERVE;
-					else if(vmanip.m_data[i].getContent() == c_water_source)
-						vmanip.m_flags[i] |= VMANIP_FLAG_DUNGEON_PRESERVE;
-					vmanip->m_area.add_y(em, i, -1);
-				}
-			}
-		}
-
-		PseudoRandom random(blockseed+2);
-
-		// Add it
-		make_dungeon1(vmanip, random, ndef);
-
-		// Convert some cobble to mossy cobble
-		for(s16 x=full_node_min.X; x<=full_node_max.X; x++)
-		for(s16 z=full_node_min.Z; z<=full_node_max.Z; z++)
-		{
-			// Node position
-			v2s16 p2d(x,z);
-			{
-				// Use fast index incrementing
-				v3s16 em = vmanip.m_area.getExtent();
-				u32 i = vmanip.m_area.index(v3s16(p2d.X, full_node_max.Y, p2d.Y));
-				for(s16 y=full_node_max.Y; y>=full_node_min.Y; y--)
-				{
-					// (noisebuf not used because it doesn't contain the
-					//  full area)
-					double wetness = noise3d_param(
-							get_ground_wetness_params(data->seed), x,y,z);
-					double d = noise3d_perlin((float)x/2.5,
-							(float)y/2.5,(float)z/2.5,
-							blockseed, 2, 1.4);
-					if(vmanip.m_data[i].getContent() == c_cobble)
-					{
-						if(d < wetness/3.0)
-						{
-							vmanip.m_data[i].setContent(c_mossycobble);
-						}
-					}
-					/*else if(vmanip.m_flags[i] & VMANIP_FLAG_DUNGEON_INSIDE)
-					{
-						if(wetness > 1.2)
-							vmanip.m_data[i].setContent(c_dirt);
-					}*/
-					vmanip->m_area.add_y(em, i, -1);
-				}
-			}
-		}
-	}
-
-	/*
-		Add NC
-	*/
-	{
-		PseudoRandom ncrandom(blockseed+9324342);
-		if(ncrandom.range(0, 1000) == 0 && blockpos.Y <= -3)
-		{
-			make_nc(vmanip, ncrandom, ndef);
-		}
-	}
-
-	/*
-		Add top and bottom side of water to transforming_liquid queue
-	*/
-
-	for(s16 x=node_min.X; x<=node_max.X; x++)
-	for(s16 z=node_min.Z; z<=node_max.Z; z++)
-	{
-		// Node position
-		v2s16 p2d(x,z);
-		{
-			bool water_found = false;
-			// Use fast index incrementing
-			v3s16 em = vmanip.m_area.getExtent();
-			u32 i = vmanip.m_area.index(v3s16(p2d.X, node_max.Y, p2d.Y));
-			for(s16 y=node_max.Y; y>=node_min.Y; y--)
-			{
-				if(water_found == false)
-				{
-					if(vmanip.m_data[i].getContent() == c_water_source)
-					{
-						v3s16 p = v3s16(p2d.X, y, p2d.Y);
-						data->transforming_liquid.push_back(p);
-						water_found = true;
-					}
-				}
-				else
-				{
-					// This can be done because water_found can only
-					// turn to true and end up here after going through
-					// a single block.
-					if(vmanip.m_data[i+1].getContent() != c_water_source)
-					{
-						v3s16 p = v3s16(p2d.X, y+1, p2d.Y);
-						data->transforming_liquid.push_back(p);
-						water_found = false;
-					}
-				}
-
-				vmanip->m_area.add_y(em, i, -1);
-			}
-		}
-	}
-
-	/*
-		If close to ground level
-	*/
-
-	//if(abs(approx_ground_depth) < 30)
-	if(minimum_ground_depth < 5 && maximum_ground_depth > -5)
-	{
-		/*
-			Add grass and mud
-		*/
-
-		for(s16 x=node_min.X; x<=node_max.X; x++)
-		for(s16 z=node_min.Z; z<=node_max.Z; z++)
-		{
-			// Node position
-			v2s16 p2d(x,z);
-			{
-				bool possibly_have_sand = get_have_beach(data->seed, p2d);
-				bool have_sand = false;
-				u32 current_depth = 0;
-				bool air_detected = false;
-				bool water_detected = false;
-				bool have_clay = false;
-
-				// Use fast index incrementing
-				s16 start_y = node_max.Y+2;
-				v3s16 em = vmanip.m_area.getExtent();
-				u32 i = vmanip.m_area.index(v3s16(p2d.X, start_y, p2d.Y));
-				for(s16 y=start_y; y>=node_min.Y-3; y--)
-				{
-					if(vmanip.m_data[i].getContent() == c_water_source)
-						water_detected = true;
-					if(vmanip.m_data[i].getContent() == CONTENT_AIR)
-						air_detected = true;
-
-					if((vmanip.m_data[i].getContent() == c_stone
-							|| vmanip.m_data[i].getContent() == c_dirt_with_grass
-							|| vmanip.m_data[i].getContent() == c_dirt
-							|| vmanip.m_data[i].getContent() == c_sand
-							|| vmanip.m_data[i].getContent() == c_gravel
-							) && (air_detected || water_detected))
-					{
-						if(current_depth == 0 && y <= WATER_LEVEL+2
-								&& possibly_have_sand)
-							have_sand = true;
-
-						if(current_depth < 4)
-						{
-							if(have_sand)
-							{
-								vmanip.m_data[i] = MapNode(c_sand);
-							}
-							#if 1
-							else if(current_depth==0 && !water_detected
-									&& y >= WATER_LEVEL && air_detected)
-								vmanip.m_data[i] = MapNode(c_dirt_with_grass);
-							#endif
-							else
-								vmanip.m_data[i] = MapNode(c_dirt);
-						}
-						else
-						{
-							if(vmanip.m_data[i].getContent() == c_dirt
-								|| vmanip.m_data[i].getContent() == c_dirt_with_grass)
-								vmanip.m_data[i] = MapNode(c_stone);
-						}
-
-						current_depth++;
-
-						if(current_depth >= 8)
-							break;
-					}
-					else if(current_depth != 0)
-						break;
-
-					vmanip->m_area.add_y(em, i, -1);
-				}
-			}
-		}
-
-		/*
-			Calculate some stuff
-		*/
-
-		float surface_humidity = surface_humidity_2d(data->seed, p2d_center);
-		bool is_jungle = surface_humidity > 0.75;
-		// Amount of trees
-		u32 tree_count = gen_area_nodes * tree_amount_2d(data->seed, p2d_center);
-		if(is_jungle)
-			tree_count *= 5;
-
-		/*
-			Add trees
-		*/
-		PseudoRandom treerandom(blockseed);
-		// Put trees in random places on part of division
-		for(u32 i=0; i<tree_count; i++)
-		{
-			s16 x = treerandom.range(node_min.X, node_max.X);
-			s16 z = treerandom.range(node_min.Z, node_max.Z);
-			//s16 y = find_ground_level(vmanip, v2s16(x,z));
-			s16 y = find_ground_level_from_noise(data->seed, v2s16(x,z), 4);
-			// Don't make a tree under water level
-			if(y < WATER_LEVEL)
-				continue;
-			// Make sure tree fits (only trees whose starting point is
-			// at this block are added)
-			if(y < node_min.Y || y > node_max.Y)
-				continue;
-			/*
-				Find exact ground level
-			*/
-			v3s16 p(x,y+6,z);
-			bool found = false;
-			for(; p.Y >= y-6; p.Y--)
-			{
-				u32 i = vmanip->m_area.index(p);
-				MapNode *n = &vmanip->m_data[i];
-				if(n->getContent() != CONTENT_AIR && n->getContent() != c_water_source && n->getContent() != CONTENT_IGNORE)
-				{
-					found = true;
-					break;
-				}
-			}
-			// If not found, handle next one
-			if(found == false)
-				continue;
-
-			{
-				u32 i = vmanip->m_area.index(p);
-				MapNode *n = &vmanip->m_data[i];
-
-				if(n->getContent() != c_dirt && n->getContent() != c_dirt_with_grass && n->getContent() != c_sand)
-						continue;
-
-				// Papyrus grows only on mud and in water
-				if(n->getContent() == c_dirt && y <= WATER_LEVEL)
-				{
-					p.Y++;
-					make_papyrus(vmanip, p, ndef);
-				}
-				// Trees grow only on mud and grass, on land
-				else if((n->getContent() == c_dirt || n->getContent() == c_dirt_with_grass) && y > WATER_LEVEL + 2)
-				{
-					p.Y++;
-					//if(surface_humidity_2d(data->seed, v2s16(x, y)) < 0.5)
-					if(is_jungle == false)
-					{
-						bool is_apple_tree;
-						if(myrand_range(0,4) != 0)
-							is_apple_tree = false;
-						else
-							is_apple_tree = noise2d_perlin(
-									0.5+(float)p.X/100, 0.5+(float)p.Z/100,
-									data->seed+342902, 3, 0.45) > 0.2;
-						make_tree(vmanip, p, is_apple_tree, ndef);
-					}
-					else
-						make_jungletree(vmanip, p, ndef);
-				}
-				// Cactii grow only on sand, on land
-				else if(n->getContent() == c_sand && y > WATER_LEVEL + 2)
-				{
-					p.Y++;
-					make_cactus(vmanip, p, ndef);
-				}
-			}
-		}
-
-		/*
-			Add jungle grass
-		*/
-		if(is_jungle)
-		{
-			PseudoRandom grassrandom(blockseed);
-			for(u32 i=0; i<surface_humidity*5*tree_count; i++)
-			{
-				s16 x = grassrandom.range(node_min.X, node_max.X);
-				s16 z = grassrandom.range(node_min.Z, node_max.Z);
-				s16 y = find_ground_level_from_noise(data->seed, v2s16(x,z), 4);
-				if(y < WATER_LEVEL)
-					continue;
-				if(y < node_min.Y || y > node_max.Y)
-					continue;
-				/*
-					Find exact ground level
-				*/
-				v3s16 p(x,y+6,z);
-				bool found = false;
-				for(; p.Y >= y-6; p.Y--)
-				{
-					u32 i = vmanip->m_area.index(p);
-					MapNode *n = &vmanip->m_data[i];
-					if(data->nodedef->get(*n).is_ground_content)
-					{
-						found = true;
-						break;
-					}
-				}
-				// If not found, handle next one
-				if(found == false)
-					continue;
-				p.Y++;
-				if(vmanip.m_area.contains(p) == false)
-					continue;
-				if(vmanip.m_data[vmanip.m_area.index(p)].getContent() != CONTENT_AIR)
-					continue;
-				/*p.Y--;
-				if(vmanip.m_area.contains(p))
-					vmanip.m_data[vmanip.m_area.index(p)] = c_dirt;
-				p.Y++;*/
-				if(vmanip.m_area.contains(p))
-					vmanip.m_data[vmanip.m_area.index(p)] = c_junglegrass;
-			}
-		}
-
-#if 0
-		/*
-			Add some kind of random stones
-		*/
-
-		u32 random_stone_count = gen_area_nodes *
-				randomstone_amount_2d(data->seed, p2d_center);
-		// Put in random places on part of division
-		for(u32 i=0; i<random_stone_count; i++)
-		{
-			s16 x = myrand_range(node_min.X, node_max.X);
-			s16 z = myrand_range(node_min.Z, node_max.Z);
-			s16 y = find_ground_level_from_noise(data->seed, v2s16(x,z), 1);
-			// Don't add under water level
-			/*if(y < WATER_LEVEL)
-				continue;*/
-			// Don't add if doesn't belong to this block
-			if(y < node_min.Y || y > node_max.Y)
-				continue;
-			v3s16 p(x,y,z);
-			// Filter placement
-			/*{
-				u32 i = vmanip->m_area.index(v3s16(p));
-				MapNode *n = &vmanip->m_data[i];
-				if(n->getContent() != c_dirt && n->getContent() != c_dirt_with_grass)
-					continue;
-			}*/
-			// Will be placed one higher
-			p.Y++;
-			// Add it
-			make_randomstone(vmanip, p);
-		}
-#endif
-
-#if 0
-		/*
-			Add larger stones
-		*/
-
-		u32 large_stone_count = gen_area_nodes *
-				largestone_amount_2d(data->seed, p2d_center);
-		//u32 large_stone_count = 1;
-		// Put in random places on part of division
-		for(u32 i=0; i<large_stone_count; i++)
-		{
-			s16 x = myrand_range(node_min.X, node_max.X);
-			s16 z = myrand_range(node_min.Z, node_max.Z);
-			s16 y = find_ground_level_from_noise(data->seed, v2s16(x,z), 1);
-			// Don't add under water level
-			/*if(y < WATER_LEVEL)
-				continue;*/
-			// Don't add if doesn't belong to this block
-			if(y < node_min.Y || y > node_max.Y)
-				continue;
-			v3s16 p(x,y,z);
-			// Filter placement
-			/*{
-				u32 i = vmanip->m_area.index(v3s16(p));
-				MapNode *n = &vmanip->m_data[i];
-				if(n->getContent() != c_dirt && n->getContent() != c_dirt_with_grass)
-					continue;
-			}*/
-			// Will be placed one lower
-			p.Y--;
-			// Add it
-			make_largestone(vmanip, p);
-		}
-#endif
-	}
-
-	/*
-		Add minerals
-	*/
-
-	{
-		PseudoRandom mineralrandom(blockseed);
-
-		/*
-			Add meseblocks
-		*/
-		for(s16 i=0; i<approx_ground_depth/4; i++)
-		{
-			if(mineralrandom.next()%50 == 0)
-			{
-				s16 x = mineralrandom.range(node_min.X+1, node_max.X-1);
-				s16 y = mineralrandom.range(node_min.Y+1, node_max.Y-1);
-				s16 z = mineralrandom.range(node_min.Z+1, node_max.Z-1);
-				for(u16 i=0; i<27; i++)
-				{
-					v3s16 p = v3s16(x,y,z) + g_27dirs[i];
-					u32 vi = vmanip.m_area.index(p);
-					if(vmanip.m_data[vi].getContent() == c_stone)
-						if(mineralrandom.next()%8 == 0)
-							vmanip.m_data[vi] = MapNode(c_mese);
-				}
-
-			}
-		}
-		/*
-			Add others
-		*/
-		{
-			u16 a = mineralrandom.range(0,15);
-			a = a*a*a;
-			u16 amount = 20 * a/1000;
-			for(s16 i=0; i<amount; i++)
-			{
-				s16 x = mineralrandom.range(node_min.X+1, node_max.X-1);
-				s16 y = mineralrandom.range(node_min.Y+1, node_max.Y-1);
-				s16 z = mineralrandom.range(node_min.Z+1, node_max.Z-1);
-
-				u8 base_content = c_stone;
-				MapNode new_content(CONTENT_IGNORE);
-				u32 sparseness = 6;
-
-				if(noisebuf_ground_crumbleness.get(x,y+5,z) < -0.1)
-				{
-					new_content = MapNode(c_stone_with_coal);
-				}
-				else
-				{
-					if(noisebuf_ground_wetness.get(x,y+5,z) > 0.0)
-						new_content = MapNode(c_stone_with_iron);
-					/*if(noisebuf_ground_wetness.get(x,y,z) > 0.0)
-						vmanip.m_data[i] = MapNode(c_dirt);
-					else
-						vmanip.m_data[i] = MapNode(c_sand);*/
-				}
-				/*else if(noisebuf_ground_crumbleness.get(x,y,z) > 0.1)
-				{
-				}*/
-
-				if(new_content.getContent() != CONTENT_IGNORE)
-				{
-					for(u16 i=0; i<27; i++)
-					{
-						v3s16 p = v3s16(x,y,z) + g_27dirs[i];
-						u32 vi = vmanip.m_area.index(p);
-						if(vmanip.m_data[vi].getContent() == base_content)
-						{
-							if(mineralrandom.next()%sparseness == 0)
-								vmanip.m_data[vi] = new_content;
-						}
-					}
-				}
-			}
-		}
-		/*
-			Add coal
-		*/
-		//for(s16 i=0; i < MYMAX(0, 50 - abs(node_min.Y+8 - (-30))); i++)
-		//for(s16 i=0; i<50; i++)
-		u16 coal_amount = 30;
-		u16 coal_rareness = 60 / coal_amount;
-		if(coal_rareness == 0)
-			coal_rareness = 1;
-		if(mineralrandom.next()%coal_rareness == 0)
-		{
-			u16 a = mineralrandom.next() % 16;
-			u16 amount = coal_amount * a*a*a / 1000;
-			for(s16 i=0; i<amount; i++)
-			{
-				s16 x = mineralrandom.range(node_min.X+1, node_max.X-1);
-				s16 y = mineralrandom.range(node_min.Y+1, node_max.Y-1);
-				s16 z = mineralrandom.range(node_min.Z+1, node_max.Z-1);
-				for(u16 i=0; i<27; i++)
-				{
-					v3s16 p = v3s16(x,y,z) + g_27dirs[i];
-					u32 vi = vmanip.m_area.index(p);
-					if(vmanip.m_data[vi].getContent() == c_stone)
-						if(mineralrandom.next()%8 == 0)
-							vmanip.m_data[vi] = MapNode(c_stone_with_coal);
-				}
-			}
-		}
-		/*
-			Add iron
-		*/
-		u16 iron_amount = 8;
-		u16 iron_rareness = 60 / iron_amount;
-		if(iron_rareness == 0)
-			iron_rareness = 1;
-		if(mineralrandom.next()%iron_rareness == 0)
-		{
-			u16 a = mineralrandom.next() % 16;
-			u16 amount = iron_amount * a*a*a / 1000;
-			for(s16 i=0; i<amount; i++)
-			{
-				s16 x = mineralrandom.range(node_min.X+1, node_max.X-1);
-				s16 y = mineralrandom.range(node_min.Y+1, node_max.Y-1);
-				s16 z = mineralrandom.range(node_min.Z+1, node_max.Z-1);
-				for(u16 i=0; i<27; i++)
-				{
-					v3s16 p = v3s16(x,y,z) + g_27dirs[i];
-					u32 vi = vmanip.m_area.index(p);
-					if(vmanip.m_data[vi].getContent() == c_stone)
-						if(mineralrandom.next()%8 == 0)
-							vmanip.m_data[vi] = MapNode(c_stone_with_iron);
-				}
-			}
-		}
-	}
-#endif
-
-	/*
-		Calculate lighting
-	*/
-	{
-	ScopeProfiler sp(g_profiler, "EmergeThread: mapgen lighting update",
-			SPT_AVG);
-	//VoxelArea a(node_min, node_max);
-	VoxelArea a(node_min-v3s16(1,0,1)*MAP_BLOCKSIZE,
-			node_max+v3s16(1,0,1)*MAP_BLOCKSIZE);
-	/*VoxelArea a(node_min-v3s16(1,0,1)*MAP_BLOCKSIZE/2,
-			node_max+v3s16(1,0,1)*MAP_BLOCKSIZE/2);*/
-	enum LightBank banks[2] = {LIGHTBANK_DAY, LIGHTBANK_NIGHT};
-	for(int i=0; i<2; i++)
-	{
-		enum LightBank bank = banks[i];
-
-		core::map<v3s16, bool> light_sources;
-		core::map<v3s16, u8> unlight_from;
-
-		voxalgo::clearLightAndCollectSources(vmanip, a, bank, ndef,
-				light_sources, unlight_from);
-
-		bool inexistent_top_provides_sunlight = !block_is_underground;
-		voxalgo::SunlightPropagateResult res = voxalgo::propagateSunlight(
-				vmanip, a, inexistent_top_provides_sunlight,
-				light_sources, ndef);
-		// TODO: Do stuff according to bottom_sunlight_valid
-
-		vmanip.unspreadLight(bank, unlight_from, light_sources, ndef);
-
-		vmanip.spreadLight(bank, light_sources, ndef);
-	}
-	}
-}
-
-#endif ///BIG COMMENT
-
diff --git a/src/mapgen.h b/src/mapgen.h
index 911e87537cc3ac5d7bd3071487216762c73122c8..a900985da5bfbb44586e646d747d0e1a1bc2ffce 100644
--- a/src/mapgen.h
+++ b/src/mapgen.h
@@ -32,7 +32,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #define MG_TREES         0x01
 #define MG_CAVES         0x02
 #define MG_DUNGEONS      0x04
-#define MGV6_FORESTS     0x08
+#define MGV6_JUNGLES     0x08
 #define MGV6_BIOME_BLEND 0x10
 #define MG_FLAT          0x20
 
@@ -45,7 +45,8 @@ class MapBlock;
 class ManualMapVoxelManipulator;
 class VoxelManipulator;
 class INodeDefManager;
-class BlockMakeData;
+struct BlockMakeData;
+class VoxelArea;
 
 struct MapgenParams {
 	std::string mg_name;
@@ -72,6 +73,14 @@ class Mapgen {
 	int water_level;
 	bool generating;
 	int id;
+	ManualMapVoxelManipulator *vm;
+	INodeDefManager *ndef;
+
+	void updateLiquid(UniqueQueue<v3s16> *trans_liquid, v3s16 nmin, v3s16 nmax);
+	void setLighting(v3s16 nmin, v3s16 nmax, u8 light);
+	void lightSpread(VoxelArea &a, v3s16 p, u8 light);
+	void calcLighting(v3s16 nmin, v3s16 nmax);
+	void calcLightingOld(v3s16 nmin, v3s16 nmax);
 
 	virtual void makeChunk(BlockMakeData *data) {};
 	virtual int getGroundLevelAtPoint(v2s16 p) = 0;
@@ -88,5 +97,48 @@ struct MapgenFactory {
 	virtual MapgenParams *createMapgenParams() = 0;
 };
 
+enum OreType {
+	ORE_SCATTER,
+	ORE_SHEET,
+	ORE_CLAYLIKE
+};
+
+class Ore {
+public:
+	std::string ore_name;
+	std::string wherein_name;
+
+	content_t ore;
+	content_t wherein;  // the node to be replaced
+	s16 clust_scarcity; //
+	s16 clust_num_ores; // how many ore nodes are in a chunk
+	s16 clust_size;     // how large (in nodes) a chunk of ore is
+	s16 height_min;
+	s16 height_max;
+	float nthresh;      // threshhold for noise at which an ore is placed 
+	NoiseParams *np;    // noise for distribution of clusters (NULL for uniform scattering)
+	Noise *noise;
+	
+	Ore() {
+		ore     = CONTENT_IGNORE;
+		wherein = CONTENT_IGNORE;
+		np      = NULL;
+		noise   = NULL;
+	}
+	
+	void resolveNodeNames(INodeDefManager *ndef);
+	virtual void generate(Mapgen *mg, u32 blockseed, v3s16 nmin, v3s16 nmax) = 0;
+};
+
+class OreScatter : public Ore {
+	 void generate(Mapgen *mg, u32 blockseed, v3s16 nmin, v3s16 nmax);
+};
+
+class OreSheet : public Ore {
+	void generate(Mapgen *mg, u32 blockseed, v3s16 nmin, v3s16 nmax);
+};
+
+Ore *createOre(OreType type);
+
 #endif
 
diff --git a/src/mapgen_indev.cpp b/src/mapgen_indev.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..5d455827ae4db28fe314731c096f935f6181589f
--- /dev/null
+++ b/src/mapgen_indev.cpp
@@ -0,0 +1,371 @@
+/*
+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 "mapgen_indev.h"
+#include "constants.h"
+#include "map.h"
+#include "main.h"
+#include "log.h"
+
+/////////////////// Mapgen Indev perlin noise (default values - not used, from config or defaultsettings)
+
+NoiseIndevParams nparams_indev_def;
+
+/*
+NoiseIndevParams nparams_indev_def_terrain_base;
+//	(-AVERAGE_MUD_AMOUNT, 20.0, v3f(250.0, 250.0, 250.0), 82341, 5, 0.6, 1);
+NoiseIndevParams nparams_indev_def_terrain_higher;
+//	(20.0, 16.0, v3f(500.0, 500.0, 500.0), 85039, 5, 0.6, 1);
+NoiseIndevParams nparams_indev_def_steepness;
+//	(0.85, 0.5, v3f(125.0, 125.0, 125.0), -932, 5, 0.7, 1);
+NoiseIndevParams nparams_indev_def_mud;
+//	(AVERAGE_MUD_AMOUNT, 2.0, v3f(200.0, 200.0, 200.0), 91013, 3, 0.55, 1);
+NoiseIndevParams nparams_indev_def_float_islands;
+//	(1, 10.0, v3f(500.0, 500.0, 500.0), 32451, 5, 0.6, 1);
+NoiseIndevParams nparams_indev_def_biome;
+*/
+
+///////////////////////////////////////////////////////////////////////////////
+
+void NoiseIndev::init(NoiseIndevParams *np, int seed, int sx, int sy, int sz) {
+	Noise::init((NoiseParams*)np, seed, sx, sy, sz);
+	this->npindev   = np;
+}
+
+NoiseIndev::NoiseIndev(NoiseIndevParams *np, int seed, int sx, int sy) : Noise(np, seed, sx, sy) {
+        init(np, seed, sx, sy, 1);
+}
+
+NoiseIndev::NoiseIndev(NoiseIndevParams *np, int seed, int sx, int sy, int sz) : Noise(np, seed, sx, sy, sz) {
+        init(np, seed, sx, sy, sz);
+}
+
+float farscale(float scale, float z) {
+	return ( 1 + ( 1 - (MAP_GENERATION_LIMIT * 1 - (fabs(z))                     ) / (MAP_GENERATION_LIMIT * 1) ) * (scale - 1) );
+}
+
+float farscale(float scale, float x, float z) {
+	return ( 1 + ( 1 - (MAP_GENERATION_LIMIT * 2 - (fabs(x) + fabs(z))           ) / (MAP_GENERATION_LIMIT * 2) ) * (scale - 1) );
+}
+
+float farscale(float scale, float x, float y, float z) {
+	return ( 1 + ( 1 - (MAP_GENERATION_LIMIT * 3 - (fabs(x) + fabs(y) + fabs(z)) ) / (MAP_GENERATION_LIMIT * 3) ) * (scale - 1) );
+}
+
+void NoiseIndev::transformNoiseMapFarScale(float xx, float yy, float zz) {
+        int i = 0;
+        for (int z = 0; z != sz; z++) {
+                for (int y = 0; y != sy; y++) {
+                        for (int x = 0; x != sx; x++) {
+                                result[i] = result[i] * npindev->scale * farscale(npindev->farscale, xx, yy, zz) + npindev->offset;
+                                i++;
+                        }
+                }
+        }
+}
+
+MapgenIndev::MapgenIndev(int mapgenid, MapgenIndevParams *params, EmergeManager *emerge) 
+	: MapgenV6(mapgenid, params, emerge)
+{
+        noiseindev_terrain_base   = new NoiseIndev(params->npindev_terrain_base,   seed, csize.X, csize.Z);
+        noiseindev_terrain_higher = new NoiseIndev(params->npindev_terrain_higher, seed, csize.X, csize.Z);
+        noiseindev_steepness      = new NoiseIndev(params->npindev_steepness,      seed, csize.X, csize.Z);
+//        noise_height_select  = new Noise(params->np_height_select,  seed, csize.X, csize.Y);
+//        noise_trees          = new Noise(params->np_trees,          seed, csize.X, csize.Y);
+        noiseindev_mud            = new NoiseIndev(params->npindev_mud,            seed, csize.X, csize.Z);
+//        noise_beach          = new Noise(params->np_beach,          seed, csize.X, csize.Y);
+        noiseindev_float_islands1  = new NoiseIndev(params->npindev_float_islands1,  seed, csize.X, csize.Y, csize.Z);
+        noiseindev_float_islands2  = new NoiseIndev(params->npindev_float_islands2,  seed, csize.X, csize.Y, csize.Z);
+        noiseindev_float_islands3  = new NoiseIndev(params->npindev_float_islands3,  seed, csize.X, csize.Z);
+        noiseindev_biome          = new NoiseIndev(params->npindev_biome,          seed, csize.X, csize.Z);
+}
+
+MapgenIndev::~MapgenIndev() {
+	delete noiseindev_terrain_base;
+	delete noiseindev_terrain_higher;
+	delete noiseindev_steepness;
+	//delete noise_height_select;
+	//delete noise_trees;
+	delete noiseindev_mud;
+	//delete noise_beach;
+	delete noiseindev_float_islands1;
+	delete noiseindev_float_islands2;
+	delete noiseindev_float_islands3;
+	delete noiseindev_biome;
+}
+
+void MapgenIndev::calculateNoise() {
+	int x = node_min.X;
+	int y = node_min.Y;
+	int z = node_min.Z;
+	// Need to adjust for the original implementation's +.5 offset...
+	if (!(flags & MG_FLAT)) {
+		noiseindev_terrain_base->perlinMap2D(
+			x + 0.5 * noiseindev_terrain_base->npindev->spread.X * farscale(noiseindev_terrain_base->npindev->farspread, x, z),
+			z + 0.5 * noiseindev_terrain_base->npindev->spread.Z * farscale(noiseindev_terrain_base->npindev->farspread, x, z));
+		noiseindev_terrain_base->transformNoiseMapFarScale(x, y, z);
+
+		noiseindev_terrain_higher->perlinMap2D(
+			x + 0.5 * noiseindev_terrain_higher->npindev->spread.X * farscale(noiseindev_terrain_higher->npindev->farspread, x, z),
+			z + 0.5 * noiseindev_terrain_higher->npindev->spread.Z * farscale(noiseindev_terrain_higher->npindev->farspread, x, z));
+		noiseindev_terrain_higher->transformNoiseMapFarScale(x, y, z);
+
+		noiseindev_steepness->perlinMap2D(
+			x + 0.5 * noiseindev_steepness->npindev->spread.X * farscale(noiseindev_steepness->npindev->farspread, x, z),
+			z + 0.5 * noiseindev_steepness->npindev->spread.Z * farscale(noiseindev_steepness->npindev->farspread, x, z));
+		noiseindev_steepness->transformNoiseMapFarScale(x, y, z);
+
+		noise_height_select->perlinMap2D(
+			x + 0.5 * noise_height_select->np->spread.X,
+			z + 0.5 * noise_height_select->np->spread.Z);
+
+		noiseindev_float_islands1->perlinMap3D(
+			x + 0.33 * noiseindev_float_islands1->npindev->spread.X * farscale(noiseindev_float_islands1->npindev->farspread, x, y, z),
+			y + 0.33 * noiseindev_float_islands1->npindev->spread.Y * farscale(noiseindev_float_islands1->npindev->farspread, x, y, z),
+			z + 0.33 * noiseindev_float_islands1->npindev->spread.Z * farscale(noiseindev_float_islands1->npindev->farspread, x, y, z)
+		);
+		noiseindev_float_islands1->transformNoiseMapFarScale(x, y, z);
+
+		noiseindev_float_islands2->perlinMap3D(
+			x + 0.33 * noiseindev_float_islands2->npindev->spread.X * farscale(noiseindev_float_islands2->npindev->farspread, x, y, z),
+			y + 0.33 * noiseindev_float_islands2->npindev->spread.Y * farscale(noiseindev_float_islands2->npindev->farspread, x, y, z),
+			z + 0.33 * noiseindev_float_islands2->npindev->spread.Z * farscale(noiseindev_float_islands2->npindev->farspread, x, y, z)
+		);
+		noiseindev_float_islands2->transformNoiseMapFarScale(x, y, z);
+
+		noiseindev_float_islands3->perlinMap2D(
+			x + 0.5 * noiseindev_float_islands3->npindev->spread.X * farscale(noiseindev_float_islands3->npindev->farspread, x, z),
+			z + 0.5 * noiseindev_float_islands3->npindev->spread.Z * farscale(noiseindev_float_islands3->npindev->farspread, x, z));
+		noiseindev_float_islands3->transformNoiseMapFarScale(x, y, z);
+
+	}
+	
+	if (!(flags & MG_FLAT)) {
+		noiseindev_mud->perlinMap2D(
+			x + 0.5 * noiseindev_mud->npindev->spread.X * farscale(noiseindev_mud->npindev->farspread, x, y, z),
+			z + 0.5 * noiseindev_mud->npindev->spread.Z * farscale(noiseindev_mud->npindev->farspread, x, y, z));
+		noiseindev_mud->transformNoiseMapFarScale(x, y, z);
+	}
+	noise_beach->perlinMap2D(
+		x + 0.2 * noise_beach->np->spread.X,
+		z + 0.7 * noise_beach->np->spread.Z);
+
+	noise_biome->perlinMap2D(
+		x + 0.6 * noiseindev_biome->npindev->spread.X * farscale(noiseindev_biome->npindev->farspread, x, z),
+		z + 0.2 * noiseindev_biome->npindev->spread.Z * farscale(noiseindev_biome->npindev->farspread, x, z));
+}
+
+bool MapgenIndevParams::readParams(Settings *settings) {
+        freq_desert = settings->getFloat("mgv6_freq_desert");
+        freq_beach  = settings->getFloat("mgv6_freq_beach");
+
+        npindev_terrain_base   = settings->getNoiseIndevParams("mgindev_np_terrain_base");
+        npindev_terrain_higher = settings->getNoiseIndevParams("mgindev_np_terrain_higher");
+        npindev_steepness      = settings->getNoiseIndevParams("mgindev_np_steepness");
+        np_height_select  = settings->getNoiseParams("mgv6_np_height_select");
+        np_trees          = settings->getNoiseParams("mgv6_np_trees");
+        npindev_mud            = settings->getNoiseIndevParams("mgindev_np_mud");
+        np_beach          = settings->getNoiseParams("mgv6_np_beach");
+        npindev_biome     = settings->getNoiseIndevParams("mgindev_np_biome");
+        np_cave           = settings->getNoiseParams("mgv6_np_cave");
+        npindev_float_islands1  = settings->getNoiseIndevParams("mgindev_np_float_islands1");
+        npindev_float_islands2  = settings->getNoiseIndevParams("mgindev_np_float_islands2");
+        npindev_float_islands3  = settings->getNoiseIndevParams("mgindev_np_float_islands3");
+
+        bool success =
+                npindev_terrain_base  && npindev_terrain_higher && npindev_steepness &&
+                np_height_select && np_trees          && npindev_mud       &&
+                np_beach         && np_biome          && np_cave &&
+                npindev_float_islands1 && npindev_float_islands2 && npindev_float_islands3;
+        return success;
+}
+
+void MapgenIndevParams::writeParams(Settings *settings) {
+        settings->setFloat("mgv6_freq_desert", freq_desert);
+        settings->setFloat("mgv6_freq_beach",  freq_beach);
+
+        settings->setNoiseIndevParams("mgindev_np_terrain_base",   npindev_terrain_base);
+        settings->setNoiseIndevParams("mgindev_np_terrain_higher", npindev_terrain_higher);
+        settings->setNoiseIndevParams("mgindev_np_steepness",      npindev_steepness);
+        settings->setNoiseParams("mgv6_np_height_select",  np_height_select);
+        settings->setNoiseParams("mgv6_np_trees",          np_trees);
+        settings->setNoiseIndevParams("mgindev_np_mud",            npindev_mud);
+        settings->setNoiseParams("mgv6_np_beach",          np_beach);
+        settings->setNoiseIndevParams("mgindev_np_biome",          npindev_biome);
+        settings->setNoiseParams("mgv6_np_cave",           np_cave);
+        settings->setNoiseIndevParams("mgindev_np_float_islands1",  npindev_float_islands1);
+        settings->setNoiseIndevParams("mgindev_np_float_islands2",  npindev_float_islands2);
+        settings->setNoiseIndevParams("mgindev_np_float_islands3",  npindev_float_islands3);
+}
+
+
+float MapgenIndev::baseTerrainLevelFromNoise(v2s16 p) {
+	if (flags & MG_FLAT)
+		return water_level;
+		
+	float terrain_base   = NoisePerlin2DPosOffset(noiseindev_terrain_base->npindev,
+							p.X, 0.5, p.Y, 0.5, seed);
+	float terrain_higher = NoisePerlin2DPosOffset(noiseindev_terrain_higher->npindev,
+							p.X, 0.5, p.Y, 0.5, seed);
+	float steepness      = NoisePerlin2DPosOffset(noiseindev_steepness->npindev,
+							p.X, 0.5, p.Y, 0.5, seed);
+	float height_select  = NoisePerlin2DNoTxfmPosOffset(noise_height_select->np,
+							p.X, 0.5, p.Y, 0.5, seed);
+
+	return baseTerrainLevel(terrain_base, terrain_higher,
+							steepness,    height_select);
+}
+
+float MapgenIndev::baseTerrainLevelFromMap(int index) {
+	if (flags & MG_FLAT)
+		return water_level;
+	
+	float terrain_base   = noiseindev_terrain_base->result[index];
+	float terrain_higher = noiseindev_terrain_higher->result[index];
+	float steepness      = noiseindev_steepness->result[index];
+	float height_select  = noise_height_select->result[index];
+	
+	return baseTerrainLevel(terrain_base, terrain_higher,
+							steepness,    height_select);
+}
+
+float MapgenIndev::getMudAmount(int index) {
+	if (flags & MG_FLAT)
+		return AVERAGE_MUD_AMOUNT;
+		
+	/*return ((float)AVERAGE_MUD_AMOUNT + 2.0 * noise2d_perlin(
+			0.5+(float)p.X/200, 0.5+(float)p.Y/200,
+			seed+91013, 3, 0.55));*/
+	
+	return noiseindev_mud->result[index];
+}
+
+void MapgenIndev::defineCave(Cave & cave, PseudoRandom ps, v3s16 node_min, bool large_cave) {
+	cave.min_tunnel_diameter = 2;
+	cave.max_tunnel_diameter = ps.range(2,6);
+	cave.dswitchint = ps.range(1,14);
+	cave.flooded = large_cave && ps.range(0,4);
+	if(large_cave){
+		cave.part_max_length_rs = ps.range(2,4);
+		if (node_min.Y < -100 && !ps.range(0, farscale(0.2, node_min.X,node_min.Y,node_min.Z)*30)) { //huge
+			cave.flooded = !ps.range(0, 3);
+			cave.tunnel_routepoints = ps.range(5, 20);
+			cave.min_tunnel_diameter = 30;
+			cave.max_tunnel_diameter = ps.range(40, ps.range(80,120));
+		} else {
+			cave.tunnel_routepoints = ps.range(5, ps.range(15,30));
+			cave.min_tunnel_diameter = 5;
+			cave.max_tunnel_diameter = ps.range(7, ps.range(8,24));
+		}
+	} else {
+		cave.part_max_length_rs = ps.range(2,9);
+		cave.tunnel_routepoints = ps.range(10, ps.range(15,30));
+	}
+	cave.large_cave_is_flat = (ps.range(0,1) == 0);
+}
+
+/*
+// version with one perlin3d. use with good params like
+settings->setDefault("mgindev_np_float_islands1",  "-9.5, 10,  (20,  50,  50 ), 45123, 5, 0.6,  1.5, 5");
+void MapgenIndev::generateFloatIslands(int min_y) {
+	if (node_min.Y < min_y) return;
+	v3s16 p0(node_min.X, node_min.Y, node_min.Z);
+	MapNode n1(c_stone), n2(c_desert_stone);
+	int xl = node_max.X - node_min.X;
+	int yl = node_max.Y - node_min.Y;
+	int zl = node_max.Z - node_min.Z;
+	u32 index = 0;
+	for (int x1 = 0; x1 <= xl; x1++)
+	{
+		//int x = x1 + node_min.Y;
+		for (int z1 = 0; z1 <= zl; z1++)
+		{
+			//int z = z1 + node_min.Z;
+			for (int y1 = 0; y1 <= yl; y1++, index++)
+			{
+				//int y = y1 + node_min.Y;
+				float noise = noiseindev_float_islands1->result[index];
+				//dstream << " y1="<<y1<< " x1="<<x1<<" z1="<<z1<< " noise="<<noise << std::endl;
+				if (noise > 0 ) {
+					v3s16 p = p0 + v3s16(x1, y1, z1);
+					u32 i = vm->m_area.index(p);
+					if (!vm->m_area.contains(i))
+						continue;
+					// Cancel if not  air
+					if (vm->m_data[i].getContent() != CONTENT_AIR)
+						continue;
+					vm->m_data[i] = noise > 1 ? n1 : n2;
+				}
+			}
+		}
+	}
+}
+*/
+
+void MapgenIndev::generateFloatIslands(int min_y) {
+	if (node_min.Y < min_y) return;
+	PseudoRandom pr(blockseed + 985);
+	// originally from http://forum.minetest.net/viewtopic.php?id=4776
+	float RAR = 0.8 * farscale(0.4, node_min.Y); // 0.4; // Island rarity in chunk layer. -0.4 = thick layer with holes, 0 = 50%, 0.4 = desert rarity, 0.7 = very rare.
+	float AMPY = 24; // 24; // Amplitude of island centre y variation.
+	float TGRAD = 24; // 24; // Noise gradient to create top surface. Tallness of island top.
+	float BGRAD = 24; // 24; // Noise gradient to create bottom surface. Tallness of island bottom.
+
+	v3s16 p0(node_min.X, node_min.Y, node_min.Z);
+	MapNode n1(c_stone);
+
+	float xl = node_max.X - node_min.X;
+	float yl = node_max.Y - node_min.Y;
+	float zl = node_max.Z - node_min.Z;
+	float midy = node_min.Y + yl * 0.5;
+	u32 index = 0, index2d = 0;
+	for (int x1 = 0; x1 <= xl; ++x1)
+	{
+		for (int z1 = 0; z1 <= zl; ++z1, ++index2d)
+		{
+			float noise3 = noiseindev_float_islands3->result[index2d];
+			float pmidy = midy + noise3 / 1.5 * AMPY;
+			for (int y1 = 0; y1 <= yl; ++y1, ++index)
+			{
+				int y = y1 + node_min.Y;
+				float noise1 = noiseindev_float_islands1->result[index];
+				float offset = y > pmidy ? (y - pmidy) / TGRAD : (pmidy - y) / BGRAD;
+				float noise1off = noise1 - offset - RAR;
+				if (noise1off > 0 && noise1off < 0.7) {
+					float noise2 = noiseindev_float_islands2->result[index];
+					if (noise2 - noise1off > -0.7){
+						v3s16 p = p0 + v3s16(x1, y1, z1);
+						u32 i = vm->m_area.index(p);
+						if (!vm->m_area.contains(i))
+							continue;
+						// Cancel if not  air
+						if (vm->m_data[i].getContent() != CONTENT_AIR)
+							continue;
+						vm->m_data[i] = n1;
+					}
+				}
+			}
+		}
+	}
+}
+
+void MapgenIndev::generateSomething() {
+	int float_islands = g_settings->getS16("mgindev_float_islands");
+	if(float_islands) generateFloatIslands(float_islands);
+}
diff --git a/src/mapgen_indev.h b/src/mapgen_indev.h
new file mode 100644
index 0000000000000000000000000000000000000000..fdac1ba209f4808217527909c43e26236477946e
--- /dev/null
+++ b/src/mapgen_indev.h
@@ -0,0 +1,152 @@
+/*
+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 MAPGENINDEV_HEADER
+#define MAPGENINDEV_HEADER
+
+#include "mapgen.h"
+#include "mapgen_v6.h"
+
+float farscale(float scale, float z);
+float farscale(float scale, float x, float z);
+float farscale(float scale, float x, float y, float z);
+
+struct NoiseIndevParams : public NoiseParams {
+	float farscale;
+	float farspread;
+
+	NoiseIndevParams(){}
+	NoiseIndevParams(float offset_, float scale_, v3f spread_, int seed_, int octaves_, float persist_, float farscale_ = 1, float farspread_ = 1)
+	{
+		offset = offset_;
+		scale = scale_;
+		spread = spread_;
+		seed = seed_;
+		octaves = octaves_;
+		persist = persist_;
+
+		farscale = farscale_;
+		farspread = farspread_;
+	}
+
+};
+
+#define getNoiseIndevParams(x) getStruct<NoiseIndevParams>((x), "f,f,v3,s32,s32,f,f,f")
+#define setNoiseIndevParams(x, y) setStruct((x), "f,f,v3,s32,s32,f,f,f", (y))
+
+class NoiseIndev : public Noise {
+    public:
+	NoiseIndevParams *npindev;
+
+	//NoiseIndev() {};
+	NoiseIndev(NoiseIndevParams *np, int seed, int sx, int sy);
+	NoiseIndev(NoiseIndevParams *np, int seed, int sx, int sy, int sz);
+	void init(NoiseIndevParams *np, int seed, int sx, int sy, int sz);
+	void transformNoiseMapFarScale(float xx = 0, float yy = 0, float zz = 0);
+};
+
+extern NoiseIndevParams nparams_indev_def;
+/*
+extern NoiseIndevParams nparams_indev_def_terrain_base;
+extern NoiseIndevParams nparams_indev_def_terrain_higher;
+extern NoiseIndevParams nparams_indev_def_steepness;
+//extern NoiseIndevParams nparams_indev_def_height_select;
+//extern NoiseIndevParams nparams_indev_def_trees;
+extern NoiseIndevParams nparams_indev_def_mud;
+//extern NoiseIndevParams nparams_indev_def_beach;
+extern NoiseIndevParams nparams_indev_def_biome;
+//extern NoiseIndevParams nparams_indev_def_cave;
+extern NoiseIndevParams nparams_indev_def_float_islands;
+*/
+
+struct MapgenIndevParams : public MapgenV6Params {
+	NoiseIndevParams *npindev_terrain_base;
+	NoiseIndevParams *npindev_terrain_higher;
+	NoiseIndevParams *npindev_steepness;
+	//NoiseParams *np_height_select;
+	//NoiseParams *np_trees;
+	NoiseIndevParams *npindev_mud;
+	//NoiseParams *np_beach;
+	NoiseIndevParams *npindev_biome;
+	//NoiseParams *np_cave;
+	NoiseIndevParams *npindev_float_islands1;
+	NoiseIndevParams *npindev_float_islands2;
+	NoiseIndevParams *npindev_float_islands3;
+
+	MapgenIndevParams() {
+		//freq_desert       = 0.45;
+		//freq_beach        = 0.15;
+		npindev_terrain_base   = &nparams_indev_def; //&nparams_indev_def_terrain_base;
+		npindev_terrain_higher = &nparams_indev_def; //&nparams_indev_def_terrain_higher;
+		npindev_steepness      = &nparams_indev_def; //&nparams_indev_def_steepness;
+		//np_height_select  = &nparams_v6_def_height_select;
+		//np_trees          = &nparams_v6_def_trees;
+		npindev_mud            = &nparams_indev_def; //&nparams_indev_def_mud;
+		//np_beach          = &nparams_v6_def_beach;
+		npindev_biome          = &nparams_indev_def; //&nparams_indev_def_biome;
+		//np_cave           = &nparams_v6_def_cave;
+		npindev_float_islands1  = &nparams_indev_def; //&nparams_indev_def_float_islands;
+		npindev_float_islands2  = &nparams_indev_def; //&nparams_indev_def_float_islands;
+		npindev_float_islands3  = &nparams_indev_def; //&nparams_indev_def_float_islands;
+
+	}
+
+	bool readParams(Settings *settings);
+	void writeParams(Settings *settings);
+};
+
+class MapgenIndev : public MapgenV6 {
+    public:
+	NoiseIndev *noiseindev_terrain_base;
+	NoiseIndev *noiseindev_terrain_higher;
+	NoiseIndev *noiseindev_steepness;
+	//NoiseIndev *noise_height_select;
+	//NoiseIndev *noise_trees;
+	NoiseIndev *noiseindev_mud;
+	//NoiseIndev *noise_beach;
+	NoiseIndev *noiseindev_biome;
+	//NoiseIndevParams *np_cave;
+	NoiseIndev *noiseindev_float_islands1;
+	NoiseIndev *noiseindev_float_islands2;
+	NoiseIndev *noiseindev_float_islands3;
+
+	MapgenIndev(int mapgenid, MapgenIndevParams *params, EmergeManager *emerge);
+	~MapgenIndev();
+	void calculateNoise();
+
+	float baseTerrainLevelFromNoise(v2s16 p);
+	float baseTerrainLevelFromMap(int index);
+	float getMudAmount(int index);
+	void defineCave(Cave & cave, PseudoRandom ps, v3s16 node_min, bool large_cave);
+	void generateSomething();
+	
+	void generateFloatIslands(int min_y);
+};
+
+struct MapgenFactoryIndev : public MapgenFactoryV6 {
+	Mapgen *createMapgen(int mgid, MapgenParams *params, EmergeManager *emerge) {
+		return new MapgenIndev(mgid, (MapgenIndevParams *)params, emerge);
+	};
+
+	MapgenParams *createMapgenParams() {
+		return new MapgenIndevParams();
+	};
+};
+
+#endif
diff --git a/src/mapgen_singlenode.cpp b/src/mapgen_singlenode.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..22b756abba513de587b7ff01846dcc71f6f84b64
--- /dev/null
+++ b/src/mapgen_singlenode.cpp
@@ -0,0 +1,102 @@
+/*
+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 "mapgen_singlenode.h"
+#include "voxel.h"
+#include "mapblock.h"
+#include "mapnode.h"
+#include "map.h"
+#include "nodedef.h"
+#include "voxelalgorithms.h"
+#include "profiler.h"
+#include "settings.h" // For g_settings
+#include "main.h" // For g_profiler
+#include "emerge.h"
+
+//////////////////////// Mapgen Singlenode parameter read/write
+
+bool MapgenSinglenodeParams::readParams(Settings *settings) {
+	return true;
+}
+
+
+void MapgenSinglenodeParams::writeParams(Settings *settings) {
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+MapgenSinglenode::MapgenSinglenode(int mapgenid, MapgenSinglenodeParams *params) {
+}
+
+
+MapgenSinglenode::~MapgenSinglenode() {
+}
+
+//////////////////////// Map generator
+
+void MapgenSinglenode::makeChunk(BlockMakeData *data) {
+	assert(data->vmanip);
+	assert(data->nodedef);
+	assert(data->blockpos_requested.X >= data->blockpos_min.X &&
+		   data->blockpos_requested.Y >= data->blockpos_min.Y &&
+		   data->blockpos_requested.Z >= data->blockpos_min.Z);
+	assert(data->blockpos_requested.X <= data->blockpos_max.X &&
+		   data->blockpos_requested.Y <= data->blockpos_max.Y &&
+		   data->blockpos_requested.Z <= data->blockpos_max.Z);
+
+	this->generating = true;
+	this->vm   = data->vmanip;	
+	this->ndef = data->nodedef;
+			
+	v3s16 blockpos_min = data->blockpos_min;
+	v3s16 blockpos_max = data->blockpos_max;
+
+	// Area of central chunk
+	v3s16 node_min = blockpos_min*MAP_BLOCKSIZE;
+	v3s16 node_max = (blockpos_max+v3s16(1,1,1))*MAP_BLOCKSIZE-v3s16(1,1,1);
+
+	content_t c_node = ndef->getId("mapgen_singlenode");
+	if (c_node == CONTENT_IGNORE)
+		c_node = CONTENT_AIR;
+	
+	MapNode n_node(c_node);
+	
+	for (s16 z = node_min.Z; z <= node_max.Z; z++)
+	for (s16 y = node_min.Y; y <= node_max.Y; y++) {
+		u32 i = vm->m_area.index(node_min.X, y, z);
+		for (s16 x = node_min.X; x <= node_max.X; x++) {
+			if (vm->m_data[i].getContent() == CONTENT_IGNORE)
+				vm->m_data[i] = n_node;
+			i++;
+		}
+	}
+
+	// Add top and bottom side of water to transforming_liquid queue
+	updateLiquid(&data->transforming_liquid, node_min, node_max);
+
+	// Calculate lighting
+	calcLighting(node_min, node_max);
+	
+	this->generating = false;
+}
+
+int MapgenSinglenode::getGroundLevelAtPoint(v2s16 p) {
+	return 0;
+}
+
diff --git a/src/mapgen_singlenode.h b/src/mapgen_singlenode.h
new file mode 100644
index 0000000000000000000000000000000000000000..b86c9a77faf09d80eb8c99f33ac59cab8828f1ca
--- /dev/null
+++ b/src/mapgen_singlenode.h
@@ -0,0 +1,53 @@
+/*
+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 MAPGEN_SINGLENODE_HEADER
+#define MAPGEN_SINGLENODE_HEADER
+
+#include "mapgen.h"
+
+struct MapgenSinglenodeParams : public MapgenParams {
+	
+	MapgenSinglenodeParams() {
+	}
+	
+	bool readParams(Settings *settings);
+	void writeParams(Settings *settings);
+};
+
+class MapgenSinglenode : public Mapgen {
+public:
+	MapgenSinglenode(int mapgenid, MapgenSinglenodeParams *params);
+	~MapgenSinglenode();
+	
+	void makeChunk(BlockMakeData *data);
+	int getGroundLevelAtPoint(v2s16 p);
+};
+
+struct MapgenFactorySinglenode : public MapgenFactory {
+	Mapgen *createMapgen(int mgid, MapgenParams *params, EmergeManager *emerge) {
+		return new MapgenSinglenode(mgid, (MapgenSinglenodeParams *)params);
+	};
+	
+	MapgenParams *createMapgenParams() {
+		return new MapgenSinglenodeParams();
+	};
+};
+
+#endif
diff --git a/src/mapgen_v6.cpp b/src/mapgen_v6.cpp
index d5405876eae3ac247e65ff884dff849146e845ac..0b419617d22bb2d8a54986c1600386fa3fa4ec65 100644
--- a/src/mapgen_v6.cpp
+++ b/src/mapgen_v6.cpp
@@ -32,6 +32,8 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include "settings.h" // For g_settings
 #include "main.h" // For g_profiler
 #include "emerge.h"
+#include "dungeongen.h"
+#include "treegen.h"
 #include "mapgen_v6.h"
 
 /////////////////// Mapgen V6 perlin noise default values
@@ -43,8 +45,6 @@ NoiseParams nparams_v6_def_steepness =
 	{0.85, 0.5, v3f(125.0, 125.0, 125.0), -932, 5, 0.7};
 NoiseParams nparams_v6_def_height_select =
 	{0.5, 1.0, v3f(250.0, 250.0, 250.0), 4213, 5, 0.69};
-NoiseParams nparams_v6_def_trees =
-	{0.0, 1.0, v3f(125.0, 125.0, 125.0), 2, 4, 0.66};
 NoiseParams nparams_v6_def_mud =
 	{AVERAGE_MUD_AMOUNT, 2.0, v3f(200.0, 200.0, 200.0), 91013, 3, 0.55};
 NoiseParams nparams_v6_def_beach =
@@ -53,14 +53,21 @@ NoiseParams nparams_v6_def_biome =
 	{0.0, 1.0, v3f(250.0, 250.0, 250.0), 9130, 3, 0.50};
 NoiseParams nparams_v6_def_cave =
 	{6.0, 6.0, v3f(250.0, 250.0, 250.0), 34329, 3, 0.50};
+NoiseParams nparams_v6_def_humidity =
+	{0.5, 0.5, v3f(500.0, 500.0, 500.0), 72384, 4, 0.66};
+NoiseParams nparams_v6_def_trees =
+	{0.0, 1.0, v3f(125.0, 125.0, 125.0), 2, 4, 0.66};
+NoiseParams nparams_v6_def_apple_trees =
+	{0.0, 1.0, v3f(100.0, 100.0, 100.0), 342902, 3, 0.45};
 
 
 ///////////////////////////////////////////////////////////////////////////////
 
 
-MapgenV6::MapgenV6(int mapgenid, MapgenV6Params *params) {
+MapgenV6::MapgenV6(int mapgenid, MapgenV6Params *params, EmergeManager *emerge) {
 	this->generating  = false;
 	this->id       = mapgenid;
+	this->emerge   = emerge;
 
 	this->seed     = (int)params->seed;
 	this->water_level = params->water_level;
@@ -72,25 +79,18 @@ MapgenV6::MapgenV6(int mapgenid, MapgenV6Params *params) {
 
 	this->ystride = csize.X; //////fix this
 
-	np_cave = params->np_cave;
+	np_cave        = params->np_cave;
+	np_humidity    = params->np_humidity;
+	np_trees       = params->np_trees;
+	np_apple_trees = params->np_apple_trees;
 
 	noise_terrain_base   = new Noise(params->np_terrain_base,   seed, csize.X, csize.Y);
 	noise_terrain_higher = new Noise(params->np_terrain_higher, seed, csize.X, csize.Y);
 	noise_steepness      = new Noise(params->np_steepness,      seed, csize.X, csize.Y);
 	noise_height_select  = new Noise(params->np_height_select,  seed, csize.X, csize.Y);
-	noise_trees          = new Noise(params->np_trees,          seed, csize.X, csize.Y);
 	noise_mud            = new Noise(params->np_mud,            seed, csize.X, csize.Y);
 	noise_beach          = new Noise(params->np_beach,          seed, csize.X, csize.Y);
 	noise_biome          = new Noise(params->np_biome,          seed, csize.X, csize.Y);
-
-	map_terrain_base   = noise_terrain_base->result;
-	map_terrain_higher = noise_terrain_higher->result;
-	map_steepness      = noise_steepness->result;
-	map_height_select  = noise_height_select->result;
-	map_trees          = noise_trees->result;
-	map_mud            = noise_mud->result;
-	map_beach          = noise_beach->result;
-	map_biome          = noise_biome->result;
 }
 
 
@@ -99,169 +99,53 @@ MapgenV6::~MapgenV6() {
 	delete noise_terrain_higher;
 	delete noise_steepness;
 	delete noise_height_select;
-	delete noise_trees;
 	delete noise_mud;
 	delete noise_beach;
 	delete noise_biome;
 }
 
 
-/*
-	Some helper functions for the map generator
-*/
+//////////////////////// Some helper functions for the map generator
 
-#if 1
 // Returns Y one under area minimum if not found
-s16 MapgenV6::find_ground_level(VoxelManipulator &vmanip, v2s16 p2d,
-		INodeDefManager *ndef)
-{
-	v3s16 em = vmanip.m_area.getExtent();
-	s16 y_nodes_max = vmanip.m_area.MaxEdge.Y;
-	s16 y_nodes_min = vmanip.m_area.MinEdge.Y;
-	u32 i = vmanip.m_area.index(v3s16(p2d.X, y_nodes_max, p2d.Y));
+s16 MapgenV6::find_ground_level(v2s16 p2d) {
+	v3s16 em = vm->m_area.getExtent();
+	s16 y_nodes_max = vm->m_area.MaxEdge.Y;
+	s16 y_nodes_min = vm->m_area.MinEdge.Y;
+	u32 i = vm->m_area.index(p2d.X, y_nodes_max, p2d.Y);
 	s16 y;
-	for(y=y_nodes_max; y>=y_nodes_min; y--)
-	{
-		MapNode &n = vmanip.m_data[i];
+	
+	for (y = y_nodes_max; y >= y_nodes_min; y--) {
+		MapNode &n = vm->m_data[i];
 		if(ndef->get(n).walkable)
 			break;
 
-		vmanip.m_area.add_y(em, i, -1);
+		vm->m_area.add_y(em, i, -1);
 	}
-	if(y >= y_nodes_min)
-		return y;
-	else
-		return y_nodes_min - 1;
+	return (y >= y_nodes_min) ? y : y_nodes_min - 1;
 }
 
 // Returns Y one under area minimum if not found
-s16 MapgenV6::find_stone_level(VoxelManipulator &vmanip, v2s16 p2d,
-		INodeDefManager *ndef)
-{
-	v3s16 em = vmanip.m_area.getExtent();
-	s16 y_nodes_max = vmanip.m_area.MaxEdge.Y;
-	s16 y_nodes_min = vmanip.m_area.MinEdge.Y;
-	u32 i = vmanip.m_area.index(v3s16(p2d.X, y_nodes_max, p2d.Y));
+s16 MapgenV6::find_stone_level(v2s16 p2d) {
+	v3s16 em = vm->m_area.getExtent();
+	s16 y_nodes_max = vm->m_area.MaxEdge.Y;
+	s16 y_nodes_min = vm->m_area.MinEdge.Y;
+	u32 i = vm->m_area.index(p2d.X, y_nodes_max, p2d.Y);
 	s16 y;
-	content_t c_stone = ndef->getId("mapgen_stone");
-	content_t c_desert_stone = ndef->getId("mapgen_desert_stone");
-	for(y=y_nodes_max; y>=y_nodes_min; y--)
-	{
-		MapNode &n = vmanip.m_data[i];
+
+	for (y = y_nodes_max; y >= y_nodes_min; y--) {
+		MapNode &n = vm->m_data[i];
 		content_t c = n.getContent();
-		if(c != CONTENT_IGNORE && (
-				c == c_stone || c == c_desert_stone))
+		if (c != CONTENT_IGNORE && (
+			c == c_stone || c == c_desert_stone))
 			break;
 
-		vmanip.m_area.add_y(em, i, -1);
-	}
-	if(y >= y_nodes_min)
-		return y;
-	else
-		return y_nodes_min - 1;
-}
-#endif
-
-void MapgenV6::make_tree(ManualMapVoxelManipulator &vmanip, v3s16 p0,
-		bool is_apple_tree, INodeDefManager *ndef)
-{
-	MapNode treenode(ndef->getId("mapgen_tree"));
-	MapNode leavesnode(ndef->getId("mapgen_leaves"));
-	MapNode applenode(ndef->getId("mapgen_apple"));
-
-	s16 trunk_h = myrand_range(4, 5);
-	v3s16 p1 = p0;
-	for(s16 ii=0; ii<trunk_h; ii++)
-	{
-		if(vmanip.m_area.contains(p1))
-			vmanip.m_data[vmanip.m_area.index(p1)] = treenode;
-		p1.Y++;
-	}
-
-	// p1 is now the last piece of the trunk
-	p1.Y -= 1;
-
-	VoxelArea leaves_a(v3s16(-2,-1,-2), v3s16(2,2,2));
-	//SharedPtr<u8> leaves_d(new u8[leaves_a.getVolume()]);
-	Buffer<u8> leaves_d(leaves_a.getVolume());
-	for(s32 i=0; i<leaves_a.getVolume(); i++)
-		leaves_d[i] = 0;
-
-	// Force leaves at near the end of the trunk
-	{
-		s16 d = 1;
-		for(s16 z=-d; z<=d; z++)
-		for(s16 y=-d; y<=d; y++)
-		for(s16 x=-d; x<=d; x++)
-		{
-			leaves_d[leaves_a.index(v3s16(x,y,z))] = 1;
-		}
-	}
-
-	// Add leaves randomly
-	for(u32 iii=0; iii<7; iii++)
-	{
-		s16 d = 1;
-
-		v3s16 p(
-			myrand_range(leaves_a.MinEdge.X, leaves_a.MaxEdge.X-d),
-			myrand_range(leaves_a.MinEdge.Y, leaves_a.MaxEdge.Y-d),
-			myrand_range(leaves_a.MinEdge.Z, leaves_a.MaxEdge.Z-d)
-		);
-
-		for(s16 z=0; z<=d; z++)
-		for(s16 y=0; y<=d; y++)
-		for(s16 x=0; x<=d; x++)
-		{
-			leaves_d[leaves_a.index(p+v3s16(x,y,z))] = 1;
-		}
-	}
-
-	// Blit leaves to vmanip
-	for(s16 z=leaves_a.MinEdge.Z; z<=leaves_a.MaxEdge.Z; z++)
-	for(s16 y=leaves_a.MinEdge.Y; y<=leaves_a.MaxEdge.Y; y++)
-	for(s16 x=leaves_a.MinEdge.X; x<=leaves_a.MaxEdge.X; x++)
-	{
-		v3s16 p(x,y,z);
-		p += p1;
-		if(vmanip.m_area.contains(p) == false)
-			continue;
-		u32 vi = vmanip.m_area.index(p);
-		if(vmanip.m_data[vi].getContent() != CONTENT_AIR
-				&& vmanip.m_data[vi].getContent() != CONTENT_IGNORE)
-			continue;
-		u32 i = leaves_a.index(x,y,z);
-		if(leaves_d[i] == 1) {
-			bool is_apple = myrand_range(0,99) < 10;
-			if(is_apple_tree && is_apple) {
-				vmanip.m_data[vi] = applenode;
-			} else {
-				vmanip.m_data[vi] = leavesnode;
-			}
-		}
+		vm->m_area.add_y(em, i, -1);
 	}
+	return (y >= y_nodes_min) ? y : y_nodes_min - 1;
 }
 
 
-/*
-	Noise functions. Make sure seed is mangled differently in each one.
-*/
-
-
-// Amount of trees per area in nodes
-double MapgenV6::tree_amount_2d(u64 seed, v2s16 p)
-{
-	/*double noise = noise2d_perlin(
-			0.5+(float)p.X/125, 0.5+(float)p.Y/125,
-			seed+2, 4, 0.66);*/
-	double noise = map_trees[(p.Y - node_min.Z) * ystride + (p.X - node_min.X)];
-	double zeroval = -0.39;
-	if(noise < zeroval)
-		return 0;
-	else
-		return 0.04 * (noise-zeroval) / (1.0-zeroval);
-}
-
 // Required by mapgen.h
 bool MapgenV6::block_is_underground(u64 seed, v3s16 blockpos)
 {
@@ -277,105 +161,148 @@ bool MapgenV6::block_is_underground(u64 seed, v3s16 blockpos)
 }
 
 
-double MapgenV6::base_rock_level_2d(u64 seed, v2s16 p)
-{
-	if (flags & MG_FLAT)
-		return water_level;
-	
-	int index = (p.Y - node_min.Z) * ystride + (p.X - node_min.X);
-
-	// The base ground level
-	/*double base = (double)WATER_LEVEL - (double)AVERAGE_MUD_AMOUNT
-			+ 20. * noise2d_perlin(
-			0.5+(float)p.X/250., 0.5+(float)p.Y/250.,
-			seed+82341, 5, 0.6);*/
-	double base = water_level + map_terrain_base[index];
+//////////////////////// Base terrain height functions
 
-	// Higher ground level
-	/*double higher = (double)WATER_LEVEL + 20. + 16. * noise2d_perlin(
-			0.5+(float)p.X/500., 0.5+(float)p.Y/500.,
-			seed+85039, 5, 0.6);*/
-	double higher = water_level + map_terrain_higher[index];
+float MapgenV6::baseTerrainLevel(float terrain_base, float terrain_higher,
+									float steepness, float height_select) {	
+	float base   = water_level + terrain_base;
+	float higher = water_level + terrain_higher;
 
-	// Limit higher to at least base
+	// Limit higher ground level to at least base
 	if(higher < base)
 		higher = base;
 
 	// Steepness factor of cliffs
-	/*double b = 0.85 + 0.5 * noise2d_perlin(
-			0.5+(float)p.X/125., 0.5+(float)p.Y/125.,
-			seed-932, 5, 0.7);*/
-	double b = map_steepness[index];
+	float b = steepness;
 	b = rangelim(b, 0.0, 1000.0);
-	b = pow(b, 7);
-	b *= 5;
+	b = 5 * b * b * b * b * b * b * b;
 	b = rangelim(b, 0.5, 1000.0);
 
 	// Values 1.5...100 give quite horrible looking slopes
-	if(b > 1.5 && b < 100.0){
-		if(b < 10.0)
-			b = 1.5;
-		else
-			b = 100.0;
-	}
+	if (b > 1.5 && b < 100.0)
+		b = (b < 10.0) ? 1.5 : 100.0;
 
-	// Offset to more low
-	double a_off = -0.20;
+	float a_off = -0.20; // Offset to more low
+	float a = 0.5 + b * (a_off + height_select);
+	a = rangelim(a, 0.0, 1.0); // Limit
+	
+	return base * (1.0 - a) + higher * a;
+}
 
-	// High/low selector
-	/*double a = (double)0.5 + b * (a_off + noise2d_perlin(
-			0.5+(float)p.X/250., 0.5+(float)p.Y/250.,
-			seed+4213, 5, 0.69));*/
-	double a = 0.5 + b * (a_off + map_height_select[index]);
 
-	// Limit
-	a = rangelim(a, 0.0, 1.0);
+float MapgenV6::baseTerrainLevelFromNoise(v2s16 p) {
+	if (flags & MG_FLAT)
+		return water_level;
+		
+	float terrain_base   = NoisePerlin2DPosOffset(noise_terrain_base->np,
+							p.X, 0.5, p.Y, 0.5, seed);
+	float terrain_higher = NoisePerlin2DPosOffset(noise_terrain_higher->np,
+							p.X, 0.5, p.Y, 0.5, seed);
+	float steepness      = NoisePerlin2DPosOffset(noise_steepness->np,
+							p.X, 0.5, p.Y, 0.5, seed);
+	float height_select  = NoisePerlin2DNoTxfmPosOffset(noise_height_select->np,
+							p.X, 0.5, p.Y, 0.5, seed);
+
+	return baseTerrainLevel(terrain_base, terrain_higher,
+							steepness,    height_select);
+}
 
-	double h = base*(1.0-a) + higher*a;
 
-	return h;
+float MapgenV6::baseTerrainLevelFromMap(v2s16 p) {
+	int index = (p.Y - node_min.Z) * ystride + (p.X - node_min.X);
+	return baseTerrainLevelFromMap(index);
 }
 
-double MapgenV6::baseRockLevelFromNoise(v2s16 p) {
+
+float MapgenV6::baseTerrainLevelFromMap(int index) {
 	if (flags & MG_FLAT)
 		return water_level;
 	
-	double base = water_level + 
-		NoisePerlin2DPosOffset(noise_terrain_base->np, p.X, 0.5, p.Y, 0.5, seed);
-	double higher = water_level +
-		NoisePerlin2DPosOffset(noise_terrain_higher->np, p.X, 0.5, p.Y, 0.5, seed);
+	float terrain_base   = noise_terrain_base->result[index];
+	float terrain_higher = noise_terrain_higher->result[index];
+	float steepness      = noise_steepness->result[index];
+	float height_select  = noise_height_select->result[index];
+	
+	return baseTerrainLevel(terrain_base, terrain_higher,
+							steepness,    height_select);
+}
 
-	if (higher < base)
-		higher = base;
 
-	double b = NoisePerlin2DPosOffset(noise_steepness->np, p.X, 0.5, p.Y, 0.5, seed);
-	b = rangelim(b, 0.0, 1000.0);
-	b = b*b*b*b*b*b*b;
-	b *= 5;
-	b = rangelim(b, 0.5, 1000.0);
+s16 MapgenV6::find_ground_level_from_noise(u64 seed, v2s16 p2d, s16 precision) {
+	return baseTerrainLevelFromNoise(p2d) + AVERAGE_MUD_AMOUNT;
+}
 
-	if(b > 1.5 && b < 100.0){
-		if(b < 10.0)
-			b = 1.5;
-		else
-			b = 100.0;
-	}
-	
-	double a_off = -0.20;
-	double a = 0.5 + b * (a_off + NoisePerlin2DNoTxfmPosOffset(
-			noise_height_select->np, p.X, 0.5, p.Y, 0.5, seed));
-	a = rangelim(a, 0.0, 1.0);
 
-	return base * (1.0 - a) + higher * a;
+int MapgenV6::getGroundLevelAtPoint(v2s16 p) {
+	return baseTerrainLevelFromNoise(p) + AVERAGE_MUD_AMOUNT;
 }
 
 
-s16 MapgenV6::find_ground_level_from_noise(u64 seed, v2s16 p2d, s16 precision)
+//////////////////////// Noise functions
+
+float MapgenV6::getMudAmount(v2s16 p) {
+	int index = (p.Y - node_min.Z) * ystride + (p.X - node_min.X);
+	return getMudAmount(index);
+}
+
+
+bool MapgenV6::getHaveBeach(v2s16 p) {
+	int index = (p.Y - node_min.Z) * ystride + (p.X - node_min.X);
+	return getHaveBeach(index);
+}
+
+
+BiomeType MapgenV6::getBiome(v2s16 p) {
+	int index = (p.Y - node_min.Z) * ystride + (p.X - node_min.X);
+	return getBiome(index, p);
+}
+
+
+float MapgenV6::getHumidity(v2s16 p)
 {
-	return baseRockLevelFromNoise(p2d) + AVERAGE_MUD_AMOUNT;
+	/*double noise = noise2d_perlin(
+		0.5+(float)p.X/500, 0.5+(float)p.Y/500,
+		seed+72384, 4, 0.66);
+	noise = (noise + 1.0)/2.0;*/
+
+	float noise = NoisePerlin2D(np_humidity, p.X, p.Y, seed);
+
+	if (noise < 0.0)
+		noise = 0.0;
+	if (noise > 1.0)
+		noise = 1.0;
+	return noise;
 }
 
-double MapgenV6::get_mud_add_amount(u64 seed, v2s16 p)
+
+float MapgenV6::getTreeAmount(v2s16 p)
+{
+	/*double noise = noise2d_perlin(
+			0.5+(float)p.X/125, 0.5+(float)p.Y/125,
+			seed+2, 4, 0.66);*/
+	
+	float noise = NoisePerlin2D(np_trees, p.X, p.Y, seed);
+	float zeroval = -0.39;
+	if (noise < zeroval)
+		return 0;
+	else
+		return 0.04 * (noise-zeroval) / (1.0-zeroval);
+}
+
+
+bool MapgenV6::getHaveAppleTree(v2s16 p)
+{
+	/*is_apple_tree = noise2d_perlin(
+		0.5+(float)p.X/100, 0.5+(float)p.Z/100,
+		data->seed+342902, 3, 0.45) > 0.2;*/
+	
+	float noise = NoisePerlin2D(np_apple_trees, p.X, p.Y, seed);
+	
+	return noise > 0.2;
+}
+
+
+float MapgenV6::getMudAmount(int index)
 {
 	if (flags & MG_FLAT)
 		return AVERAGE_MUD_AMOUNT;
@@ -383,39 +310,42 @@ double MapgenV6::get_mud_add_amount(u64 seed, v2s16 p)
 	/*return ((float)AVERAGE_MUD_AMOUNT + 2.0 * noise2d_perlin(
 			0.5+(float)p.X/200, 0.5+(float)p.Y/200,
 			seed+91013, 3, 0.55));*/
-	int index = (p.Y - node_min.Z) * ystride + (p.X - node_min.X);
-	return map_mud[index];
+	
+	return noise_mud->result[index];
 }
 
-bool MapgenV6::get_have_beach(u64 seed, v2s16 p2d)
+
+bool MapgenV6::getHaveBeach(int index)
 {
 	// Determine whether to have sand here
 	/*double sandnoise = noise2d_perlin(
 			0.2+(float)p2d.X/250, 0.7+(float)p2d.Y/250,
 			seed+59420, 3, 0.50);*/
-	int index = (p2d.Y - node_min.Z) * ystride + (p2d.X - node_min.X);
-	double sandnoise = map_beach[index];
-
+	
+	float sandnoise = noise_beach->result[index];
 	return (sandnoise > freq_beach);
 }
 
-BiomeType MapgenV6::get_biome(u64 seed, v2s16 p2d)
+
+BiomeType MapgenV6::getBiome(int index, v2s16 p)
 {
 	// Just do something very simple as for now
 	/*double d = noise2d_perlin(
 			0.6+(float)p2d.X/250, 0.2+(float)p2d.Y/250,
 			seed+9130, 3, 0.50);*/
-	int index = (p2d.Y - node_min.Z) * ystride + (p2d.X - node_min.X);
-	double d = map_biome[index];
-	if(d > freq_desert)
+	
+	float d = noise_biome->result[index];
+	if (d > freq_desert)
 		return BT_DESERT;
-	if (flags & MGV6_BIOME_BLEND) {
-		if(d > freq_desert - 0.10 &&
-			 (noise2d(p2d.X, p2d.Y, seed) + 1.0) > (freq_desert - d) * 20.0)
-			return BT_DESERT;
-	}
+		
+	if ((flags & MGV6_BIOME_BLEND) &&
+		(d > freq_desert - 0.10) &&
+		((noise2d(p.X, p.Y, seed) + 1.0) > (freq_desert - d) * 20.0))
+		return BT_DESERT;
+	
 	return BT_NORMAL;
-};
+}
+
 
 u32 MapgenV6::get_blockseed(u64 seed, v3s16 p)
 {
@@ -424,275 +354,622 @@ u32 MapgenV6::get_blockseed(u64 seed, v3s16 p)
 }
 
 
-int MapgenV6::getGroundLevelAtPoint(v2s16 p) {
-	return baseRockLevelFromNoise(p) + AVERAGE_MUD_AMOUNT;
-}
-
-#define VMANIP_FLAG_CAVE VOXELFLAG_CHECKED1
-
-void MapgenV6::makeChunk(BlockMakeData *data)
-{
-	this->generating = true;
+//////////////////////// Map generator
 
+void MapgenV6::makeChunk(BlockMakeData *data) {
 	assert(data->vmanip);
 	assert(data->nodedef);
 	assert(data->blockpos_requested.X >= data->blockpos_min.X &&
-			data->blockpos_requested.Y >= data->blockpos_min.Y &&
-			data->blockpos_requested.Z >= data->blockpos_min.Z);
+		   data->blockpos_requested.Y >= data->blockpos_min.Y &&
+		   data->blockpos_requested.Z >= data->blockpos_min.Z);
 	assert(data->blockpos_requested.X <= data->blockpos_max.X &&
-			data->blockpos_requested.Y <= data->blockpos_max.Y &&
-			data->blockpos_requested.Z <= data->blockpos_max.Z);
-
-	INodeDefManager *ndef = data->nodedef;
-
-	// Hack: use minimum block coordinates for old code that assumes
-	// a single block
+		   data->blockpos_requested.Y <= data->blockpos_max.Y &&
+		   data->blockpos_requested.Z <= data->blockpos_max.Z);
+			
+	this->generating = true;
+	this->vm   = data->vmanip;	
+	this->ndef = data->nodedef;
+	
+	// Hack: use minimum block coords for old code that assumes a single block
 	v3s16 blockpos = data->blockpos_requested;
-
-	/*dstream<<"makeBlock(): ("<<blockpos.X<<","<<blockpos.Y<<","
-			<<blockpos.Z<<")"<<std::endl;*/
-
 	v3s16 blockpos_min = data->blockpos_min;
 	v3s16 blockpos_max = data->blockpos_max;
 	v3s16 blockpos_full_min = blockpos_min - v3s16(1,1,1);
 	v3s16 blockpos_full_max = blockpos_max + v3s16(1,1,1);
 
-	ManualMapVoxelManipulator &vmanip = *(data->vmanip);
 	// Area of central chunk
 	node_min = blockpos_min*MAP_BLOCKSIZE;
 	node_max = (blockpos_max+v3s16(1,1,1))*MAP_BLOCKSIZE-v3s16(1,1,1);
+
 	// Full allocated area
-	v3s16 full_node_min = (blockpos_min-1)*MAP_BLOCKSIZE;
-	v3s16 full_node_max = (blockpos_max+2)*MAP_BLOCKSIZE-v3s16(1,1,1);
+	full_node_min = (blockpos_min-1)*MAP_BLOCKSIZE;
+	full_node_max = (blockpos_max+2)*MAP_BLOCKSIZE-v3s16(1,1,1);
 
-	v3s16 central_area_size = node_max - node_min + v3s16(1,1,1);
+	central_area_size = node_max - node_min + v3s16(1,1,1);
+	assert(central_area_size.X == central_area_size.Z);
+
+	int volume_blocks = (blockpos_max.X - blockpos_min.X + 1)
+					  * (blockpos_max.Y - blockpos_min.Y + 1)
+					  * (blockpos_max.Z - blockpos_max.Z + 1);
+
+	volume_nodes = volume_blocks *
+		MAP_BLOCKSIZE * MAP_BLOCKSIZE * MAP_BLOCKSIZE;
+
+	// Create a block-specific seed
+	blockseed = get_blockseed(data->seed, full_node_min);
+
+	// Make some noise
+	calculateNoise();
+
+	c_stone           = ndef->getId("mapgen_stone");
+	c_dirt            = ndef->getId("mapgen_dirt");
+	c_dirt_with_grass = ndef->getId("mapgen_dirt_with_grass");
+	c_sand            = ndef->getId("mapgen_sand");
+	c_water_source    = ndef->getId("mapgen_water_source");
+	c_lava_source     = ndef->getId("mapgen_lava_source");
+	c_gravel          = ndef->getId("mapgen_gravel");
+	c_cobble          = ndef->getId("mapgen_cobble");
+	c_desert_sand     = ndef->getId("mapgen_desert_sand");
+	c_desert_stone    = ndef->getId("mapgen_desert_stone");
+	if (c_desert_sand == CONTENT_IGNORE)
+		c_desert_sand = c_sand;
+	if (c_desert_stone == CONTENT_IGNORE)
+		c_desert_stone = c_stone;
+
+	// Maximum height of the stone surface and obstacles.
+	// This is used to guide the cave generation
+	s16 stone_surface_max_y;
+
+	// Generate general ground level to full area
+	stone_surface_max_y = generateGround();
+
+	generateSomething();
 
 	const s16 max_spread_amount = MAP_BLOCKSIZE;
+	// Limit dirt flow area by 1 because mud is flown into neighbors.
+	s16 mudflow_minpos = -max_spread_amount + 1;
+	s16 mudflow_maxpos = central_area_size.X + max_spread_amount - 2;
 
-	int volume_blocks = (blockpos_max.X - blockpos_min.X + 1)
-			* (blockpos_max.Y - blockpos_min.Y + 1)
-			* (blockpos_max.Z - blockpos_max.Z + 1);
-
-	int volume_nodes = volume_blocks *
-			MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
-
-	// Generated surface area
-	//double gen_area_nodes = MAP_BLOCKSIZE*MAP_BLOCKSIZE * rel_volume;
-
-	// Horribly wrong heuristic, but better than nothing
-	bool block_is_underground = (water_level > node_max.Y);
-
-	/*
-		Create a block-specific seed
-	*/
-	u32 blockseed = get_blockseed(data->seed, full_node_min);
-
-	/*
-		Make some noise
-	*/
-	{
-		int x = node_min.X;
-		int z = node_min.Z;
-
-		// Need to adjust for the original implementation's +.5 offset...
-		if (!(flags & MG_FLAT)) {
-			noise_terrain_base->perlinMap2D(
-				x + 0.5 * noise_terrain_base->np->spread.X,
-				z + 0.5 * noise_terrain_base->np->spread.Z);
-			noise_terrain_base->transformNoiseMap();
-
-			noise_terrain_higher->perlinMap2D(
-				x + 0.5 * noise_terrain_higher->np->spread.X,
-				z + 0.5 * noise_terrain_higher->np->spread.Z);
-			noise_terrain_higher->transformNoiseMap();
-
-			noise_steepness->perlinMap2D(
-				x + 0.5 * noise_steepness->np->spread.X,
-				z + 0.5 * noise_steepness->np->spread.Z);
-			noise_steepness->transformNoiseMap();
-
-			noise_height_select->perlinMap2D(
-				x + 0.5 * noise_height_select->np->spread.X,
-				z + 0.5 * noise_height_select->np->spread.Z);
-		}
-		
-		noise_trees->perlinMap2D(
-			x + 0.5 * noise_trees->np->spread.X,
-			z + 0.5 * noise_trees->np->spread.Z);
-			
-		if (!(flags & MG_FLAT)) {
-			noise_mud->perlinMap2D(
-				x + 0.5 * noise_mud->np->spread.X,
-				z + 0.5 * noise_mud->np->spread.Z);
-			noise_mud->transformNoiseMap();
-		}
-		noise_beach->perlinMap2D(
-			x + 0.2 * noise_beach->np->spread.X,
-			z + 0.7 * noise_beach->np->spread.Z);
+	// Loop this part, it will make stuff look older and newer nicely
+	const u32 age_loops = 2;
+	for (u32 i_age = 0; i_age < age_loops; i_age++) { // Aging loop
+		// Make caves (this code is relatively horrible)
+		if (flags & MG_CAVES)
+			generateCaves(stone_surface_max_y);
+
+		// Add mud to the central chunk
+		addMud();
 
-		noise_biome->perlinMap2D(
-			x + 0.6 * noise_biome->np->spread.X,
-			z + 0.2 * noise_biome->np->spread.Z);
+		// Add blobs of dirt and gravel underground
+		addDirtGravelBlobs();
+
+		// Flow mud away from steep edges
+		flowMud(mudflow_minpos, mudflow_maxpos);
+
+	}
+	
+	// Add dungeons
+	if (flags & MG_DUNGEONS) {
+		DungeonGen dgen(ndef, data->seed, water_level);
+		dgen.generate(vm, blockseed, full_node_min, full_node_max);
 	}
+	
+	// Add top and bottom side of water to transforming_liquid queue
+	updateLiquid(&data->transforming_liquid, full_node_min, full_node_max);
 
+	// Grow grass
+	growGrass();
 
-	/*
-		Cache some ground type values for speed
-	*/
+	// Generate some trees, and add grass, if a jungle
+	if (flags & MG_TREES)
+		placeTreesAndJungleGrass();
 
-// Creates variables c_name=id and n_name=node
-#define CONTENT_VARIABLE(ndef, name)\
-	content_t c_##name = ndef->getId("mapgen_" #name);\
-	MapNode n_##name(c_##name);
-// Default to something else if was CONTENT_IGNORE
-#define CONTENT_VARIABLE_FALLBACK(name, dname)\
-	if(c_##name == CONTENT_IGNORE){\
-		c_##name = c_##dname;\
-		n_##name = n_##dname;\
+	// Generate the registered ores
+	for (unsigned int i = 0; i != emerge->ores.size(); i++) {
+		Ore *ore = emerge->ores[i];
+		ore->generate(this, blockseed + i, node_min, node_max);
 	}
 
-	CONTENT_VARIABLE(ndef, stone);
-	CONTENT_VARIABLE(ndef, air);
-	CONTENT_VARIABLE(ndef, water_source);
-	CONTENT_VARIABLE(ndef, dirt);
-	CONTENT_VARIABLE(ndef, sand);
-	CONTENT_VARIABLE(ndef, gravel);
-	CONTENT_VARIABLE(ndef, clay);
-	CONTENT_VARIABLE(ndef, lava_source);
-	CONTENT_VARIABLE(ndef, cobble);
-	CONTENT_VARIABLE(ndef, mossycobble);
-	CONTENT_VARIABLE(ndef, dirt_with_grass);
-	CONTENT_VARIABLE(ndef, junglegrass);
-	CONTENT_VARIABLE(ndef, stone_with_coal);
-	CONTENT_VARIABLE(ndef, stone_with_iron);
-	CONTENT_VARIABLE(ndef, mese);
-	CONTENT_VARIABLE(ndef, desert_sand);
-	CONTENT_VARIABLE_FALLBACK(desert_sand, sand);
-	CONTENT_VARIABLE(ndef, desert_stone);
-	CONTENT_VARIABLE_FALLBACK(desert_stone, stone);
+	// Calculate lighting
+	calcLighting(node_min, node_max);
+	
+	this->generating = false;
+}
 
-	// Maximum height of the stone surface and obstacles.
-	// This is used to guide the cave generation
-	s16 stone_surface_max_y = 0;
 
-	/*
-		Generate general ground level to full area
-	*/
-	{
-#if 1
-	TimeTaker timer1("Generating ground level");
+void MapgenV6::calculateNoise() {
+	int x = node_min.X;
+	int z = node_min.Z;
 
-	for(s16 x=node_min.X; x<=node_max.X; x++)
-	for(s16 z=node_min.Z; z<=node_max.Z; z++)
-	{
-		// Node position
-		v2s16 p2d = v2s16(x,z);
+	// Need to adjust for the original implementation's +.5 offset...
+	if (!(flags & MG_FLAT)) {
+		noise_terrain_base->perlinMap2D(
+			x + 0.5 * noise_terrain_base->np->spread.X,
+			z + 0.5 * noise_terrain_base->np->spread.Z);
+		noise_terrain_base->transformNoiseMap();
 
-		/*
-			Skip of already generated
-		*/
-		/*{
-			v3s16 p(p2d.X, node_min.Y, p2d.Y);
-			if(vmanip.m_data[vmanip.m_area.index(p)].d != CONTENT_AIR)
-				continue;
-		}*/
+		noise_terrain_higher->perlinMap2D(
+			x + 0.5 * noise_terrain_higher->np->spread.X,
+			z + 0.5 * noise_terrain_higher->np->spread.Z);
+		noise_terrain_higher->transformNoiseMap();
 
-		// Ground height at this point
-		float surface_y_f = 0.0;
+		noise_steepness->perlinMap2D(
+			x + 0.5 * noise_steepness->np->spread.X,
+			z + 0.5 * noise_steepness->np->spread.Z);
+		noise_steepness->transformNoiseMap();
 
-		// Use perlin noise for ground height
-		surface_y_f = base_rock_level_2d(data->seed, p2d);
+		noise_height_select->perlinMap2D(
+			x + 0.5 * noise_height_select->np->spread.X,
+			z + 0.5 * noise_height_select->np->spread.Z);
 
-		/*// Experimental stuff
-		{
-			float a = highlands_level_2d(data->seed, p2d);
-			if(a > surface_y_f)
-				surface_y_f = a;
-		}*/
+		noise_mud->perlinMap2D(
+			x + 0.5 * noise_mud->np->spread.X,
+			z + 0.5 * noise_mud->np->spread.Z);
+		noise_mud->transformNoiseMap();
+	}
 
-		// Convert to integer
-		s16 surface_y = (s16)surface_y_f;
+	noise_beach->perlinMap2D(
+		x + 0.2 * noise_beach->np->spread.X,
+		z + 0.7 * noise_beach->np->spread.Z);
 
+	noise_biome->perlinMap2D(
+		x + 0.6 * noise_biome->np->spread.X,
+		z + 0.2 * noise_biome->np->spread.Z);
+}
+
+
+int MapgenV6::generateGround() {
+	//TimeTaker timer1("Generating ground level");
+	MapNode n_air(CONTENT_AIR), n_water_source(c_water_source);
+	MapNode n_stone(c_stone), n_desert_stone(c_desert_stone);
+	int stone_surface_max_y = -MAP_GENERATION_LIMIT;
+	u32 index = 0;
+	
+	for (s16 z = node_min.Z; z <= node_max.Z; z++)
+	for (s16 x = node_min.X; x <= node_max.X; x++, index++) {
+		// Surface height
+		s16 surface_y = (s16)baseTerrainLevelFromMap(index);
+		
 		// Log it
-		if(surface_y > stone_surface_max_y)
+		if (surface_y > stone_surface_max_y)
 			stone_surface_max_y = surface_y;
 
-		BiomeType bt = get_biome(data->seed, p2d);
-		/*
-			Fill ground with stone
-		*/
-		{
-			// Use fast index incrementing
-			v3s16 em = vmanip.m_area.getExtent();
-			u32 i = vmanip.m_area.index(v3s16(p2d.X, node_min.Y, p2d.Y));
-			for(s16 y=node_min.Y; y<=node_max.Y; y++)
+		BiomeType bt = getBiome(index, v2s16(x, z));
+		
+		// Fill ground with stone
+		v3s16 em = vm->m_area.getExtent();
+		u32 i = vm->m_area.index(x, node_min.Y, z);
+		for (s16 y = node_min.Y; y <= node_max.Y; y++) {
+			if (vm->m_data[i].getContent() == CONTENT_IGNORE) {
+				if (y <= surface_y) {
+					vm->m_data[i] = (y > water_level && bt == BT_DESERT) ? 
+						n_desert_stone : n_stone;
+				} else if (y <= water_level) {
+					vm->m_data[i] = n_water_source;
+				} else {
+					vm->m_data[i] = n_air;
+				}
+			}
+			vm->m_area.add_y(em, i, 1);
+		}
+	}
+	
+	return stone_surface_max_y;
+}
+
+
+void MapgenV6::addMud() {
+	// 15ms @cs=8
+	//TimeTaker timer1("add mud");
+	MapNode n_dirt(c_dirt), n_gravel(c_gravel);
+	MapNode n_sand(c_sand), n_desert_sand(c_desert_sand);
+	MapNode addnode;
+
+	u32 index = 0;
+	for (s16 z = node_min.Z; z <= node_max.Z; z++)
+	for (s16 x = node_min.X; x <= node_max.X; x++, index++) {
+		// Randomize mud amount
+		s16 mud_add_amount = getMudAmount(index) / 2.0 + 0.5;
+
+		// Find ground level
+		s16 surface_y = find_stone_level(v2s16(x, z)); /////////////////optimize this!
+		
+		// Handle area not found
+		if (surface_y == vm->m_area.MinEdge.Y - 1)
+			continue;
+		
+		BiomeType bt = getBiome(index, v2s16(x, z));
+		addnode = (bt == BT_DESERT) ? n_desert_sand : n_dirt;
+
+		if (bt == BT_DESERT && surface_y + mud_add_amount <= water_level + 1) {
+			addnode = n_sand;
+		} else if (mud_add_amount <= 0) {
+			mud_add_amount = 1 - mud_add_amount;
+			addnode = n_gravel;
+		} else if (bt == BT_NORMAL && getHaveBeach(index) &&
+				surface_y + mud_add_amount <= water_level + 2) {
+			addnode = n_sand;
+		}
+
+		if (bt == BT_DESERT && surface_y > 20)
+			mud_add_amount = MYMAX(0, mud_add_amount - (surface_y - 20) / 5);
+
+		// If topmost node is grass, change it to mud.  It might be if it was
+		// flown to there from a neighboring chunk and then converted.
+		u32 i = vm->m_area.index(x, surface_y, z);
+		if (vm->m_data[i].getContent() == c_dirt_with_grass)
+			vm->m_data[i] = n_dirt;
+
+		// Add mud on ground
+		s16 mudcount = 0;
+		v3s16 em = vm->m_area.getExtent();
+		s16 y_start = surface_y + 1;
+		i = vm->m_area.index(x, y_start, z);
+		for (s16 y = y_start; y <= node_max.Y; y++) {
+			if (mudcount >= mud_add_amount)
+				break;
+
+			vm->m_data[i] = addnode;
+			mudcount++;
+
+			vm->m_area.add_y(em, i, 1);
+		}
+	}
+}
+
+
+void MapgenV6::flowMud(s16 &mudflow_minpos, s16 &mudflow_maxpos) {
+	// 340ms @cs=8
+	TimeTaker timer1("flow mud");
+
+	// Iterate a few times
+	for(s16 k = 0; k < 3; k++) {
+		for (s16 z = mudflow_minpos; z <= mudflow_maxpos; z++)
+		for (s16 x = mudflow_minpos; x <= mudflow_maxpos; x++) {
+			// Invert coordinates every 2nd iteration
+			if (k % 2 == 0) {
+				x = mudflow_maxpos - (x - mudflow_minpos);
+				z = mudflow_maxpos - (z - mudflow_minpos);
+			}
+
+			// Node position in 2d
+			v2s16 p2d = v2s16(node_min.X, node_min.Z) + v2s16(x, z);
+
+			v3s16 em = vm->m_area.getExtent();
+			u32 i = vm->m_area.index(p2d.X, node_max.Y, p2d.Y);
+			s16 y = node_max.Y;
+
+			while(y >= node_min.Y)
+			{
+
+			for(;; y--)
 			{
-				if(vmanip.m_data[i].getContent() == CONTENT_IGNORE){
-					if(y <= surface_y){
-						if(y > water_level && bt == BT_DESERT)
-							vmanip.m_data[i] = n_desert_stone;
+				MapNode *n = NULL;
+				// Find mud
+				for(; y >= node_min.Y; y--) {
+					n = &vm->m_data[i];
+					if (n->getContent() == c_dirt ||
+						n->getContent() == c_dirt_with_grass ||
+						n->getContent() == c_gravel)
+						break;
+
+					vm->m_area.add_y(em, i, -1);
+				}
+
+				// Stop if out of area
+				//if(vmanip.m_area.contains(i) == false)
+				if (y < node_min.Y)
+					break;
+
+				if (n->getContent() == c_dirt ||
+					n->getContent() == c_dirt_with_grass)
+				{
+					// Make it exactly mud
+					n->setContent(c_dirt);
+
+					// Don't flow it if the stuff under it is not mud
+					{
+						u32 i2 = i;
+						vm->m_area.add_y(em, i2, -1);
+						// Cancel if out of area
+						if(vm->m_area.contains(i2) == false)
+							continue;
+						MapNode *n2 = &vm->m_data[i2];
+						if (n2->getContent() != c_dirt &&
+							n2->getContent() != c_dirt_with_grass)
+							continue;
+					}
+				}
+
+				v3s16 dirs4[4] = {
+					v3s16(0,0,1), // back
+					v3s16(1,0,0), // right
+					v3s16(0,0,-1), // front
+					v3s16(-1,0,0), // left
+				};
+
+				// Check that upper is air or doesn't exist.
+				// Cancel dropping if upper keeps it in place
+				u32 i3 = i;
+				vm->m_area.add_y(em, i3, 1);
+				if (vm->m_area.contains(i3) == true &&
+					ndef->get(vm->m_data[i3]).walkable)
+					continue;
+
+				// Drop mud on side
+				for(u32 di=0; di<4; di++) {
+					v3s16 dirp = dirs4[di];
+					u32 i2 = i;
+					// Move to side
+					vm->m_area.add_p(em, i2, dirp);
+					// Fail if out of area
+					if (vm->m_area.contains(i2) == false)
+						continue;
+					// Check that side is air
+					MapNode *n2 = &vm->m_data[i2];
+					if (ndef->get(*n2).walkable)
+						continue;
+					// Check that under side is air
+					vm->m_area.add_y(em, i2, -1);
+					if (vm->m_area.contains(i2) == false)
+						continue;
+					n2 = &vm->m_data[i2];
+					if (ndef->get(*n2).walkable)
+						continue;
+					// Loop further down until not air
+					bool dropped_to_unknown = false;
+					do {
+						vm->m_area.add_y(em, i2, -1);
+						n2 = &vm->m_data[i2];
+						// if out of known area
+						if(vm->m_area.contains(i2) == false ||
+							n2->getContent() == CONTENT_IGNORE) {
+							dropped_to_unknown = true;
+							break;
+						}
+					} while (ndef->get(*n2).walkable == false);
+					// Loop one up so that we're in air
+					vm->m_area.add_y(em, i2, 1);
+					n2 = &vm->m_data[i2];
+
+					bool old_is_water = (n->getContent() == c_water_source);
+					// Move mud to new place
+					if (!dropped_to_unknown) {
+						*n2 = *n;
+						// Set old place to be air (or water)
+						if(old_is_water)
+							*n = MapNode(c_water_source);
 						else
-							vmanip.m_data[i] = n_stone;
-					} else if(y <= water_level){
-						vmanip.m_data[i] = MapNode(c_water_source);
-					} else {
-						vmanip.m_data[i] = MapNode(c_air);
+							*n = MapNode(CONTENT_AIR);
 					}
+
+					// Done
+					break;
 				}
-				vmanip.m_area.add_y(em, i, 1);
+			}
 			}
 		}
 	}
-#endif
+}
 
-	}//timer1
 
-	// Limit dirt flow area by 1 because mud is flown into neighbors.
-	assert(central_area_size.X == central_area_size.Z);
-	s16 mudflow_minpos = 0-max_spread_amount+1;
-	s16 mudflow_maxpos = central_area_size.X+max_spread_amount-2;
+void MapgenV6::addDirtGravelBlobs() {
+	if (getBiome(v2s16(node_min.X, node_min.Z)) != BT_NORMAL)
+		return;
+	
+	PseudoRandom pr(blockseed + 983);
+	for (int i = 0; i < volume_nodes/10/10/10; i++) {
+		bool only_fill_cave = (myrand_range(0,1) != 0);
+		v3s16 size(
+			pr.range(1, 8),
+			pr.range(1, 8),
+			pr.range(1, 8)
+		);
+		v3s16 p0(
+			pr.range(node_min.X, node_max.X) - size.X / 2,
+			pr.range(node_min.Y, node_max.Y) - size.Y / 2,
+			pr.range(node_min.Z, node_max.Z) - size.Z / 2
+		);
+		
+		MapNode n1((p0.Y > -32 && !pr.range(0, 1)) ? c_dirt : c_gravel);
+		for (int z1 = 0; z1 < size.Z; z1++)
+		for (int y1 = 0; y1 < size.Y; y1++)
+		for (int x1 = 0; x1 < size.X; x1++) {
+			v3s16 p = p0 + v3s16(x1, y1, z1);
+			u32 i = vm->m_area.index(p);
+			if (!vm->m_area.contains(i))
+				continue;
+			// Cancel if not stone and not cave air
+			if (vm->m_data[i].getContent() != c_stone &&
+				!(vm->m_flags[i] & VMANIP_FLAG_CAVE))
+				continue;
+			if (only_fill_cave && !(vm->m_flags[i] & VMANIP_FLAG_CAVE))
+				continue;
+			vm->m_data[i] = n1;
+		}
+	}
+}
 
-	/*
-		Loop this part, it will make stuff look older and newer nicely
-	*/
 
-	/*double cave_amount = 6.0 + 6.0 * noise2d_perlin(
-			0.5+(double)node_min.X/250, 0.5+(double)node_min.Y/250,
-			data->seed+34329, 3, 0.50);*/
+void MapgenV6::placeTreesAndJungleGrass() {
+	//TimeTaker t("placeTrees");
+	if (node_max.Y < water_level)
+		return;
+	
+	PseudoRandom grassrandom(blockseed + 53);
+	content_t c_junglegrass = ndef->getId("mapgen_junglegrass");
+	// if we don't have junglegrass, don't place cignore... that's bad
+	if (c_junglegrass == CONTENT_IGNORE)
+		c_junglegrass = CONTENT_AIR;
+	MapNode n_junglegrass(c_junglegrass);
+	v3s16 em = vm->m_area.getExtent();
+	
+	// Divide area into parts
+	s16 div = 8;
+	s16 sidelen = central_area_size.X / div;
+	double area = sidelen * sidelen;
+	
+	// N.B.  We must add jungle grass first, since tree leaves will
+	// obstruct the ground, giving us a false ground level
+	for (s16 z0 = 0; z0 < div; z0++)
+	for (s16 x0 = 0; x0 < div; x0++) {
+		// Center position of part of division
+		v2s16 p2d_center(
+			node_min.X + sidelen / 2 + sidelen * x0,
+			node_min.Z + sidelen / 2 + sidelen * z0
+		);
+		// Minimum edge of part of division
+		v2s16 p2d_min(
+			node_min.X + sidelen * x0,
+			node_min.Z + sidelen * z0
+		);
+		// Maximum edge of part of division
+		v2s16 p2d_max(
+			node_min.X + sidelen + sidelen * x0 - 1,
+			node_min.Z + sidelen + sidelen * z0 - 1
+		);
+		
+		// Amount of trees, jungle area
+		u32 tree_count = area * getTreeAmount(p2d_center);
+		
+		float humidity;
+		bool is_jungle = false;
+		if (flags & MGV6_JUNGLES) {
+			humidity = getHumidity(p2d_center);
+			if (humidity > 0.75) {
+				is_jungle = true;
+				tree_count *= 4;
+			}
+		}
+
+		// Add jungle grass
+		if (is_jungle) {			
+			u32 grass_count = 5 * humidity * tree_count;
+			for (u32 i = 0; i < grass_count; i++) {
+				s16 x = grassrandom.range(p2d_min.X, p2d_max.X);
+				s16 z = grassrandom.range(p2d_min.Y, p2d_max.Y);
+				
+				s16 y = find_ground_level(v2s16(x, z)); ////////////////optimize this!
+				if (y < water_level || y < node_min.Y || y > node_max.Y)
+					continue;
+				
+				u32 vi = vm->m_area.index(x, y, z);
+				// place on dirt_with_grass, since we know it is exposed to sunlight
+				if (vm->m_data[vi].getContent() == c_dirt_with_grass) {
+					vm->m_area.add_y(em, vi, 1);
+					vm->m_data[vi] = n_junglegrass;
+				}
+			}
+		}
+		
+		// Put trees in random places on part of division
+		for (u32 i = 0; i < tree_count; i++) {
+			s16 x = myrand_range(p2d_min.X, p2d_max.X);
+			s16 z = myrand_range(p2d_min.Y, p2d_max.Y);
+			s16 y = find_ground_level(v2s16(x, z)); ////////////////////optimize this!
+			// Don't make a tree under water level
+			// Don't make a tree so high that it doesn't fit
+			if(y < water_level || y > node_max.Y - 6)
+				continue;
+			
+			v3s16 p(x,y,z);
+			// Trees grow only on mud and grass
+			{
+				u32 i = vm->m_area.index(p);
+				MapNode *n = &vm->m_data[i];
+				if (n->getContent() != c_dirt &&
+					n->getContent() != c_dirt_with_grass)
+					continue;
+			}
+			p.Y++;
+			
+			// Make a tree
+			if (is_jungle) {
+				treegen::make_jungletree(*vm, p, ndef, myrand());
+			} else {
+				bool is_apple_tree = (myrand_range(0, 3) == 0) &&
+										getHaveAppleTree(v2s16(x, z));
+				treegen::make_tree(*vm, p, is_apple_tree, ndef, myrand());
+			}
+		}
+	}
+	//printf("placeTreesAndJungleGrass: %dms\n", t.stop());
+}
 
-	double cave_amount = NoisePerlin2D(np_cave, node_min.X, node_min.Y, data->seed);
 
-	const u32 age_loops = 2;
-	for(u32 i_age=0; i_age<age_loops; i_age++)
-	{ // Aging loop
-	/******************************
-		BEGINNING OF AGING LOOP
-	******************************/
-
-#if 1
-	{
+void MapgenV6::growGrass() {
+	for (s16 z = full_node_min.Z; z <= full_node_max.Z; z++)
+	for (s16 x = full_node_min.X; x <= full_node_max.X; x++) {
+		// Find the lowest surface to which enough light ends up to make
+		// grass grow.  Basically just wait until not air and not leaves.
+		s16 surface_y = 0;
+		{
+			v3s16 em = vm->m_area.getExtent();
+			u32 i = vm->m_area.index(x, node_max.Y, z);
+			s16 y;
+			// Go to ground level
+			for (y = node_max.Y; y >= full_node_min.Y; y--) {
+				MapNode &n = vm->m_data[i];
+				if (ndef->get(n).param_type != CPT_LIGHT ||
+					ndef->get(n).liquid_type != LIQUID_NONE)
+					break;
+				vm->m_area.add_y(em, i, -1);
+			}
+			surface_y = (y >= full_node_min.Y) ? y : full_node_min.Y;
+		}
+
+		u32 i = vm->m_area.index(x, surface_y, z);
+		MapNode *n = &vm->m_data[i];
+		if (n->getContent() == c_dirt && surface_y >= water_level - 20)
+			n->setContent(c_dirt_with_grass);
+	}
+}
+
+
+void MapgenV6::defineCave(Cave &cave, PseudoRandom ps,
+						 v3s16 node_min, bool large_cave) {
+		cave.min_tunnel_diameter = 2;
+		cave.max_tunnel_diameter = ps.range(2,6);
+		cave.dswitchint = ps.range(1,14);
+		cave.flooded = true; //large_cave && ps.range(0,4);
+		if(large_cave){
+			cave.part_max_length_rs = ps.range(2,4);
+			cave.tunnel_routepoints = ps.range(5, ps.range(15,30));
+			cave.min_tunnel_diameter = 5;
+			cave.max_tunnel_diameter = ps.range(7, ps.range(8,24));
+		} else {
+			cave.part_max_length_rs = ps.range(2,9);
+			cave.tunnel_routepoints = ps.range(10, ps.range(15,30));
+		}
+		cave.large_cave_is_flat = (ps.range(0,1) == 0);
+}
+
+
+void MapgenV6::generateCaves(int max_stone_y) {
 	// 24ms @cs=8
 	//TimeTaker timer1("caves");
+	
+	/*double cave_amount = 6.0 + 6.0 * noise2d_perlin(
+		0.5+(double)node_min.X/250, 0.5+(double)node_min.Y/250,
+		data->seed+34329, 3, 0.50);*/
+	const s16 max_spread_amount = MAP_BLOCKSIZE;
+	float cave_amount = NoisePerlin2D(np_cave, node_min.X, node_min.Y, seed);
 
-	/*
-		Make caves (this code is relatively horrible)
-	*/
 	cave_amount = MYMAX(0.0, cave_amount);
 	u32 caves_count = cave_amount * volume_nodes / 50000;
 	u32 bruises_count = 1;
-	PseudoRandom ps(blockseed+21343);
-	PseudoRandom ps2(blockseed+1032);
-	if(ps.range(1, 6) == 1)
+	PseudoRandom ps(blockseed + 21343);
+	PseudoRandom ps2(blockseed + 1032);
+	
+	if (ps.range(1, 6) == 1)
 		bruises_count = ps.range(0, ps.range(0, 2));
-	if(get_biome(data->seed, v2s16(node_min.X, node_min.Z)) == BT_DESERT){
-		caves_count /= 3;
+	
+	if (getBiome(v2s16(node_min.X, node_min.Z)) == BT_DESERT) {
+		caves_count   /= 3;
 		bruises_count /= 3;
 	}
-	for(u32 jj=0; jj<caves_count+bruises_count; jj++)
-	{
-		if (!(flags & MG_CAVES))
-			continue;
-
+	
+	for(u32 jj = 0; jj < caves_count + bruises_count; jj++) {
 		/*int avg_height = (int)
 			  ((base_rock_level_2d(data->seed, v2s16(node_min.X, node_min.Z)) +
 				base_rock_level_2d(data->seed, v2s16(node_max.X, node_max.Z))) / 2);
@@ -700,21 +977,9 @@ void MapgenV6::makeChunk(BlockMakeData *data)
 			break;*/
 
 		bool large_cave = (jj >= caves_count);
-		s16 min_tunnel_diameter = 2;
-		s16 max_tunnel_diameter = ps.range(2,6);
-		int dswitchint = ps.range(1,14);
-		u16 tunnel_routepoints = 0;
-		int part_max_length_rs = 0;
-		if(large_cave){
-			part_max_length_rs = ps.range(2,4);
-			tunnel_routepoints = ps.range(5, ps.range(15,30));
-			min_tunnel_diameter = 5;
-			max_tunnel_diameter = ps.range(7, ps.range(8,24));
-		} else {
-			part_max_length_rs = ps.range(2,9);
-			tunnel_routepoints = ps.range(10, ps.range(15,30));
-		}
-		bool large_cave_is_flat = (ps.range(0,1) == 0);
+
+		Cave cave;
+		defineCave(cave, ps, node_min, large_cave);
 
 		v3f main_direction(0,0,0);
 
@@ -727,13 +992,13 @@ void MapgenV6::makeChunk(BlockMakeData *data)
 		// Allow a bit more
 		//(this should be more than the maximum radius of the tunnel)
 		s16 insure = 10;
-		s16 more = max_spread_amount - max_tunnel_diameter/2 - insure;
+		s16 more = max_spread_amount - cave.max_tunnel_diameter / 2 - insure;
 		ar += v3s16(1,0,1) * more * 2;
 		of -= v3s16(1,0,1) * more;
 
 		s16 route_y_min = 0;
 		// Allow half a diameter + 7 over stone surface
-		s16 route_y_max = -of.Y + stone_surface_max_y + max_tunnel_diameter/2 + 7;
+		s16 route_y_max = -of.Y + max_stone_y + cave.max_tunnel_diameter/2 + 7;
 
 		// Limit maximum to area
 		route_y_max = rangelim(route_y_max, 0, ar.Y-1);
@@ -743,10 +1008,10 @@ void MapgenV6::makeChunk(BlockMakeData *data)
 			s16 min = 0;
 			if(node_min.Y < water_level && node_max.Y > water_level)
 			{
-				min = water_level - max_tunnel_diameter/3 - of.Y;
-				route_y_max = water_level + max_tunnel_diameter/3 - of.Y;
+				min = water_level - cave.max_tunnel_diameter/3 - of.Y;
+				route_y_max = water_level + cave.max_tunnel_diameter/3 - of.Y;
 			}
-			route_y_min = ps.range(min, min + max_tunnel_diameter);
+			route_y_min = ps.range(min, min + cave.max_tunnel_diameter);
 			route_y_min = rangelim(route_y_min, 0, route_y_max);
 		}
 
@@ -774,9 +1039,9 @@ void MapgenV6::makeChunk(BlockMakeData *data)
 			Generate some tunnel starting from orp
 		*/
 
-		for(u16 j=0; j<tunnel_routepoints; j++)
+		for(u16 j=0; j<cave.tunnel_routepoints; j++)
 		{
-			if(j%dswitchint==0 && large_cave == false)
+			if(j%cave.dswitchint==0 && large_cave == false)
 			{
 				main_direction = v3f(
 					((float)(ps.next()%20)-(float)10)/10,
@@ -787,8 +1052,8 @@ void MapgenV6::makeChunk(BlockMakeData *data)
 			}
 
 			// Randomize size
-			s16 min_d = min_tunnel_diameter;
-			s16 max_d = max_tunnel_diameter;
+			s16 min_d = cave.min_tunnel_diameter;
+			s16 max_d = cave.max_tunnel_diameter;
 			s16 rs = ps.range(min_d, max_d);
 
 			// Every second section is rough
@@ -798,17 +1063,17 @@ void MapgenV6::makeChunk(BlockMakeData *data)
 			if(large_cave)
 			{
 				maxlen = v3s16(
-					rs*part_max_length_rs,
-					rs*part_max_length_rs/2,
-					rs*part_max_length_rs
+					rs*cave.part_max_length_rs,
+					rs*cave.part_max_length_rs/2,
+					rs*cave.part_max_length_rs
 				);
 			}
 			else
 			{
 				maxlen = v3s16(
-					rs*part_max_length_rs,
-					ps.range(1, rs*part_max_length_rs),
-					rs*part_max_length_rs
+					rs*cave.part_max_length_rs,
+					ps.range(1, rs*cave.part_max_length_rs),
+					rs*cave.part_max_length_rs
 				);
 			}
 
@@ -880,9 +1145,9 @@ void MapgenV6::makeChunk(BlockMakeData *data)
 							/*// Make better floors in small caves
 							if(y0 <= -rs/2 && rs<=7)
 								continue;*/
-							if(large_cave_is_flat){
+							if (cave.large_cave_is_flat) {
 								// Make large caves not so tall
-								if(rs > 7 && abs(y0) >= rs/3)
+								if (rs > 7 && abs(y0) >= rs/3)
 									continue;
 							}
 
@@ -892,543 +1157,44 @@ void MapgenV6::makeChunk(BlockMakeData *data)
 							v3s16 p(x,y,z);
 							p += of;
 
-							if(vmanip.m_area.contains(p) == false)
+							if(vm->m_area.contains(p) == false)
 								continue;
 
-							u32 i = vmanip.m_area.index(p);
+							u32 i = vm->m_area.index(p);
 
-							if(large_cave)
-							{
-								if(full_node_min.Y < water_level &&
-									full_node_max.Y > water_level){
-									if(p.Y <= water_level)
-										vmanip.m_data[i] = waternode;
+							if(large_cave) {
+								if (cave.flooded && full_node_min.Y < water_level &&
+									full_node_max.Y > water_level) {
+									if (p.Y <= water_level)
+										vm->m_data[i] = waternode;
 									else
-										vmanip.m_data[i] = airnode;
-								} else if(full_node_max.Y < water_level){
-									if(p.Y < startp.Y - 2)
-										vmanip.m_data[i] = lavanode;
+										vm->m_data[i] = airnode;
+								} else if (cave.flooded && full_node_max.Y < water_level) {
+									if (p.Y < startp.Y - 2)
+										vm->m_data[i] = lavanode;
 									else
-										vmanip.m_data[i] = airnode;
+										vm->m_data[i] = airnode;
 								} else {
-									vmanip.m_data[i] = airnode;
+									vm->m_data[i] = airnode;
 								}
 							} else {
 								// Don't replace air or water or lava or ignore
-								if(vmanip.m_data[i].getContent() == CONTENT_IGNORE ||
-								vmanip.m_data[i].getContent() == CONTENT_AIR ||
-								vmanip.m_data[i].getContent() == c_water_source ||
-								vmanip.m_data[i].getContent() == c_lava_source)
+								if (vm->m_data[i].getContent() == CONTENT_IGNORE ||
+									vm->m_data[i].getContent() == CONTENT_AIR ||
+									vm->m_data[i].getContent() == c_water_source ||
+									vm->m_data[i].getContent() == c_lava_source)
 									continue;
 
-								vmanip.m_data[i] = airnode;
+								vm->m_data[i] = airnode;
 
 								// Set tunnel flag
-								vmanip.m_flags[i] |= VMANIP_FLAG_CAVE;
+								vm->m_flags[i] |= VMANIP_FLAG_CAVE;
 							}
 						}
 					}
 				}
 			}
-
 			orp = rp;
 		}
-
 	}
-
-	}//timer1
-#endif
-
-#if 1
-	{
-	// 15ms @cs=8
-	TimeTaker timer1("add mud");
-
-	/*
-		Add mud to the central chunk
-	*/
-
-	for(s16 x=node_min.X; x<=node_max.X; x++)
-	for(s16 z=node_min.Z; z<=node_max.Z; z++)
-	{
-		// Node position in 2d
-		v2s16 p2d = v2s16(x,z);
-
-		// Randomize mud amount
-		s16 mud_add_amount = get_mud_add_amount(data->seed, p2d) / 2.0 + 0.5;
-
-		// Find ground level
-		s16 surface_y = find_stone_level(vmanip, p2d, ndef);
-		// Handle area not found
-		if(surface_y == vmanip.m_area.MinEdge.Y - 1)
-			continue;
-
-		MapNode addnode(c_dirt);
-		BiomeType bt = get_biome(data->seed, p2d);
-
-		if(bt == BT_DESERT)
-			addnode = MapNode(c_desert_sand);
-
-		if(bt == BT_DESERT && surface_y + mud_add_amount <= water_level+1){
-			addnode = MapNode(c_sand);
-		} else if(mud_add_amount <= 0){
-			mud_add_amount = 1 - mud_add_amount;
-			addnode = MapNode(c_gravel);
-		} else if(bt == BT_NORMAL && get_have_beach(data->seed, p2d) &&
-				surface_y + mud_add_amount <= water_level+2){
-			addnode = MapNode(c_sand);
-		}
-
-		if(bt == BT_DESERT){
-			if(surface_y > 20){
-				mud_add_amount = MYMAX(0, mud_add_amount - (surface_y - 20)/5);
-			}
-		}
-
-		/*
-			If topmost node is grass, change it to mud.
-			It might be if it was flown to there from a neighboring
-			chunk and then converted.
-		*/
-		{
-			u32 i = vmanip.m_area.index(v3s16(p2d.X, surface_y, p2d.Y));
-			MapNode *n = &vmanip.m_data[i];
-			if(n->getContent() == c_dirt_with_grass)
-				*n = MapNode(c_dirt);
-		}
-
-		/*
-			Add mud on ground
-		*/
-		{
-			s16 mudcount = 0;
-			v3s16 em = vmanip.m_area.getExtent();
-			s16 y_start = surface_y+1;
-			u32 i = vmanip.m_area.index(v3s16(p2d.X, y_start, p2d.Y));
-			for(s16 y=y_start; y<=node_max.Y; y++)
-			{
-				if(mudcount >= mud_add_amount)
-					break;
-
-				MapNode &n = vmanip.m_data[i];
-				n = addnode;
-				mudcount++;
-
-				vmanip.m_area.add_y(em, i, 1);
-			}
-		}
-
-	}
-
-	}//timer1
-#endif
-
-	/*
-		Add blobs of dirt and gravel underground
-	*/
-	if(get_biome(data->seed, v2s16(node_min.X, node_min.Z)) == BT_NORMAL)
-	{
-	PseudoRandom pr(blockseed+983);
-	for(int i=0; i<volume_nodes/10/10/10; i++)
-	{
-		bool only_fill_cave = (myrand_range(0,1) != 0);
-		v3s16 size(
-			pr.range(1, 8),
-			pr.range(1, 8),
-			pr.range(1, 8)
-		);
-		v3s16 p0(
-			pr.range(node_min.X, node_max.X)-size.X/2,
-			pr.range(node_min.Y, node_max.Y)-size.Y/2,
-			pr.range(node_min.Z, node_max.Z)-size.Z/2
-		);
-		MapNode n1;
-		if(p0.Y > -32 && pr.range(0,1) == 0)
-			n1 = MapNode(c_dirt);
-		else
-			n1 = MapNode(c_gravel);
-		for(int x1=0; x1<size.X; x1++)
-		for(int y1=0; y1<size.Y; y1++)
-		for(int z1=0; z1<size.Z; z1++)
-		{
-			v3s16 p = p0 + v3s16(x1,y1,z1);
-			u32 i = vmanip.m_area.index(p);
-			if(!vmanip.m_area.contains(i))
-				continue;
-			// Cancel if not stone and not cave air
-			if(vmanip.m_data[i].getContent() != c_stone &&
-					!(vmanip.m_flags[i] & VMANIP_FLAG_CAVE))
-				continue;
-			if(only_fill_cave && !(vmanip.m_flags[i] & VMANIP_FLAG_CAVE))
-				continue;
-			vmanip.m_data[i] = n1;
-		}
-	}
-	}
-
-#if 1
-	{
-	// 340ms @cs=8
-	TimeTaker timer1("flow mud");
-
-	/*
-		Flow mud away from steep edges
-	*/
-
-	// Iterate a few times
-	for(s16 k=0; k<3; k++)
-	{
-
-	for(s16 x=mudflow_minpos; x<=mudflow_maxpos; x++)
-	for(s16 z=mudflow_minpos; z<=mudflow_maxpos; z++)
-	{
-		// Invert coordinates every 2nd iteration
-		if(k%2 == 0)
-		{
-			x = mudflow_maxpos - (x-mudflow_minpos);
-			z = mudflow_maxpos - (z-mudflow_minpos);
-		}
-
-		// Node position in 2d
-		v2s16 p2d = v2s16(node_min.X, node_min.Z) + v2s16(x,z);
-
-		v3s16 em = vmanip.m_area.getExtent();
-		u32 i = vmanip.m_area.index(v3s16(p2d.X, node_max.Y, p2d.Y));
-		s16 y=node_max.Y;
-
-		while(y >= node_min.Y)
-		{
-
-		for(;; y--)
-		{
-			MapNode *n = NULL;
-			// Find mud
-			for(; y>=node_min.Y; y--)
-			{
-				n = &vmanip.m_data[i];
-				//if(content_walkable(n->d))
-				//	break;
-				if(n->getContent() == c_dirt ||
-						n->getContent() == c_dirt_with_grass ||
-						n->getContent() == c_gravel)
-					break;
-
-				vmanip.m_area.add_y(em, i, -1);
-			}
-
-			// Stop if out of area
-			//if(vmanip.m_area.contains(i) == false)
-			if(y < node_min.Y)
-				break;
-
-			/*// If not mud, do nothing to it
-			MapNode *n = &vmanip.m_data[i];
-			if(n->d != CONTENT_MUD && n->d != CONTENT_GRASS)
-				continue;*/
-
-			if(n->getContent() == c_dirt ||
-					n->getContent() == c_dirt_with_grass)
-			{
-				// Make it exactly mud
-				n->setContent(c_dirt);
-
-				/*
-					Don't flow it if the stuff under it is not mud
-				*/
-				{
-					u32 i2 = i;
-					vmanip.m_area.add_y(em, i2, -1);
-					// Cancel if out of area
-					if(vmanip.m_area.contains(i2) == false)
-						continue;
-					MapNode *n2 = &vmanip.m_data[i2];
-					if(n2->getContent() != c_dirt &&
-							n2->getContent() != c_dirt_with_grass)
-						continue;
-				}
-			}
-
-			/*s16 recurse_count = 0;
-	mudflow_recurse:*/
-
-			v3s16 dirs4[4] = {
-				v3s16(0,0,1), // back
-				v3s16(1,0,0), // right
-				v3s16(0,0,-1), // front
-				v3s16(-1,0,0), // left
-			};
-
-			// Theck that upper is air or doesn't exist.
-			// Cancel dropping if upper keeps it in place
-			u32 i3 = i;
-			vmanip.m_area.add_y(em, i3, 1);
-			if(vmanip.m_area.contains(i3) == true
-					&& ndef->get(vmanip.m_data[i3]).walkable)
-			{
-				continue;
-			}
-
-			// Drop mud on side
-
-			for(u32 di=0; di<4; di++)
-			{
-				v3s16 dirp = dirs4[di];
-				u32 i2 = i;
-				// Move to side
-				vmanip.m_area.add_p(em, i2, dirp);
-				// Fail if out of area
-				if(vmanip.m_area.contains(i2) == false)
-					continue;
-				// Check that side is air
-				MapNode *n2 = &vmanip.m_data[i2];
-				if(ndef->get(*n2).walkable)
-					continue;
-				// Check that under side is air
-				vmanip.m_area.add_y(em, i2, -1);
-				if(vmanip.m_area.contains(i2) == false)
-					continue;
-				n2 = &vmanip.m_data[i2];
-				if(ndef->get(*n2).walkable)
-					continue;
-				/*// Check that under that is air (need a drop of 2)
-				vmanip.m_area.add_y(em, i2, -1);
-				if(vmanip.m_area.contains(i2) == false)
-					continue;
-				n2 = &vmanip.m_data[i2];
-				if(content_walkable(n2->d))
-					continue;*/
-				// Loop further down until not air
-				bool dropped_to_unknown = false;
-				do{
-					vmanip.m_area.add_y(em, i2, -1);
-					n2 = &vmanip.m_data[i2];
-					// if out of known area
-					if(vmanip.m_area.contains(i2) == false
-							|| n2->getContent() == CONTENT_IGNORE){
-						dropped_to_unknown = true;
-						break;
-					}
-				}while(ndef->get(*n2).walkable == false);
-				// Loop one up so that we're in air
-				vmanip.m_area.add_y(em, i2, 1);
-				n2 = &vmanip.m_data[i2];
-
-				bool old_is_water = (n->getContent() == c_water_source);
-				// Move mud to new place
-				if(!dropped_to_unknown) {
-					*n2 = *n;
-					// Set old place to be air (or water)
-					if(old_is_water)
-						*n = MapNode(c_water_source);
-					else
-						*n = MapNode(CONTENT_AIR);
-				}
-
-				// Done
-				break;
-			}
-		}
-		}
-	}
-
-	}
-
-	}//timer1
-#endif
-
-	} // Aging loop
-	/***********************
-		END OF AGING LOOP
-	************************/
-
-	/*
-		Add top and bottom side of water to transforming_liquid queue
-	*/
-
-	for(s16 x=full_node_min.X; x<=full_node_max.X; x++)
-	for(s16 z=full_node_min.Z; z<=full_node_max.Z; z++)
-	{
-		// Node position
-		v2s16 p2d(x,z);
-		{
-			bool water_found = false;
-			// Use fast index incrementing
-			v3s16 em = vmanip.m_area.getExtent();
-			u32 i = vmanip.m_area.index(v3s16(p2d.X, full_node_max.Y, p2d.Y));
-			for(s16 y=full_node_max.Y; y>=full_node_min.Y; y--)
-			{
-				if(y == full_node_max.Y){
-					water_found =
-						(vmanip.m_data[i].getContent() == c_water_source ||
-						vmanip.m_data[i].getContent() == c_lava_source);
-				}
-				else if(water_found == false)
-				{
-					if(vmanip.m_data[i].getContent() == c_water_source ||
-							vmanip.m_data[i].getContent() == c_lava_source)
-					{
-						v3s16 p = v3s16(p2d.X, y, p2d.Y);
-						data->transforming_liquid.push_back(p);
-						water_found = true;
-					}
-				}
-				else
-				{
-					// This can be done because water_found can only
-					// turn to true and end up here after going through
-					// a single block.
-					if(vmanip.m_data[i+1].getContent() != c_water_source ||
-							vmanip.m_data[i+1].getContent() != c_lava_source)
-					{
-						v3s16 p = v3s16(p2d.X, y+1, p2d.Y);
-						data->transforming_liquid.push_back(p);
-						water_found = false;
-					}
-				}
-
-				vmanip.m_area.add_y(em, i, -1);
-			}
-		}
-	}
-
-	/*
-		Grow grass
-	*/
-
-	for(s16 x=full_node_min.X; x<=full_node_max.X; x++)
-	for(s16 z=full_node_min.Z; z<=full_node_max.Z; z++)
-	{
-		// Node position in 2d
-		v2s16 p2d = v2s16(x,z);
-
-		/*
-			Find the lowest surface to which enough light ends up
-			to make grass grow.
-
-			Basically just wait until not air and not leaves.
-		*/
-		s16 surface_y = 0;
-		{
-			v3s16 em = vmanip.m_area.getExtent();
-			u32 i = vmanip.m_area.index(v3s16(p2d.X, node_max.Y, p2d.Y));
-			s16 y;
-			// Go to ground level
-			for(y=node_max.Y; y>=full_node_min.Y; y--)
-			{
-				MapNode &n = vmanip.m_data[i];
-				if(ndef->get(n).param_type != CPT_LIGHT
-						|| ndef->get(n).liquid_type != LIQUID_NONE)
-					break;
-				vmanip.m_area.add_y(em, i, -1);
-			}
-			if(y >= full_node_min.Y)
-				surface_y = y;
-			else
-				surface_y = full_node_min.Y;
-		}
-
-		u32 i = vmanip.m_area.index(p2d.X, surface_y, p2d.Y);
-		MapNode *n = &vmanip.m_data[i];
-		if(n->getContent() == c_dirt){
-			// Well yeah, this can't be overground...
-			if(surface_y < water_level - 20)
-				continue;
-			n->setContent(c_dirt_with_grass);
-		}
-	}
-
-	/*
-		Generate some trees
-	*/
-	assert(central_area_size.X == central_area_size.Z);
-	if (flags & MG_TREES) {
-		// Divide area into parts
-		s16 div = 8;
-		s16 sidelen = central_area_size.X / div;
-		double area = sidelen * sidelen;
-		for(s16 x0=0; x0<div; x0++)
-		for(s16 z0=0; z0<div; z0++)
-		{
-			// Center position of part of division
-			v2s16 p2d_center(
-				node_min.X + sidelen/2 + sidelen*x0,
-				node_min.Z + sidelen/2 + sidelen*z0
-			);
-			// Minimum edge of part of division
-			v2s16 p2d_min(
-				node_min.X + sidelen*x0,
-				node_min.Z + sidelen*z0
-			);
-			// Maximum edge of part of division
-			v2s16 p2d_max(
-				node_min.X + sidelen + sidelen*x0 - 1,
-				node_min.Z + sidelen + sidelen*z0 - 1
-			);
-			// Amount of trees
-			u32 tree_count = area * tree_amount_2d(data->seed, p2d_center);
-			// Put trees in random places on part of division
-			for(u32 i=0; i<tree_count; i++)
-			{
-				s16 x = myrand_range(p2d_min.X, p2d_max.X);
-				s16 z = myrand_range(p2d_min.Y, p2d_max.Y);
-				s16 y = find_ground_level(vmanip, v2s16(x,z), ndef);
-				// Don't make a tree under water level
-				if(y < water_level)
-					continue;
-				// Don't make a tree so high that it doesn't fit
-				if(y > node_max.Y - 6)
-					continue;
-				v3s16 p(x,y,z);
-				/*
-					Trees grow only on mud and grass
-				*/
-				{
-					u32 i = vmanip.m_area.index(v3s16(p));
-					MapNode *n = &vmanip.m_data[i];
-					if(n->getContent() != c_dirt
-							&& n->getContent() != c_dirt_with_grass)
-						continue;
-				}
-				p.Y++;
-				// Make a tree
-				make_tree(vmanip, p, false, ndef);
-			}
-		}
-	}
-
-
-	/*
-		Calculate lighting
-	*/
-	{
-	ScopeProfiler sp(g_profiler, "EmergeThread: mapgen lighting update",
-			SPT_AVG);
-	//VoxelArea a(node_min, node_max);
-	VoxelArea a(node_min-v3s16(1,0,1)*MAP_BLOCKSIZE,
-			node_max+v3s16(1,0,1)*MAP_BLOCKSIZE);
-	/*VoxelArea a(node_min-v3s16(1,0,1)*MAP_BLOCKSIZE/2,
-			node_max+v3s16(1,0,1)*MAP_BLOCKSIZE/2);*/
-	enum LightBank banks[2] = {LIGHTBANK_DAY, LIGHTBANK_NIGHT};
-	for(int i=0; i<2; i++)
-	{
-		enum LightBank bank = banks[i];
-
-		core::map<v3s16, bool> light_sources;
-		core::map<v3s16, u8> unlight_from;
-
-		voxalgo::clearLightAndCollectSources(vmanip, a, bank, ndef,
-				light_sources, unlight_from);
-
-		bool inexistent_top_provides_sunlight = !block_is_underground;
-		voxalgo::SunlightPropagateResult res = voxalgo::propagateSunlight(
-				vmanip, a, inexistent_top_provides_sunlight,
-				light_sources, ndef);
-		// TODO: Do stuff according to bottom_sunlight_valid
-
-		vmanip.unspreadLight(bank, unlight_from, light_sources, ndef);
-
-		vmanip.spreadLight(bank, light_sources, ndef);
-	}
-	}
-	this->generating = false;
 }
diff --git a/src/mapgen_v6.h b/src/mapgen_v6.h
index d2f05252bfccf5041687ff30877424815c807099..d37e406cbcb1d65933e31ae51d8aa4c07b8edd92 100644
--- a/src/mapgen_v6.h
+++ b/src/mapgen_v6.h
@@ -23,6 +23,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include "mapgen.h"
 
 #define AVERAGE_MUD_AMOUNT 4
+#define VMANIP_FLAG_CAVE VOXELFLAG_CHECKED1
 
 enum BiomeType
 {
@@ -34,11 +35,23 @@ extern NoiseParams nparams_v6_def_terrain_base;
 extern NoiseParams nparams_v6_def_terrain_higher;
 extern NoiseParams nparams_v6_def_steepness;
 extern NoiseParams nparams_v6_def_height_select;
-extern NoiseParams nparams_v6_def_trees;
 extern NoiseParams nparams_v6_def_mud;
 extern NoiseParams nparams_v6_def_beach;
 extern NoiseParams nparams_v6_def_biome;
 extern NoiseParams nparams_v6_def_cave;
+extern NoiseParams nparams_v6_def_humidity;
+extern NoiseParams nparams_v6_def_trees;
+extern NoiseParams nparams_v6_def_apple_trees;
+
+struct Cave {
+	s16 min_tunnel_diameter;
+	s16 max_tunnel_diameter;
+	int dswitchint;
+	u16 tunnel_routepoints;
+	int part_max_length_rs;
+	bool large_cave_is_flat;
+	bool flooded;
+};
 
 struct MapgenV6Params : public MapgenParams {
 	float freq_desert;
@@ -47,12 +60,14 @@ struct MapgenV6Params : public MapgenParams {
 	NoiseParams *np_terrain_higher;
 	NoiseParams *np_steepness;
 	NoiseParams *np_height_select;
-	NoiseParams *np_trees;
 	NoiseParams *np_mud;
 	NoiseParams *np_beach;
 	NoiseParams *np_biome;
 	NoiseParams *np_cave;
-
+	NoiseParams *np_humidity;
+	NoiseParams *np_trees;
+	NoiseParams *np_apple_trees;
+	
 	MapgenV6Params() {
 		freq_desert       = 0.45;
 		freq_beach        = 0.15;
@@ -60,11 +75,14 @@ struct MapgenV6Params : public MapgenParams {
 		np_terrain_higher = &nparams_v6_def_terrain_higher;
 		np_steepness      = &nparams_v6_def_steepness;
 		np_height_select  = &nparams_v6_def_height_select;
-		np_trees          = &nparams_v6_def_trees;
 		np_mud            = &nparams_v6_def_mud;
 		np_beach          = &nparams_v6_def_beach;
 		np_biome          = &nparams_v6_def_biome;
 		np_cave           = &nparams_v6_def_cave;
+		np_humidity       = &nparams_v6_def_humidity;
+		np_trees          = &nparams_v6_def_trees;
+		np_apple_trees    = &nparams_v6_def_apple_trees;
+
 	}
 	
 	bool readParams(Settings *settings);
@@ -73,64 +91,90 @@ struct MapgenV6Params : public MapgenParams {
 
 class MapgenV6 : public Mapgen {
 public:
-	//ManualMapVoxelManipulator &vmanip;
+	EmergeManager *emerge;
 
 	int ystride;
 	v3s16 csize;
+	u32 flags;
 
+	u32 blockseed;
 	v3s16 node_min;
 	v3s16 node_max;
+	v3s16 full_node_min;
+	v3s16 full_node_max;
+	v3s16 central_area_size;
+	int volume_nodes;
 
 	Noise *noise_terrain_base;
 	Noise *noise_terrain_higher;
 	Noise *noise_steepness;
 	Noise *noise_height_select;
-	Noise *noise_trees;
 	Noise *noise_mud;
 	Noise *noise_beach;
 	Noise *noise_biome;
-
-	float *map_terrain_base;
-	float *map_terrain_higher;
-	float *map_steepness;
-	float *map_height_select;
-	float *map_trees;
-	float *map_mud;
-	float *map_beach;
-	float *map_biome;
-
 	NoiseParams *np_cave;
-
-	u32 flags;
+	NoiseParams *np_humidity;
+	NoiseParams *np_trees;
+	NoiseParams *np_apple_trees;
 	float freq_desert;
 	float freq_beach;
-
-	MapgenV6(int mapgenid, MapgenV6Params *params);
+	
+	content_t c_stone;
+	content_t c_dirt;
+	content_t c_dirt_with_grass;
+	content_t c_sand;
+	content_t c_water_source;
+	content_t c_lava_source;
+	content_t c_gravel;
+	content_t c_cobble;
+	content_t c_desert_sand;
+	content_t c_desert_stone;
+
+	MapgenV6(int mapgenid, MapgenV6Params *params, EmergeManager *emerge);
 	~MapgenV6();
 	
 	void makeChunk(BlockMakeData *data);
 	int getGroundLevelAtPoint(v2s16 p);
 
-	double baseRockLevelFromNoise(v2s16 p);
-	static s16 find_ground_level(VoxelManipulator &vmanip,
-								 v2s16 p2d, INodeDefManager *ndef);
-	static s16 find_stone_level(VoxelManipulator &vmanip,
-								 v2s16 p2d, INodeDefManager *ndef);
-	void make_tree(ManualMapVoxelManipulator &vmanip, v3s16 p0,
-					 bool is_apple_tree, INodeDefManager *ndef);
-	double tree_amount_2d(u64 seed, v2s16 p);
+	float baseTerrainLevel(float terrain_base, float terrain_higher,
+						   float steepness, float height_select);
+	virtual float baseTerrainLevelFromNoise(v2s16 p);
+	virtual float baseTerrainLevelFromMap(v2s16 p);
+	virtual float baseTerrainLevelFromMap(int index);
+
+	s16 find_ground_level(v2s16 p2d);
+	s16 find_stone_level(v2s16 p2d);
 	bool block_is_underground(u64 seed, v3s16 blockpos);
-	double base_rock_level_2d(u64 seed, v2s16 p);
 	s16 find_ground_level_from_noise(u64 seed, v2s16 p2d, s16 precision);
-	double get_mud_add_amount(u64 seed, v2s16 p);
-	bool get_have_beach(u64 seed, v2s16 p2d);
-	BiomeType get_biome(u64 seed, v2s16 p2d);
+	
+	float getHumidity(v2s16 p);
+	float getTreeAmount(v2s16 p);
+	bool getHaveAppleTree(v2s16 p);
+	float getMudAmount(v2s16 p);
+	virtual float getMudAmount(int index);
+	bool getHaveBeach(v2s16 p);
+	bool getHaveBeach(int index);
+	BiomeType getBiome(v2s16 p);
+	BiomeType getBiome(int index, v2s16 p);
+	
 	u32 get_blockseed(u64 seed, v3s16 p);
+	
+	virtual void calculateNoise();
+	int generateGround();
+	void addMud();
+	void flowMud(s16 &mudflow_minpos, s16 &mudflow_maxpos);
+	void addDirtGravelBlobs();
+	void growGrass();
+	void placeTreesAndJungleGrass();
+	virtual void defineCave(Cave &cave, PseudoRandom ps,
+							v3s16 node_min, bool large_cave);
+	void generateCaves(int max_stone_y);
+	virtual void generateSomething() {}; //for next mapgen
 };
 
 struct MapgenFactoryV6 : public MapgenFactory {
 	Mapgen *createMapgen(int mgid, MapgenParams *params, EmergeManager *emerge) {
-		return new MapgenV6(mgid, (MapgenV6Params *)params);
+		return new MapgenV6(mgid, (MapgenV6Params *)params, emerge);
 	};
 	
 	MapgenParams *createMapgenParams() {
diff --git a/src/mapnode.cpp b/src/mapnode.cpp
index 0513e688c399debf17d235837e5c9eefa911845d..bba845fcce95678dffae5f033d96d554a911d681 100644
--- a/src/mapnode.cpp
+++ b/src/mapnode.cpp
@@ -107,7 +107,7 @@ u8 MapNode::getFaceDir(INodeDefManager *nodemgr) const
 {
 	const ContentFeatures &f = nodemgr->get(*this);
 	if(f.param_type_2 == CPT2_FACEDIR)
-		return getParam2() & 0x03;
+		return getParam2() & 0x1F;
 	return 0;
 }
 
@@ -140,29 +140,131 @@ static std::vector<aabb3f> transformNodeBox(const MapNode &n,
 	{
 		const std::vector<aabb3f> &fixed = nodebox.fixed;
 		int facedir = n.getFaceDir(nodemgr);
+		u8 axisdir = facedir>>2;
+		facedir&=0x03;
 		for(std::vector<aabb3f>::const_iterator
 				i = fixed.begin();
 				i != fixed.end(); i++)
 		{
 			aabb3f box = *i;
-			if(facedir == 1)
+			switch (axisdir)
 			{
-				box.MinEdge.rotateXZBy(-90);
-				box.MaxEdge.rotateXZBy(-90);
-				box.repair();
-			}
-			else if(facedir == 2)
-			{
-				box.MinEdge.rotateXZBy(180);
-				box.MaxEdge.rotateXZBy(180);
-				box.repair();
-			}
-			else if(facedir == 3)
-			{
-				box.MinEdge.rotateXZBy(90);
-				box.MaxEdge.rotateXZBy(90);
-				box.repair();
+			case 0:
+				if(facedir == 1)
+				{
+					box.MinEdge.rotateXZBy(-90);
+					box.MaxEdge.rotateXZBy(-90);
+				}
+				else if(facedir == 2)
+				{
+					box.MinEdge.rotateXZBy(180);
+					box.MaxEdge.rotateXZBy(180);
+				}
+				else if(facedir == 3)
+				{
+					box.MinEdge.rotateXZBy(90);
+					box.MaxEdge.rotateXZBy(90);
+				}
+				break;
+			case 1: // z+
+				box.MinEdge.rotateYZBy(90);
+				box.MaxEdge.rotateYZBy(90);
+				if(facedir == 1)
+				{
+					box.MinEdge.rotateXYBy(90);
+					box.MaxEdge.rotateXYBy(90);
+				}
+				else if(facedir == 2)
+				{
+					box.MinEdge.rotateXYBy(180);
+					box.MaxEdge.rotateXYBy(180);
+				}
+				else if(facedir == 3)
+				{
+					box.MinEdge.rotateXYBy(-90);
+					box.MaxEdge.rotateXYBy(-90);
+				}
+				break;
+			case 2: //z-
+				box.MinEdge.rotateYZBy(-90);
+				box.MaxEdge.rotateYZBy(-90);
+				if(facedir == 1)
+				{
+					box.MinEdge.rotateXYBy(-90);
+					box.MaxEdge.rotateXYBy(-90);
+				}
+				else if(facedir == 2)
+				{
+					box.MinEdge.rotateXYBy(180);
+					box.MaxEdge.rotateXYBy(180);
+				}
+				else if(facedir == 3)
+				{
+					box.MinEdge.rotateXYBy(90);
+					box.MaxEdge.rotateXYBy(90);
+				}
+				break;
+			case 3:  //x+
+				box.MinEdge.rotateXYBy(-90);
+				box.MaxEdge.rotateXYBy(-90);
+				if(facedir == 1)
+				{
+					box.MinEdge.rotateYZBy(90);
+					box.MaxEdge.rotateYZBy(90);
+				}
+				else if(facedir == 2)
+				{
+					box.MinEdge.rotateYZBy(180);
+					box.MaxEdge.rotateYZBy(180);
+				}
+				else if(facedir == 3)
+				{
+					box.MinEdge.rotateYZBy(-90);
+					box.MaxEdge.rotateYZBy(-90);
+				}
+				break;
+			case 4:  //x-
+				box.MinEdge.rotateXYBy(90);
+				box.MaxEdge.rotateXYBy(90);
+				if(facedir == 1)
+				{
+					box.MinEdge.rotateYZBy(-90);
+					box.MaxEdge.rotateYZBy(-90);
+				}
+				else if(facedir == 2)
+				{
+					box.MinEdge.rotateYZBy(180);
+					box.MaxEdge.rotateYZBy(180);
+				}
+				else if(facedir == 3)
+				{
+					box.MinEdge.rotateYZBy(90);
+					box.MaxEdge.rotateYZBy(90);
+				}
+				break;
+			case 5:
+				box.MinEdge.rotateXYBy(-180);
+				box.MaxEdge.rotateXYBy(-180);
+				if(facedir == 1)
+				{
+					box.MinEdge.rotateXZBy(90);
+					box.MaxEdge.rotateXZBy(90);
+				}
+				else if(facedir == 2)
+				{
+					box.MinEdge.rotateXZBy(180);
+					box.MaxEdge.rotateXZBy(180);
+				}
+				else if(facedir == 3)
+				{
+					box.MinEdge.rotateXZBy(-90);
+					box.MaxEdge.rotateXZBy(-90);
+				}
+				break;
+			default:
+				break;
 			}
+			box.repair();
 			boxes.push_back(box);
 		}
 	}
diff --git a/src/mapsector.cpp b/src/mapsector.cpp
index 108effa79a6e888013c46faf56c92679a7c17eda..ebb050ec364be99b060ef7af72d88a41351293ef 100644
--- a/src/mapsector.cpp
+++ b/src/mapsector.cpp
@@ -45,10 +45,10 @@ void MapSector::deleteBlocks()
 	m_block_cache = NULL;
 
 	// Delete all
-	core::map<s16, MapBlock*>::Iterator i = m_blocks.getIterator();
-	for(; i.atEnd() == false; i++)
+	for(std::map<s16, MapBlock*>::iterator i = m_blocks.begin();
+		i != m_blocks.end(); ++i)
 	{
-		delete i.getNode()->getValue();
+		delete i->second;
 	}
 
 	// Clear container
@@ -64,14 +64,14 @@ MapBlock * MapSector::getBlockBuffered(s16 y)
 	}
 	
 	// If block doesn't exist, return NULL
-	core::map<s16, MapBlock*>::Node *n = m_blocks.find(y);
-	if(n == NULL)
+	std::map<s16, MapBlock*>::iterator n = m_blocks.find(y);
+	if(n == m_blocks.end())
 	{
 		block = NULL;
 	}
 	// If block exists, return it
 	else{
-		block = n->getValue();
+		block = n->second;
 	}
 	
 	// Cache the last result
@@ -101,7 +101,7 @@ MapBlock * MapSector::createBlankBlock(s16 y)
 {
 	MapBlock *block = createBlankBlockNoInsert(y);
 	
-	m_blocks.insert(y, block);
+	m_blocks[y] = block;
 
 	return block;
 }
@@ -119,7 +119,7 @@ void MapSector::insertBlock(MapBlock *block)
 	assert(p2d == m_pos);
 	
 	// Insert into container
-	m_blocks.insert(block_y, block);
+	m_blocks[block_y] = block;
 }
 
 void MapSector::deleteBlock(MapBlock *block)
@@ -130,23 +130,18 @@ void MapSector::deleteBlock(MapBlock *block)
 	m_block_cache = NULL;
 	
 	// Remove from container
-	m_blocks.remove(block_y);
+	m_blocks.erase(block_y);
 
 	// Delete
 	delete block;
 }
 
-void MapSector::getBlocks(core::list<MapBlock*> &dest)
+void MapSector::getBlocks(std::list<MapBlock*> &dest)
 {
-	core::list<MapBlock*> ref_list;
-
-	core::map<s16, MapBlock*>::Iterator bi;
-
-	bi = m_blocks.getIterator();
-	for(; bi.atEnd() == false; bi++)
+	for(std::map<s16, MapBlock*>::iterator bi = m_blocks.begin();
+		bi != m_blocks.end(); ++bi)
 	{
-		MapBlock *b = bi.getNode()->getValue();
-		dest.push_back(b);
+		dest.push_back(bi->second);
 	}
 }
 
@@ -189,7 +184,7 @@ ServerMapSector* ServerMapSector::deSerialize(
 		std::istream &is,
 		Map *parent,
 		v2s16 p2d,
-		core::map<v2s16, MapSector*> & sectors,
+		std::map<v2s16, MapSector*> & sectors,
 		IGameDef *gamedef
 	)
 {
@@ -219,22 +214,22 @@ ServerMapSector* ServerMapSector::deSerialize(
 
 	ServerMapSector *sector = NULL;
 
-	core::map<v2s16, MapSector*>::Node *n = sectors.find(p2d);
+	std::map<v2s16, MapSector*>::iterator n = sectors.find(p2d);
 
-	if(n != NULL)
+	if(n != sectors.end())
 	{
 		dstream<<"WARNING: deSerializing existent sectors not supported "
 				"at the moment, because code hasn't been tested."
 				<<std::endl;
 
-		MapSector *sector = n->getValue();
+		MapSector *sector = n->second;
 		assert(sector->getId() == MAPSECTOR_SERVER);
 		return (ServerMapSector*)sector;
 	}
 	else
 	{
 		sector = new ServerMapSector(parent, p2d, gamedef);
-		sectors.insert(p2d, sector);
+		sectors[p2d] = sector;
 	}
 
 	/*
diff --git a/src/mapsector.h b/src/mapsector.h
index 88fc76b571974a1e6cce5d6d1ef7da2861da15ea..4f2b3f31feb35acf3a555f51ba3168ded618d52b 100644
--- a/src/mapsector.h
+++ b/src/mapsector.h
@@ -24,6 +24,8 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include "irrlichttypes_bloated.h"
 #include "exceptions.h"
 #include <ostream>
+#include <map>
+#include <list>
 
 class MapBlock;
 class Map;
@@ -60,7 +62,7 @@ class MapSector
 	
 	void deleteBlock(MapBlock *block);
 	
-	void getBlocks(core::list<MapBlock*> &dest);
+	void getBlocks(std::list<MapBlock*> &dest);
 	
 	// Always false at the moment, because sector contains no metadata.
 	bool differs_from_disk;
@@ -68,7 +70,7 @@ class MapSector
 protected:
 	
 	// The pile of MapBlocks
-	core::map<s16, MapBlock*> m_blocks;
+	std::map<s16, MapBlock*> m_blocks;
 
 	Map *m_parent;
 	// Position on parent (in MapBlock widths)
@@ -110,7 +112,7 @@ class ServerMapSector : public MapSector
 			std::istream &is,
 			Map *parent,
 			v2s16 p2d,
-			core::map<v2s16, MapSector*> & sectors,
+			std::map<v2s16, MapSector*> & sectors,
 			IGameDef *gamedef
 		);
 		
diff --git a/src/mods.cpp b/src/mods.cpp
index ac2d9b17d666218a6e0c97aaa7275245f01802e4..6a7ab79aade2870804b74119d46f5bb381d3b64c 100644
--- a/src/mods.cpp
+++ b/src/mods.cpp
@@ -23,6 +23,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include "log.h"
 #include "subgame.h"
 #include "settings.h"
+#include "strfnd.h"
 
 std::map<std::string, ModSpec> getModsInPath(std::string path)
 {
@@ -188,11 +189,58 @@ void ModConfiguration::addMods(std::vector<ModSpec> new_mods)
 	}
 }
 
+// If failed, returned modspec has name==""
+static ModSpec findCommonMod(const std::string &modname)
+{
+	// Try to find in {$user,$share}/games/common/$modname
+	std::vector<std::string> find_paths;
+	find_paths.push_back(porting::path_user + DIR_DELIM + "games" +
+			DIR_DELIM + "common" + DIR_DELIM + "mods" + DIR_DELIM + modname);
+	find_paths.push_back(porting::path_share + DIR_DELIM + "games" +
+			DIR_DELIM + "common" + DIR_DELIM + "mods" + DIR_DELIM + modname);
+	for(u32 i=0; i<find_paths.size(); i++){
+		const std::string &try_path = find_paths[i];
+		if(fs::PathExists(try_path))
+			return ModSpec(modname, try_path);
+	}
+	// Failed to find mod
+	return ModSpec();
+}
+
 ModConfiguration::ModConfiguration(std::string worldpath)
 {
+	SubgameSpec gamespec = findWorldSubgame(worldpath);
+
+	// Add common mods without dependency handling
+	std::vector<std::string> inexistent_common_mods;
+	Settings gameconf;
+	if(getGameConfig(gamespec.path, gameconf)){
+		if(gameconf.exists("common_mods")){
+			Strfnd f(gameconf.get("common_mods"));
+			while(!f.atend()){
+				std::string modname = trim(f.next(","));
+				if(modname.empty())
+					continue;
+				ModSpec spec = findCommonMod(modname);
+				if(spec.name.empty())
+					inexistent_common_mods.push_back(modname);
+				else
+					m_sorted_mods.push_back(spec);
+			}
+		}
+	}
+	if(!inexistent_common_mods.empty()){
+		std::string s = "Required common mods ";
+		for(u32 i=0; i<inexistent_common_mods.size(); i++){
+			if(i != 0) s += ", ";
+			s += std::string("\"") + inexistent_common_mods[i] + "\"";
+		}
+		s += " could not be found.";
+		throw ModError(s);
+	}
+
 	// Add all world mods and all game mods
 	addModsInPath(worldpath + DIR_DELIM + "worldmods");
-	SubgameSpec gamespec = findWorldSubgame(worldpath);
 	addModsInPath(gamespec.gamemods_path);
 
 	// check world.mt file for mods explicitely declared to be
@@ -217,6 +265,6 @@ ModConfiguration::ModConfiguration(std::string worldpath)
 	}
 
 	for(std::set<std::string>::const_iterator i = gamespec.addon_mods_paths.begin();
-		i != gamespec.addon_mods_paths.end(); ++i)
+			i != gamespec.addon_mods_paths.end(); ++i)
 		addModsInPathFiltered((*i),exclude_mod_names);
 }
diff --git a/src/mods.h b/src/mods.h
index 9761a910329a87a94329ef1d290c2587e7e83505..32bcfb471e57e4824b59c3a15640c9b9c2ff1fb8 100644
--- a/src/mods.h
+++ b/src/mods.h
@@ -28,6 +28,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include <string>
 #include <map>
 #include <exception>
+#include <list>
 
 class ModError : public std::exception
 {
@@ -68,7 +69,6 @@ struct ModSpec
 	{}
 };
 
-
 std::map<std::string,ModSpec> getModsInPath(std::string path);
 
 // expands modpack contents, but does not replace them.
@@ -140,6 +140,4 @@ class ModConfiguration
 
 };
 
-
 #endif
-
diff --git a/src/nodedef.cpp b/src/nodedef.cpp
index 9a1145a8e856a8700782c9288e3a474e3bc97978..d41df5c3b8a394f47c3ec10cf8d3a1018eb43aaa 100644
--- a/src/nodedef.cpp
+++ b/src/nodedef.cpp
@@ -107,26 +107,31 @@ void NodeBox::deSerialize(std::istream &is)
 	TileDef
 */
 
-void TileDef::serialize(std::ostream &os) const
+void TileDef::serialize(std::ostream &os, u16 protocol_version) const
 {
-	writeU8(os, 0); // version
+	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);
-	if(version != 0)
-		throw SerializationError("unsupported TileDef version");
 	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);
 }
 
 /*
@@ -235,10 +240,10 @@ void ContentFeatures::serialize(std::ostream &os, u16 protocol_version)
 	writeF1000(os, visual_scale);
 	writeU8(os, 6);
 	for(u32 i=0; i<6; i++)
-		tiledef[i].serialize(os);
+		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);
+		tiledef_special[i].serialize(os, protocol_version);
 	}
 	writeU8(os, alpha);
 	writeU8(os, post_effect_color.getAlpha());
@@ -270,9 +275,9 @@ void ContentFeatures::serialize(std::ostream &os, u16 protocol_version)
 	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
-	writeU8(os, rightclickable);
 }
 
 void ContentFeatures::deSerialize(std::istream &is)
@@ -331,12 +336,12 @@ void ContentFeatures::deSerialize(std::istream &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
-		rightclickable = readU8(is);
 	}catch(SerializationError &e) {};
 }
 
@@ -809,10 +814,10 @@ void ContentFeatures::serializeOld(std::ostream &os, u16 protocol_version)
 		writeF1000(os, visual_scale);
 		writeU8(os, 6);
 		for(u32 i=0; i<6; i++)
-			tiledef[i].serialize(os);
+			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);
+			tiledef_special[i].serialize(os, protocol_version);
 		}
 		writeU8(os, alpha);
 		writeU8(os, post_effect_color.getAlpha());
diff --git a/src/nodedef.h b/src/nodedef.h
index b3f972b9b3259296320c0a4aca308b8bba939be9..4f07565d1ad2073ded8fd09d44f3ec1e115a4ac9 100644
--- a/src/nodedef.h
+++ b/src/nodedef.h
@@ -119,7 +119,7 @@ struct TileDef
 		animation.length = 1.0;
 	}
 
-	void serialize(std::ostream &os) const;
+	void serialize(std::ostream &os, u16 protocol_version) const;
 	void deSerialize(std::istream &is);
 };
 
diff --git a/src/noise.h b/src/noise.h
index c2a85771ccdf9a3e2ad74da1417ff643ef468556..34b4d0b41bcad8cdea32e4ba38ffe86e0b381324 100644
--- a/src/noise.h
+++ b/src/noise.h
@@ -89,7 +89,7 @@ class Noise {
 	Noise(NoiseParams *np, int seed, int sx, int sy, int sz);
 	~Noise();
 
-	void init(NoiseParams *np, int seed, int sx, int sy, int sz);
+	virtual void init(NoiseParams *np, int seed, int sx, int sy, int sz);
 	void setSize(int sx, int sy);
 	void setSize(int sx, int sy, int sz);
 	void setSpreadFactor(v3f spread);
@@ -157,7 +157,7 @@ inline float easeCurve(float t) {
 		(s) + (np)->seed, (np)->octaves, (np)->persist))
 
 #define NoisePerlin3D(np, x, y, z, s) ((np)->offset + (np)->scale * \
-		noise2d_perlin((float)(x) / (np)->spread.X, (float)(y) / (np)->spread.Y, \
+		noise3d_perlin((float)(x) / (np)->spread.X, (float)(y) / (np)->spread.Y, \
 		(float)(z) / (np)->spread.Z, (s) + (np)->seed, (np)->octaves, (np)->persist))
 
 #endif
diff --git a/src/object_properties.h b/src/object_properties.h
index bde38bd66554dbb607900ca2c85db9a3ccbd383e..eeb397efa40d3112ee14777dad0b8a3febe8a414 100644
--- a/src/object_properties.h
+++ b/src/object_properties.h
@@ -24,6 +24,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include "irrlichttypes_bloated.h"
 #include <iostream>
 #include <map>
+#include <vector>
 
 struct ObjectProperties
 {
@@ -35,8 +36,8 @@ struct ObjectProperties
 	std::string visual;
 	std::string mesh;
 	v2f visual_size;
-	core::array<std::string> textures;
-	core::array<video::SColor> colors;
+	std::vector<std::string> textures;
+	std::vector<video::SColor> colors;
 	v2s16 spritediv;
 	v2s16 initial_sprite_basepos;
 	bool is_visible;
diff --git a/src/particles.cpp b/src/particles.cpp
index fef21d91b1f043fd08a080f6d85abe7c44a6a1b7..1d814f6191513676f76af074f93d82cb947c3d2e 100644
--- a/src/particles.cpp
+++ b/src/particles.cpp
@@ -32,19 +32,34 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include "clientmap.h"
 #include "mapnode.h"
 
+/*
+	Utility
+*/
+
+v3f random_v3f(v3f min, v3f max)
+{
+	return v3f( rand()/(float)RAND_MAX*(max.X-min.X)+min.X,
+			rand()/(float)RAND_MAX*(max.Y-min.Y)+min.Y,
+			rand()/(float)RAND_MAX*(max.Z-min.Z)+min.Z);
+}
+
+std::vector<Particle*> all_particles;
+std::map<u32, ParticleSpawner*> all_particlespawners;
+
 Particle::Particle(
 	IGameDef *gamedef,
 	scene::ISceneManager* smgr,
 	LocalPlayer *player,
-	s32 id,
+	ClientEnvironment &env,
 	v3f pos,
 	v3f velocity,
 	v3f acceleration,
 	float expirationtime,
 	float size,
+	bool collisiondetection,
 	AtlasPointer ap
 ):
-	scene::ISceneNode(smgr->getRootSceneNode(), smgr, id)
+	scene::ISceneNode(smgr->getRootSceneNode(), smgr)
 {
 	// Misc
 	m_gamedef = gamedef;
@@ -57,7 +72,6 @@ Particle::Particle(
 	m_material.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL;
 	m_material.setTexture(0, ap.atlas);
 	m_ap = ap;
-	m_light = 0;
 
 
 	// Particle related
@@ -68,10 +82,20 @@ Particle::Particle(
 	m_time = 0;
 	m_player = player;
 	m_size = size;
+	m_collisiondetection = collisiondetection;
 
-	// Irrlicht stuff (TODO)
-	m_collisionbox = core::aabbox3d<f32>(-size/2,-size/2,-size/2,size/2,size/2,size/2);
+	// Irrlicht stuff
+	m_collisionbox = core::aabbox3d<f32>
+			(-size/2,-size/2,-size/2,size/2,size/2,size/2);
 	this->setAutomaticCulling(scene::EAC_OFF);
+
+	// Init lighting
+	updateLight(env);
+
+	// Init model
+	updateVertices();
+
+	all_particles.push_back(this);
 }
 
 Particle::~Particle()
@@ -82,8 +106,10 @@ void Particle::OnRegisterSceneNode()
 {
 	if (IsVisible)
 	{
-		SceneManager->registerNodeForRendering(this, scene::ESNRP_TRANSPARENT);
-		SceneManager->registerNodeForRendering(this, scene::ESNRP_SOLID);
+		SceneManager->registerNodeForRendering
+				(this, scene::ESNRP_TRANSPARENT);
+		SceneManager->registerNodeForRendering
+				(this, scene::ESNRP_SOLID);
 	}
 
 	ISceneNode::OnRegisterSceneNode();
@@ -96,45 +122,45 @@ void Particle::render()
 	video::IVideoDriver* driver = SceneManager->getVideoDriver();
 	driver->setMaterial(m_material);
 	driver->setTransform(video::ETS_WORLD, AbsoluteTransformation);
-	video::SColor c(255, m_light, m_light, m_light);
-
-	video::S3DVertex vertices[4] =
-	{
-		video::S3DVertex(-m_size/2,-m_size/2,0, 0,0,0, c, m_ap.x0(), m_ap.y1()),
-		video::S3DVertex(m_size/2,-m_size/2,0, 0,0,0, c, m_ap.x1(), m_ap.y1()),
-		video::S3DVertex(m_size/2,m_size/2,0, 0,0,0, c, m_ap.x1(), m_ap.y0()),
-		video::S3DVertex(-m_size/2,m_size/2,0, 0,0,0, c ,m_ap.x0(), m_ap.y0()),
-	};
-
-	for(u16 i=0; i<4; i++)
-	{
-		vertices[i].Pos.rotateYZBy(m_player->getPitch());
-		vertices[i].Pos.rotateXZBy(m_player->getYaw());
-		m_box.addInternalPoint(vertices[i].Pos);
-		vertices[i].Pos += m_pos*BS;
-	}
 
 	u16 indices[] = {0,1,2, 2,3,0};
-	driver->drawVertexPrimitiveList(vertices, 4, indices, 2,
-			video::EVT_STANDARD, scene::EPT_TRIANGLES, video::EIT_16BIT);
+	driver->drawVertexPrimitiveList(m_vertices, 4,
+			indices, 2, video::EVT_STANDARD,
+			scene::EPT_TRIANGLES, video::EIT_16BIT);
 }
 
 void Particle::step(float dtime, ClientEnvironment &env)
 {
-	core::aabbox3d<f32> box = m_collisionbox;
-	v3f p_pos = m_pos*BS;
-	v3f p_velocity = m_velocity*BS;
-	v3f p_acceleration = m_acceleration*BS;
-	collisionMoveSimple(&env.getClientMap(), m_gamedef,
-		BS*0.5, box,
-		0, dtime,
-		p_pos, p_velocity, p_acceleration);
-	m_pos = p_pos/BS;
-	m_velocity = p_velocity/BS;
-	m_acceleration = p_acceleration/BS;
 	m_time += dtime;
+	if (m_collisiondetection)
+	{
+		core::aabbox3d<f32> box = m_collisionbox;
+		v3f p_pos = m_pos*BS;
+		v3f p_velocity = m_velocity*BS;
+		v3f p_acceleration = m_acceleration*BS;
+		collisionMoveSimple(&env, m_gamedef,
+			BS*0.5, box,
+			0, dtime,
+			p_pos, p_velocity, p_acceleration);
+		m_pos = p_pos/BS;
+		m_velocity = p_velocity/BS;
+		m_acceleration = p_acceleration/BS;
+	}
+	else
+	{
+		m_velocity += m_acceleration * dtime;
+		m_pos += m_velocity * dtime;
+	}
 
 	// Update lighting
+	updateLight(env);
+
+	// Update model
+	updateVertices();
+}
+
+void Particle::updateLight(ClientEnvironment &env)
+{
 	u8 light = 0;
 	try{
 		v3s16 p = v3s16(
@@ -151,11 +177,37 @@ void Particle::step(float dtime, ClientEnvironment &env)
 	m_light = decode_light(light);
 }
 
-std::vector<Particle*> all_particles;
+void Particle::updateVertices()
+{
+	video::SColor c(255, m_light, m_light, m_light);
+	m_vertices[0] = video::S3DVertex(-m_size/2,-m_size/2,0, 0,0,0,
+			c, m_ap.x0(), m_ap.y1());
+	m_vertices[1] = video::S3DVertex(m_size/2,-m_size/2,0, 0,0,0,
+			c, m_ap.x1(), m_ap.y1());
+	m_vertices[2] = video::S3DVertex(m_size/2,m_size/2,0, 0,0,0,
+			c, m_ap.x1(), m_ap.y0());
+	m_vertices[3] = video::S3DVertex(-m_size/2,m_size/2,0, 0,0,0,
+			c ,m_ap.x0(), m_ap.y0());
+
+	for(u16 i=0; i<4; i++)
+	{
+		m_vertices[i].Pos.rotateYZBy(m_player->getPitch());
+		m_vertices[i].Pos.rotateXZBy(m_player->getYaw());
+		m_box.addInternalPoint(m_vertices[i].Pos);
+		m_vertices[i].Pos += m_pos*BS;
+	}
+}
+
+
+/*
+	Helpers
+*/
+
 
 void allparticles_step (float dtime, ClientEnvironment &env)
 {
-	for(std::vector<Particle*>::iterator i = all_particles.begin(); i != all_particles.end();)
+	for(std::vector<Particle*>::iterator i = all_particles.begin();
+			i != all_particles.end();)
 	{
 		if ((*i)->get_expired())
 		{
@@ -171,22 +223,28 @@ void allparticles_step (float dtime, ClientEnvironment &env)
 	}
 }
 
-void addDiggingParticles(IGameDef* gamedef, scene::ISceneManager* smgr, LocalPlayer *player, v3s16 pos, const TileSpec tiles[])
+void addDiggingParticles(IGameDef* gamedef, scene::ISceneManager* smgr,
+		LocalPlayer *player, ClientEnvironment &env, v3s16 pos,
+		const TileSpec tiles[])
 {
 	for (u16 j = 0; j < 32; j++) // set the amount of particles here
 	{
-		addNodeParticle(gamedef, smgr, player, pos, tiles);
+		addNodeParticle(gamedef, smgr, player, env, pos, tiles);
 	}
 }
 
-void addPunchingParticles(IGameDef* gamedef, scene::ISceneManager* smgr, LocalPlayer *player, v3s16 pos, const TileSpec tiles[])
+void addPunchingParticles(IGameDef* gamedef, scene::ISceneManager* smgr,
+		LocalPlayer *player, ClientEnvironment &env,
+		v3s16 pos, const TileSpec tiles[])
 {
-	addNodeParticle(gamedef, smgr, player, pos, tiles);
+	addNodeParticle(gamedef, smgr, player, env, pos, tiles);
 }
 
 // add a particle of a node
 // used by digging and punching particles
-void addNodeParticle(IGameDef* gamedef, scene::ISceneManager* smgr, LocalPlayer *player, v3s16 pos, const TileSpec tiles[])
+void addNodeParticle(IGameDef* gamedef, scene::ISceneManager* smgr,
+		LocalPlayer *player, ClientEnvironment &env, v3s16 pos,
+		const TileSpec tiles[])
 {
 	// Texture
 	u8 texid = myrand_range(0,5);
@@ -205,7 +263,10 @@ void addNodeParticle(IGameDef* gamedef, scene::ISceneManager* smgr, LocalPlayer
 	ap.pos.Y = ap.y0() + (y1 - ap.y0()) * ((rand()%64)/64.-texsize);
 
 	// Physics
-	v3f velocity((rand()%100/50.-1)/1.5, rand()%100/35., (rand()%100/50.-1)/1.5);
+	v3f velocity(	(rand()%100/50.-1)/1.5,
+			rand()%100/35.,
+			(rand()%100/50.-1)/1.5);
+
 	v3f acceleration(0,-9,0);
 	v3f particlepos = v3f(
 		(f32)pos.X+rand()%100/200.-0.25,
@@ -213,17 +274,180 @@ void addNodeParticle(IGameDef* gamedef, scene::ISceneManager* smgr, LocalPlayer
 		(f32)pos.Z+rand()%100/200.-0.25
 	);
 
-	Particle *particle = new Particle(
+	new Particle(
 		gamedef,
 		smgr,
 		player,
-		0,
+		env,
 		particlepos,
 		velocity,
 		acceleration,
 		rand()%100/100., // expiration time
 		visual_size,
+		true,
 		ap);
+}
 
-	all_particles.push_back(particle);
+/*
+	ParticleSpawner
+*/
+
+ParticleSpawner::ParticleSpawner(IGameDef* gamedef, scene::ISceneManager *smgr, LocalPlayer *player,
+	u16 amount, float time,
+	v3f minpos, v3f maxpos, v3f minvel, v3f maxvel, v3f minacc, v3f maxacc,
+	float minexptime, float maxexptime, float minsize, float maxsize,
+	bool collisiondetection, AtlasPointer ap, u32 id)
+{
+	m_gamedef = gamedef;
+	m_smgr = smgr;
+	m_player = player;
+	m_amount = amount;
+	m_spawntime = time;
+	m_minpos = minpos;
+	m_maxpos = maxpos;
+	m_minvel = minvel;
+	m_maxvel = maxvel;
+	m_minacc = minacc;
+	m_maxacc = maxacc;
+	m_minexptime = minexptime;
+	m_maxexptime = maxexptime;
+	m_minsize = minsize;
+	m_maxsize = maxsize;
+	m_collisiondetection = collisiondetection;
+	m_ap = ap;
+	m_time = 0;
+
+	for (u16 i = 0; i<=m_amount; i++)
+	{
+		float spawntime = (float)rand()/(float)RAND_MAX*m_spawntime;
+		m_spawntimes.push_back(spawntime);
+	}
+
+	all_particlespawners.insert(std::pair<u32, ParticleSpawner*>(id, this));
+}
+
+ParticleSpawner::~ParticleSpawner() {}
+
+void ParticleSpawner::step(float dtime, ClientEnvironment &env)
+{
+	m_time += dtime;
+
+	if (m_spawntime != 0) // Spawner exists for a predefined timespan
+	{
+		for(std::vector<float>::iterator i = m_spawntimes.begin();
+				i != m_spawntimes.end();)
+		{
+			if ((*i) <= m_time && m_amount > 0)
+			{
+				m_amount--;
+
+				v3f pos = random_v3f(m_minpos, m_maxpos);
+				v3f vel = random_v3f(m_minvel, m_maxvel);
+				v3f acc = random_v3f(m_minacc, m_maxacc);
+				float exptime = rand()/(float)RAND_MAX
+						*(m_maxexptime-m_minexptime)
+						+m_minexptime;
+				float size = rand()/(float)RAND_MAX
+						*(m_maxsize-m_minsize)
+						+m_minsize;
+
+				new Particle(
+					m_gamedef,
+					m_smgr,
+					m_player,
+					env,
+					pos,
+					vel,
+					acc,
+					exptime,
+					size,
+					m_collisiondetection,
+					m_ap);
+				m_spawntimes.erase(i);
+			}
+			else
+			{
+				i++;
+			}
+		}
+	}
+	else // Spawner exists for an infinity timespan, spawn on a per-second base
+	{
+		for (int i = 0; i <= m_amount; i++)
+		{
+			if (rand()/(float)RAND_MAX < dtime)
+			{
+				v3f pos = random_v3f(m_minpos, m_maxpos);
+				v3f vel = random_v3f(m_minvel, m_maxvel);
+				v3f acc = random_v3f(m_minacc, m_maxacc);
+				float exptime = rand()/(float)RAND_MAX
+						*(m_maxexptime-m_minexptime)
+						+m_minexptime;
+				float size = rand()/(float)RAND_MAX
+						*(m_maxsize-m_minsize)
+						+m_minsize;
+
+				new Particle(
+					m_gamedef,
+					m_smgr,
+					m_player,
+					env,
+					pos,
+					vel,
+					acc,
+					exptime,
+					size,
+					m_collisiondetection,
+					m_ap);
+			}
+		}
+	}
+}
+
+void allparticlespawners_step (float dtime, ClientEnvironment &env)
+{
+	for(std::map<u32, ParticleSpawner*>::iterator i = 
+			all_particlespawners.begin();
+			i != all_particlespawners.end();)
+	{
+		if (i->second->get_expired())
+		{
+			delete i->second;
+			all_particlespawners.erase(i++);
+		}
+		else
+		{
+			i->second->step(dtime, env);
+			i++;
+		}
+	}
+}
+
+void delete_particlespawner (u32 id)
+{
+	if (all_particlespawners.find(id) != all_particlespawners.end())
+	{
+		delete all_particlespawners.find(id)->second;
+		all_particlespawners.erase(id);
+	}
+}
+
+void clear_particles ()
+{
+	for(std::map<u32, ParticleSpawner*>::iterator i =
+			all_particlespawners.begin();
+			i != all_particlespawners.end();)
+	{
+		delete i->second;
+		all_particlespawners.erase(i++);
+	}
+
+	for(std::vector<Particle*>::iterator i =
+			all_particles.begin();
+			i != all_particles.end();)
+	{
+		(*i)->remove();
+		delete *i;
+		all_particles.erase(i);
+	}	
 }
diff --git a/src/particles.h b/src/particles.h
index b317549e731044189a7fc3f2a2b557c55253cec2..308da551f997825c33febb3aa3ee856f1ba449bd 100644
--- a/src/particles.h
+++ b/src/particles.h
@@ -35,12 +35,13 @@ class Particle : public scene::ISceneNode
 		IGameDef* gamedef,
 		scene::ISceneManager* mgr,
 		LocalPlayer *player,
-		s32 id,
+		ClientEnvironment &env,
 		v3f pos,
 		v3f velocity,
 		v3f acceleration,
 		float expirationtime,
 		float size,
+		bool collisiondetection,
 		AtlasPointer texture
 	);
 	~Particle();
@@ -69,6 +70,10 @@ class Particle : public scene::ISceneNode
 	{ return m_expiration < m_time; }
 
 private:
+	void updateLight(ClientEnvironment &env);
+	void updateVertices();
+
+	video::S3DVertex m_vertices[4];
 	float m_time;
 	float m_expiration;
 
@@ -87,12 +92,71 @@ class Particle : public scene::ISceneNode
 	float m_size;
 	AtlasPointer m_ap;
 	u8 m_light;
+	bool m_collisiondetection;
+};
+
+class ParticleSpawner
+{
+	public:
+	ParticleSpawner(IGameDef* gamedef,
+		scene::ISceneManager *smgr,
+		LocalPlayer *player,
+		u16 amount,
+		float time,
+		v3f minp, v3f maxp,
+		v3f minvel, v3f maxvel,
+		v3f minacc, v3f maxacc,
+		float minexptime, float maxexptime,
+		float minsize, float maxsize,
+		bool collisiondetection,
+		AtlasPointer ap,
+		u32 id);
+
+	~ParticleSpawner();
+
+	void step(float dtime, ClientEnvironment &env);
+
+	bool get_expired ()
+	{ return (m_amount <= 0) && m_spawntime != 0; }
+
+	private:
+	float m_time;
+	IGameDef *m_gamedef;
+	scene::ISceneManager *m_smgr;
+	LocalPlayer *m_player;
+	u16 m_amount;
+	float m_spawntime;
+	v3f m_minpos;
+	v3f m_maxpos;
+	v3f m_minvel;
+	v3f m_maxvel;
+	v3f m_minacc;
+	v3f m_maxacc;
+	float m_minexptime;
+	float m_maxexptime;
+	float m_minsize;
+	float m_maxsize;
+	AtlasPointer m_ap;
+	std::vector<float> m_spawntimes;
+	bool m_collisiondetection;
 };
 
 void allparticles_step (float dtime, ClientEnvironment &env);
+void allparticlespawners_step (float dtime, ClientEnvironment &env);
+
+void delete_particlespawner (u32 id);
+void clear_particles ();
+
+void addDiggingParticles(IGameDef* gamedef, scene::ISceneManager* smgr,
+	LocalPlayer *player, ClientEnvironment &env, v3s16 pos,
+	const TileSpec tiles[]);
+
+void addPunchingParticles(IGameDef* gamedef, scene::ISceneManager* smgr,
+	LocalPlayer *player, ClientEnvironment &env, v3s16 pos,
+	const TileSpec tiles[]);
 
-void addDiggingParticles(IGameDef* gamedef, scene::ISceneManager* smgr, LocalPlayer *player, v3s16 pos, const TileSpec tiles[]);
-void addPunchingParticles(IGameDef* gamedef, scene::ISceneManager* smgr, LocalPlayer *player, v3s16 pos, const TileSpec tiles[]);
-void addNodeParticle(IGameDef* gamedef, scene::ISceneManager* smgr, LocalPlayer *player, v3s16 pos, const TileSpec tiles[]);
+void addNodeParticle(IGameDef* gamedef, scene::ISceneManager* smgr,
+	LocalPlayer *player, ClientEnvironment &env, v3s16 pos,
+	const TileSpec tiles[]);
 
 #endif
diff --git a/src/porting.h b/src/porting.h
index d7d1073406e69a558d1452aa29b1e42e5178e374..bcce96ef7f0bbb092ffedd546ac7567ba1da69c2 100644
--- a/src/porting.h
+++ b/src/porting.h
@@ -28,6 +28,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include "irrlichttypes.h" // u32
 #include "debug.h"
 #include "constants.h"
+#include "gettime.h"
 
 #ifdef _MSC_VER
 	#define SWPRINTF_CHARSTRING L"%S"
@@ -153,18 +154,65 @@ bool threadSetPriority(threadid_t tid, int prio);
 */
 #ifdef _WIN32 // Windows
 	#include <windows.h>
+	
+	inline u32 getTimeS()
+	{
+		return GetTickCount() / 1000;
+	}
+	
 	inline u32 getTimeMs()
 	{
 		return GetTickCount();
 	}
+	
+	inline u32 getTimeUs()
+	{
+		LARGE_INTEGER freq, t;
+		QueryPerformanceFrequency(&freq);
+		QueryPerformanceCounter(&t);
+		return (double)(t.QuadPart) / ((double)(freq.QuadPart) / 1000000.0);
+	}
+	
+	inline u32 getTimeNs()
+	{
+		LARGE_INTEGER freq, t;
+		QueryPerformanceFrequency(&freq);
+		QueryPerformanceCounter(&t);
+		return (double)(t.QuadPart) / ((double)(freq.QuadPart) / 1000000000.0);
+	}
+	
 #else // Posix
 	#include <sys/time.h>
+	#include <time.h>
+	
+	inline u32 getTimeS()
+	{
+		struct timeval tv;
+		gettimeofday(&tv, NULL);
+		return tv.tv_sec;
+	}
+	
 	inline u32 getTimeMs()
 	{
 		struct timeval tv;
 		gettimeofday(&tv, NULL);
 		return tv.tv_sec * 1000 + tv.tv_usec / 1000;
 	}
+	
+	inline u32 getTimeUs()
+	{
+		struct timeval tv;
+		gettimeofday(&tv, NULL);
+		return tv.tv_sec * 1000000 + tv.tv_usec;
+	}
+	
+	inline u32 getTimeNs()
+	{
+		struct timespec ts;
+		clock_gettime(CLOCK_REALTIME, &ts);
+		return ts.tv_sec * 1000000000 + ts.tv_nsec;
+	}
+	
 	/*#include <sys/timeb.h>
 	inline u32 getTimeMs()
 	{
@@ -174,6 +222,22 @@ bool threadSetPriority(threadid_t tid, int prio);
 	}*/
 #endif
 
+inline u32 getTime(TimePrecision prec)
+{
+	switch (prec) {
+		case PRECISION_SECONDS:
+			return getTimeS();
+		case PRECISION_MILLI:
+			return getTimeMs();
+		case PRECISION_MICRO:
+			return getTimeUs();
+		case PRECISION_NANO:
+			return getTimeNs();
+	}
+	return 0;
+}
+
+
 } // namespace porting
 
 #endif // PORTING_HEADER
diff --git a/src/profiler.h b/src/profiler.h
index b9fa2248558ed599dbc1dba6d1843ff366fb9689..271ad70c1f1947c0b03dfd575b7ce656a1cb592f 100644
--- a/src/profiler.h
+++ b/src/profiler.h
@@ -45,21 +45,21 @@ class Profiler
 		JMutexAutoLock lock(m_mutex);
 		{
 			/* No average shall have been used; mark add used as -2 */
-			core::map<std::string, int>::Node *n = m_avgcounts.find(name);
-			if(n == NULL)
+			std::map<std::string, int>::iterator n = m_avgcounts.find(name);
+			if(n == m_avgcounts.end())
 				m_avgcounts[name] = -2;
 			else{
-				if(n->getValue() == -1)
-					n->setValue(-2);
-				assert(n->getValue() == -2);
+				if(n->second == -1)
+					n->second = -2;
+				assert(n->second == -2);
 			}
 		}
 		{
-			core::map<std::string, float>::Node *n = m_data.find(name);
-			if(n == NULL)
+			std::map<std::string, float>::iterator n = m_data.find(name);
+			if(n == m_data.end())
 				m_data[name] = value;
 			else
-				n->setValue(n->getValue() + value);
+				n->second += value;
 		}
 	}
 
@@ -67,35 +67,32 @@ class Profiler
 	{
 		JMutexAutoLock lock(m_mutex);
 		{
-			core::map<std::string, int>::Node *n = m_avgcounts.find(name);
-			if(n == NULL)
+			std::map<std::string, int>::iterator n = m_avgcounts.find(name);
+			if(n == m_avgcounts.end())
 				m_avgcounts[name] = 1;
 			else{
 				/* No add shall have been used */
-				assert(n->getValue() != -2);
-				if(n->getValue() <= 0)
-					n->setValue(1);
-				else
-					n->setValue(n->getValue() + 1);
+				assert(n->second != -2);
+				n->second = (std::max)(n->second, 0) + 1;
 			}
 		}
 		{
-			core::map<std::string, float>::Node *n = m_data.find(name);
-			if(n == NULL)
+			std::map<std::string, float>::iterator n = m_data.find(name);
+			if(n == m_data.end())
 				m_data[name] = value;
 			else
-				n->setValue(n->getValue() + value);
+				n->second += value;
 		}
 	}
 
 	void clear()
 	{
 		JMutexAutoLock lock(m_mutex);
-		for(core::map<std::string, float>::Iterator
-				i = m_data.getIterator();
-				i.atEnd() == false; i++)
+		for(std::map<std::string, float>::iterator
+				i = m_data.begin();
+				i != m_data.end(); ++i)
 		{
-			i.getNode()->setValue(0);
+			i->second = 0;
 		}
 		m_avgcounts.clear();
 	}
@@ -112,9 +109,9 @@ class Profiler
 		u32 minindex, maxindex;
 		paging(m_data.size(), page, pagecount, minindex, maxindex);
 
-		for(core::map<std::string, float>::Iterator
-				i = m_data.getIterator();
-				i.atEnd() == false; i++)
+		for(std::map<std::string, float>::iterator
+				i = m_data.begin();
+				i != m_data.end(); ++i)
 		{
 			if(maxindex == 0)
 				break;
@@ -126,12 +123,12 @@ class Profiler
 				continue;
 			}
 
-			std::string name = i.getNode()->getKey();
+			std::string name = i->first;
 			int avgcount = 1;
-			core::map<std::string, int>::Node *n = m_avgcounts.find(name);
-			if(n){
-				if(n->getValue() >= 1)
-					avgcount = n->getValue();
+			std::map<std::string, int>::iterator n = m_avgcounts.find(name);
+			if(n != m_avgcounts.end()){
+				if(n->second >= 1)
+					avgcount = n->second;
 			}
 			o<<"  "<<name<<": ";
 			s32 clampsize = 40;
@@ -143,7 +140,7 @@ class Profiler
 				else
 					o<<" ";
 			}
-			o<<(i.getNode()->getValue() / avgcount);
+			o<<(i->second / avgcount);
 			o<<std::endl;
 		}
 	}
@@ -169,8 +166,8 @@ class Profiler
 
 private:
 	JMutex m_mutex;
-	core::map<std::string, float> m_data;
-	core::map<std::string, int> m_avgcounts;
+	std::map<std::string, float> m_data;
+	std::map<std::string, int> m_avgcounts;
 	std::map<std::string, float> m_graphvalues;
 };
 
diff --git a/src/scriptapi.cpp b/src/scriptapi.cpp
index 7aa148fd637883c8354ee762db50cfbc468fa25d..80abffa2b900fbfe5d7c909f40442b795aeddf0e 100644
--- a/src/scriptapi.cpp
+++ b/src/scriptapi.cpp
@@ -27,89 +27,29 @@ extern "C" {
 #include <lauxlib.h>
 }
 
-#include "log.h"
-#include "server.h"
-#include "porting.h"
-#include "filesys.h"
-#include "serverobject.h"
-#include "script.h"
-#include "object_properties.h"
-#include "content_sao.h" // For LuaEntitySAO and PlayerSAO
-#include "itemdef.h"
-#include "nodedef.h"
-#include "biome.h"
-#include "craftdef.h"
-#include "main.h" // For g_settings
 #include "settings.h" // For accessing g_settings
-#include "nodemetadata.h"
-#include "mapblock.h" // For getNodeBlockPos
-#include "content_nodemeta.h"
-#include "tool.h"
-#include "daynightratio.h"
-#include "noise.h" // PseudoRandom for LuaPseudoRandom
-#include "util/pointedthing.h"
+#include "main.h" // For g_settings
+#include "biome.h"
+#include "emerge.h"
+#include "script.h"
 #include "rollback.h"
-#include "treegen.h"
-
-static void stackDump(lua_State *L, std::ostream &o)
-{
-  int i;
-  int top = lua_gettop(L);
-  for (i = 1; i <= top; i++) {  /* repeat for each level */
-	int t = lua_type(L, i);
-	switch (t) {
-
-	  case LUA_TSTRING:  /* strings */
-	  	o<<"\""<<lua_tostring(L, i)<<"\"";
-		break;
-
-	  case LUA_TBOOLEAN:  /* booleans */
-		o<<(lua_toboolean(L, i) ? "true" : "false");
-		break;
-
-	  case LUA_TNUMBER:  /* numbers */ {
-	  	char buf[10];
-		snprintf(buf, 10, "%g", lua_tonumber(L, i));
-		o<<buf;
-		break; }
-
-	  default:  /* other values */
-		o<<lua_typename(L, t);
-		break;
 
-	}
-	o<<" ";
-  }
-  o<<std::endl;
-}
-
-static void realitycheck(lua_State *L)
-{
-	int top = lua_gettop(L);
-	if(top >= 30){
-		dstream<<"Stack is over 30:"<<std::endl;
-		stackDump(L, dstream);
-		script_error(L, "Stack is over 30 (reality check)");
-	}
-}
-
-class StackUnroller
-{
-private:
-	lua_State *m_lua;
-	int m_original_top;
-public:
-	StackUnroller(lua_State *L):
-		m_lua(L),
-		m_original_top(-1)
-	{
-		m_original_top = lua_gettop(m_lua); // store stack height
-	}
-	~StackUnroller()
-	{
-		lua_settop(m_lua, m_original_top); // restore stack height
-	}
-};
+#include "scriptapi_types.h"
+#include "scriptapi_env.h"
+#include "scriptapi_nodetimer.h"
+#include "scriptapi_inventory.h"
+#include "scriptapi_nodemeta.h"
+#include "scriptapi_object.h"
+#include "scriptapi_noise.h"
+#include "scriptapi_common.h"
+#include "scriptapi_item.h"
+#include "scriptapi_content.h"
+#include "scriptapi_craft.h"
+#include "scriptapi_particles.h"
+
+/*****************************************************************************/
+/* Mod related                                                               */
+/*****************************************************************************/
 
 class ModNameStorer
 {
@@ -131,6285 +71,483 @@ class ModNameStorer
 	}
 };
 
-/*
-	Getters for stuff in main tables
-*/
-
-static Server* get_server(lua_State *L)
-{
-	// Get server from registry
-	lua_getfield(L, LUA_REGISTRYINDEX, "minetest_server");
-	Server *server = (Server*)lua_touserdata(L, -1);
-	lua_pop(L, 1);
-	return server;
-}
-
-static ServerEnvironment* get_env(lua_State *L)
-{
-	// Get environment from registry
-	lua_getfield(L, LUA_REGISTRYINDEX, "minetest_env");
-	ServerEnvironment *env = (ServerEnvironment*)lua_touserdata(L, -1);
-	lua_pop(L, 1);
-	return env;
-}
-
-static void objectref_get(lua_State *L, u16 id)
+bool scriptapi_loadmod(lua_State *L, const std::string &scriptpath,
+		const std::string &modname)
 {
-	// Get minetest.object_refs[i]
-	lua_getglobal(L, "minetest");
-	lua_getfield(L, -1, "object_refs");
-	luaL_checktype(L, -1, LUA_TTABLE);
-	lua_pushnumber(L, id);
-	lua_gettable(L, -2);
-	lua_remove(L, -2); // object_refs
-	lua_remove(L, -2); // minetest
-}
+	ModNameStorer modnamestorer(L, modname);
 
-static void luaentity_get(lua_State *L, u16 id)
-{
-	// Get minetest.luaentities[i]
-	lua_getglobal(L, "minetest");
-	lua_getfield(L, -1, "luaentities");
-	luaL_checktype(L, -1, LUA_TTABLE);
-	lua_pushnumber(L, id);
-	lua_gettable(L, -2);
-	lua_remove(L, -2); // luaentities
-	lua_remove(L, -2); // minetest
-}
+	if(!string_allowed(modname, "abcdefghijklmnopqrstuvwxyz"
+			"0123456789_")){
+		errorstream<<"Error loading mod \""<<modname
+				<<"\": modname does not follow naming conventions: "
+				<<"Only chararacters [a-z0-9_] are allowed."<<std::endl;
+		return false;
+	}
 
-/*
-	Table field getters
-*/
+	bool success = false;
 
-static bool getstringfield(lua_State *L, int table,
-		const char *fieldname, std::string &result)
-{
-	lua_getfield(L, table, fieldname);
-	bool got = false;
-	if(lua_isstring(L, -1)){
-		size_t len = 0;
-		const char *ptr = lua_tolstring(L, -1, &len);
-		result.assign(ptr, len);
-		got = true;
+	try{
+		success = script_load(L, scriptpath.c_str());
 	}
-	lua_pop(L, 1);
-	return got;
-}
-
-static bool getintfield(lua_State *L, int table,
-		const char *fieldname, int &result)
-{
-	lua_getfield(L, table, fieldname);
-	bool got = false;
-	if(lua_isnumber(L, -1)){
-		result = lua_tonumber(L, -1);
-		got = true;
+	catch(LuaError &e){
+		errorstream<<"Error loading mod \""<<modname
+				<<"\": "<<e.what()<<std::endl;
 	}
-	lua_pop(L, 1);
-	return got;
-}
 
-static bool getfloatfield(lua_State *L, int table,
-		const char *fieldname, float &result)
-{
-	lua_getfield(L, table, fieldname);
-	bool got = false;
-	if(lua_isnumber(L, -1)){
-		result = lua_tonumber(L, -1);
-		got = true;
-	}
-	lua_pop(L, 1);
-	return got;
+	return success;
 }
 
-static bool getboolfield(lua_State *L, int table,
-		const char *fieldname, bool &result)
-{
-	lua_getfield(L, table, fieldname);
-	bool got = false;
-	if(lua_isboolean(L, -1)){
-		result = lua_toboolean(L, -1);
-		got = true;
-	}
-	lua_pop(L, 1);
-	return got;
-}
 
-static std::string checkstringfield(lua_State *L, int table,
-		const char *fieldname)
-{
-	lua_getfield(L, table, fieldname);
-	std::string s = luaL_checkstring(L, -1);
-	lua_pop(L, 1);
-	return s;
-}
+/*****************************************************************************/
+/* Auth                                                                      */
+/*****************************************************************************/
 
-static std::string getstringfield_default(lua_State *L, int table,
-		const char *fieldname, const std::string &default_)
+/*
+	Privileges
+*/
+static void read_privileges(lua_State *L, int index,
+		std::set<std::string> &result)
 {
-	std::string result = default_;
-	getstringfield(L, table, fieldname, result);
-	return result;
+	result.clear();
+	lua_pushnil(L);
+	if(index < 0)
+		index -= 1;
+	while(lua_next(L, index) != 0){
+		// key at index -2 and value at index -1
+		std::string key = luaL_checkstring(L, -2);
+		bool value = lua_toboolean(L, -1);
+		if(value)
+			result.insert(key);
+		// removes value, keeps key for next iteration
+		lua_pop(L, 1);
+	}
 }
 
-static int getintfield_default(lua_State *L, int table,
-		const char *fieldname, int default_)
+static void get_auth_handler(lua_State *L)
 {
-	int result = default_;
-	getintfield(L, table, fieldname, result);
-	return result;
+	lua_getglobal(L, "minetest");
+	lua_getfield(L, -1, "registered_auth_handler");
+	if(lua_isnil(L, -1)){
+		lua_pop(L, 1);
+		lua_getfield(L, -1, "builtin_auth_handler");
+	}
+	if(lua_type(L, -1) != LUA_TTABLE)
+		throw LuaError(L, "Authentication handler table not valid");
 }
 
-static float getfloatfield_default(lua_State *L, int table,
-		const char *fieldname, float default_)
+bool scriptapi_get_auth(lua_State *L, const std::string &playername,
+		std::string *dst_password, std::set<std::string> *dst_privs)
 {
-	float result = default_;
-	getfloatfield(L, table, fieldname, result);
-	return result;
-}
+	realitycheck(L);
+	assert(lua_checkstack(L, 20));
+	StackUnroller stack_unroller(L);
 
-static bool getboolfield_default(lua_State *L, int table,
-		const char *fieldname, bool default_)
-{
-	bool result = default_;
-	getboolfield(L, table, fieldname, result);
-	return result;
-}
+	get_auth_handler(L);
+	lua_getfield(L, -1, "get_auth");
+	if(lua_type(L, -1) != LUA_TFUNCTION)
+		throw LuaError(L, "Authentication handler missing get_auth");
+	lua_pushstring(L, playername.c_str());
+	if(lua_pcall(L, 1, 1, 0))
+		script_error(L, "error: %s", lua_tostring(L, -1));
 
-struct EnumString
-{
-	int num;
-	const char *str;
-};
+	// nil = login not allowed
+	if(lua_isnil(L, -1))
+		return false;
+	luaL_checktype(L, -1, LUA_TTABLE);
 
-static bool string_to_enum(const EnumString *spec, int &result,
-		const std::string &str)
-{
-	const EnumString *esp = spec;
-	while(esp->str){
-		if(str == std::string(esp->str)){
-			result = esp->num;
-			return true;
-		}
-		esp++;
-	}
-	return false;
-}
+	std::string password;
+	bool found = getstringfield(L, -1, "password", password);
+	if(!found)
+		throw LuaError(L, "Authentication handler didn't return password");
+	if(dst_password)
+		*dst_password = password;
 
-/*static bool enum_to_string(const EnumString *spec, std::string &result,
-		int num)
-{
-	const EnumString *esp = spec;
-	while(esp){
-		if(num == esp->num){
-			result = esp->str;
-			return true;
-		}
-		esp++;
-	}
-	return false;
-}*/
+	lua_getfield(L, -1, "privileges");
+	if(!lua_istable(L, -1))
+		throw LuaError(L,
+				"Authentication handler didn't return privilege table");
+	if(dst_privs)
+		read_privileges(L, -1, *dst_privs);
+	lua_pop(L, 1);
 
-static int getenumfield(lua_State *L, int table,
-		const char *fieldname, const EnumString *spec, int default_)
-{
-	int result = default_;
-	string_to_enum(spec, result,
-			getstringfield_default(L, table, fieldname, ""));
-	return result;
+	return true;
 }
 
-static void setintfield(lua_State *L, int table,
-		const char *fieldname, int value)
+void scriptapi_create_auth(lua_State *L, const std::string &playername,
+		const std::string &password)
 {
-	lua_pushinteger(L, value);
-	if(table < 0)
-		table -= 1;
-	lua_setfield(L, table, fieldname);
-}
+	realitycheck(L);
+	assert(lua_checkstack(L, 20));
+	StackUnroller stack_unroller(L);
 
-static void setfloatfield(lua_State *L, int table,
-		const char *fieldname, float value)
-{
-	lua_pushnumber(L, value);
-	if(table < 0)
-		table -= 1;
-	lua_setfield(L, table, fieldname);
+	get_auth_handler(L);
+	lua_getfield(L, -1, "create_auth");
+	if(lua_type(L, -1) != LUA_TFUNCTION)
+		throw LuaError(L, "Authentication handler missing create_auth");
+	lua_pushstring(L, playername.c_str());
+	lua_pushstring(L, password.c_str());
+	if(lua_pcall(L, 2, 0, 0))
+		script_error(L, "error: %s", lua_tostring(L, -1));
 }
 
-static void setboolfield(lua_State *L, int table,
-		const char *fieldname, bool value)
+bool scriptapi_set_password(lua_State *L, const std::string &playername,
+		const std::string &password)
 {
-	lua_pushboolean(L, value);
-	if(table < 0)
-		table -= 1;
-	lua_setfield(L, table, fieldname);
-}
+	realitycheck(L);
+	assert(lua_checkstack(L, 20));
+	StackUnroller stack_unroller(L);
 
-static void warn_if_field_exists(lua_State *L, int table,
-		const char *fieldname, const std::string &message)
-{
-	lua_getfield(L, table, fieldname);
-	if(!lua_isnil(L, -1)){
-		infostream<<script_get_backtrace(L)<<std::endl;
-		infostream<<"WARNING: field \""<<fieldname<<"\": "
-				<<message<<std::endl;
-	}
-	lua_pop(L, 1);
+	get_auth_handler(L);
+	lua_getfield(L, -1, "set_password");
+	if(lua_type(L, -1) != LUA_TFUNCTION)
+		throw LuaError(L, "Authentication handler missing set_password");
+	lua_pushstring(L, playername.c_str());
+	lua_pushstring(L, password.c_str());
+	if(lua_pcall(L, 2, 1, 0))
+		script_error(L, "error: %s", lua_tostring(L, -1));
+	return lua_toboolean(L, -1);
 }
 
+/*****************************************************************************/
+/* Misc                                                                      */
+/*****************************************************************************/
 /*
-	EnumString definitions
+	Groups
 */
-
-struct EnumString es_ItemType[] =
+void read_groups(lua_State *L, int index,
+		std::map<std::string, int> &result)
 {
-	{ITEM_NONE, "none"},
-	{ITEM_NODE, "node"},
-	{ITEM_CRAFT, "craft"},
-	{ITEM_TOOL, "tool"},
-	{0, NULL},
-};
+	if (!lua_istable(L,index))
+		return;
+	result.clear();
+	lua_pushnil(L);
+	if(index < 0)
+		index -= 1;
+	while(lua_next(L, index) != 0){
+		// key at index -2 and value at index -1
+		std::string name = luaL_checkstring(L, -2);
+		int rating = luaL_checkinteger(L, -1);
+		result[name] = rating;
+		// removes value, keeps key for next iteration
+		lua_pop(L, 1);
+	}
+}
 
-struct EnumString es_DrawType[] =
+struct EnumString es_BiomeTerrainType[] =
 {
-	{NDT_NORMAL, "normal"},
-	{NDT_AIRLIKE, "airlike"},
-	{NDT_LIQUID, "liquid"},
-	{NDT_FLOWINGLIQUID, "flowingliquid"},
-	{NDT_GLASSLIKE, "glasslike"},
-	{NDT_ALLFACES, "allfaces"},
-	{NDT_ALLFACES_OPTIONAL, "allfaces_optional"},
-	{NDT_TORCHLIKE, "torchlike"},
-	{NDT_SIGNLIKE, "signlike"},
-	{NDT_PLANTLIKE, "plantlike"},
-	{NDT_FENCELIKE, "fencelike"},
-	{NDT_RAILLIKE, "raillike"},
-	{NDT_NODEBOX, "nodebox"},
+	{BIOME_TERRAIN_NORMAL, "normal"},
+	{BIOME_TERRAIN_LIQUID, "liquid"},
+	{BIOME_TERRAIN_NETHER, "nether"},
+	{BIOME_TERRAIN_AETHER, "aether"},
+	{BIOME_TERRAIN_FLAT,   "flat"},
 	{0, NULL},
 };
 
-struct EnumString es_ContentParamType[] =
+struct EnumString es_OreType[] =
 {
-	{CPT_NONE, "none"},
-	{CPT_LIGHT, "light"},
+	{ORE_SCATTER,  "scatter"},
+	{ORE_SHEET,    "sheet"},
+	{ORE_CLAYLIKE, "claylike"},
 	{0, NULL},
 };
 
-struct EnumString es_ContentParamType2[] =
-{
-	{CPT2_NONE, "none"},
-	{CPT2_FULL, "full"},
-	{CPT2_FLOWINGLIQUID, "flowingliquid"},
-	{CPT2_FACEDIR, "facedir"},
-	{CPT2_WALLMOUNTED, "wallmounted"},
-	{0, NULL},
-};
+/*****************************************************************************/
+/* Parameters                                                                */
+/*****************************************************************************/
+/*
+	DigParams
+*/
 
-struct EnumString es_LiquidType[] =
+static void set_dig_params(lua_State *L, int table,
+		const DigParams &params)
 {
-	{LIQUID_NONE, "none"},
-	{LIQUID_FLOWING, "flowing"},
-	{LIQUID_SOURCE, "source"},
-	{0, NULL},
-};
+	setboolfield(L, table, "diggable", params.diggable);
+	setfloatfield(L, table, "time", params.time);
+	setintfield(L, table, "wear", params.wear);
+}
 
-struct EnumString es_NodeBoxType[] =
+static void push_dig_params(lua_State *L,
+		const DigParams &params)
 {
-	{NODEBOX_REGULAR, "regular"},
-	{NODEBOX_FIXED, "fixed"},
-	{NODEBOX_WALLMOUNTED, "wallmounted"},
-	{0, NULL},
-};
+	lua_newtable(L);
+	set_dig_params(L, -1, params);
+}
 
-struct EnumString es_CraftMethod[] =
-{
-	{CRAFT_METHOD_NORMAL, "normal"},
-	{CRAFT_METHOD_COOKING, "cooking"},
-	{CRAFT_METHOD_FUEL, "fuel"},
-	{0, NULL},
-};
+/*
+	HitParams
+*/
 
-struct EnumString es_TileAnimationType[] =
+static void set_hit_params(lua_State *L, int table,
+		const HitParams &params)
 {
-	{TAT_NONE, "none"},
-	{TAT_VERTICAL_FRAMES, "vertical_frames"},
-	{0, NULL},
-};
+	setintfield(L, table, "hp", params.hp);
+	setintfield(L, table, "wear", params.wear);
+}
 
-struct EnumString es_BiomeTerrainType[] =
+static void push_hit_params(lua_State *L,
+		const HitParams &params)
 {
-	{BIOME_TERRAIN_NORMAL, "normal"},
-	{BIOME_TERRAIN_LIQUID, "liquid"},
-	{BIOME_TERRAIN_NETHER, "nether"},
-	{BIOME_TERRAIN_AETHER, "aether"},
-	{BIOME_TERRAIN_FLAT,   "flat"},
-	{0, NULL},
-};
+	lua_newtable(L);
+	set_hit_params(L, -1, params);
+}
 
 /*
-	C struct <-> Lua table converter functions
+	ServerSoundParams
 */
 
-static void push_v3f(lua_State *L, v3f p)
-{
-	lua_newtable(L);
-	lua_pushnumber(L, p.X);
-	lua_setfield(L, -2, "x");
-	lua_pushnumber(L, p.Y);
-	lua_setfield(L, -2, "y");
-	lua_pushnumber(L, p.Z);
-	lua_setfield(L, -2, "z");
-}
-
-static v2s16 read_v2s16(lua_State *L, int index)
-{
-	v2s16 p;
-	luaL_checktype(L, index, LUA_TTABLE);
-	lua_getfield(L, index, "x");
-	p.X = lua_tonumber(L, -1);
-	lua_pop(L, 1);
-	lua_getfield(L, index, "y");
-	p.Y = lua_tonumber(L, -1);
-	lua_pop(L, 1);
-	return p;
-}
-
-static v2f read_v2f(lua_State *L, int index)
-{
-	v2f p;
-	luaL_checktype(L, index, LUA_TTABLE);
-	lua_getfield(L, index, "x");
-	p.X = lua_tonumber(L, -1);
-	lua_pop(L, 1);
-	lua_getfield(L, index, "y");
-	p.Y = lua_tonumber(L, -1);
-	lua_pop(L, 1);
-	return p;
-}
-
-static v3f read_v3f(lua_State *L, int index)
-{
-	v3f pos;
-	luaL_checktype(L, index, LUA_TTABLE);
-	lua_getfield(L, index, "x");
-	pos.X = lua_tonumber(L, -1);
-	lua_pop(L, 1);
-	lua_getfield(L, index, "y");
-	pos.Y = lua_tonumber(L, -1);
-	lua_pop(L, 1);
-	lua_getfield(L, index, "z");
-	pos.Z = lua_tonumber(L, -1);
-	lua_pop(L, 1);
-	return pos;
-}
-
-static v3f check_v3f(lua_State *L, int index)
-{
-	v3f pos;
-	luaL_checktype(L, index, LUA_TTABLE);
-	lua_getfield(L, index, "x");
-	pos.X = luaL_checknumber(L, -1);
-	lua_pop(L, 1);
-	lua_getfield(L, index, "y");
-	pos.Y = luaL_checknumber(L, -1);
-	lua_pop(L, 1);
-	lua_getfield(L, index, "z");
-	pos.Z = luaL_checknumber(L, -1);
-	lua_pop(L, 1);
-	return pos;
-}
-
-static void pushFloatPos(lua_State *L, v3f p)
-{
-	p /= BS;
-	push_v3f(L, p);
-}
-
-static v3f checkFloatPos(lua_State *L, int index)
-{
-	return check_v3f(L, index) * BS;
-}
-
-static void push_v3s16(lua_State *L, v3s16 p)
-{
-	lua_newtable(L);
-	lua_pushnumber(L, p.X);
-	lua_setfield(L, -2, "x");
-	lua_pushnumber(L, p.Y);
-	lua_setfield(L, -2, "y");
-	lua_pushnumber(L, p.Z);
-	lua_setfield(L, -2, "z");
-}
-
-static v3s16 read_v3s16(lua_State *L, int index)
-{
-	// Correct rounding at <0
-	v3f pf = read_v3f(L, index);
-	return floatToInt(pf, 1.0);
-}
-
-static v3s16 check_v3s16(lua_State *L, int index)
-{
-	// Correct rounding at <0
-	v3f pf = check_v3f(L, index);
-	return floatToInt(pf, 1.0);
-}
-
-static void pushnode(lua_State *L, const MapNode &n, INodeDefManager *ndef)
-{
-	lua_newtable(L);
-	lua_pushstring(L, ndef->get(n).name.c_str());
-	lua_setfield(L, -2, "name");
-	lua_pushnumber(L, n.getParam1());
-	lua_setfield(L, -2, "param1");
-	lua_pushnumber(L, n.getParam2());
-	lua_setfield(L, -2, "param2");
-}
-
-static MapNode readnode(lua_State *L, int index, INodeDefManager *ndef)
-{
-	lua_getfield(L, index, "name");
-	const char *name = luaL_checkstring(L, -1);
-	lua_pop(L, 1);
-	u8 param1;
-	lua_getfield(L, index, "param1");
-	if(lua_isnil(L, -1))
-		param1 = 0;
-	else
-		param1 = lua_tonumber(L, -1);
-	lua_pop(L, 1);
-	u8 param2;
-	lua_getfield(L, index, "param2");
-	if(lua_isnil(L, -1))
-		param2 = 0;
-	else
-		param2 = lua_tonumber(L, -1);
-	lua_pop(L, 1);
-	return MapNode(ndef, name, param1, param2);
-}
-
-static video::SColor readARGB8(lua_State *L, int index)
-{
-	video::SColor color;
-	luaL_checktype(L, index, LUA_TTABLE);
-	lua_getfield(L, index, "a");
-	if(lua_isnumber(L, -1))
-		color.setAlpha(lua_tonumber(L, -1));
-	lua_pop(L, 1);
-	lua_getfield(L, index, "r");
-	color.setRed(lua_tonumber(L, -1));
-	lua_pop(L, 1);
-	lua_getfield(L, index, "g");
-	color.setGreen(lua_tonumber(L, -1));
-	lua_pop(L, 1);
-	lua_getfield(L, index, "b");
-	color.setBlue(lua_tonumber(L, -1));
-	lua_pop(L, 1);
-	return color;
-}
-
-static aabb3f read_aabb3f(lua_State *L, int index, f32 scale)
-{
-	aabb3f box;
-	if(lua_istable(L, index)){
-		lua_rawgeti(L, index, 1);
-		box.MinEdge.X = lua_tonumber(L, -1) * scale;
-		lua_pop(L, 1);
-		lua_rawgeti(L, index, 2);
-		box.MinEdge.Y = lua_tonumber(L, -1) * scale;
-		lua_pop(L, 1);
-		lua_rawgeti(L, index, 3);
-		box.MinEdge.Z = lua_tonumber(L, -1) * scale;
-		lua_pop(L, 1);
-		lua_rawgeti(L, index, 4);
-		box.MaxEdge.X = lua_tonumber(L, -1) * scale;
-		lua_pop(L, 1);
-		lua_rawgeti(L, index, 5);
-		box.MaxEdge.Y = lua_tonumber(L, -1) * scale;
-		lua_pop(L, 1);
-		lua_rawgeti(L, index, 6);
-		box.MaxEdge.Z = lua_tonumber(L, -1) * scale;
-		lua_pop(L, 1);
-	}
-	return box;
-}
-
-static std::vector<aabb3f> read_aabb3f_vector(lua_State *L, int index, f32 scale)
-{
-	std::vector<aabb3f> boxes;
-	if(lua_istable(L, index)){
-		int n = lua_objlen(L, index);
-		// Check if it's a single box or a list of boxes
-		bool possibly_single_box = (n == 6);
-		for(int i = 1; i <= n && possibly_single_box; i++){
-			lua_rawgeti(L, index, i);
-			if(!lua_isnumber(L, -1))
-				possibly_single_box = false;
-			lua_pop(L, 1);
-		}
-		if(possibly_single_box){
-			// Read a single box
-			boxes.push_back(read_aabb3f(L, index, scale));
-		} else {
-			// Read a list of boxes
-			for(int i = 1; i <= n; i++){
-				lua_rawgeti(L, index, i);
-				boxes.push_back(read_aabb3f(L, -1, scale));
-				lua_pop(L, 1);
-			}
-		}
-	}
-	return boxes;
-}
-
-static NodeBox read_nodebox(lua_State *L, int index)
-{
-	NodeBox nodebox;
-	if(lua_istable(L, -1)){
-		nodebox.type = (NodeBoxType)getenumfield(L, index, "type",
-				es_NodeBoxType, NODEBOX_REGULAR);
-
-		lua_getfield(L, index, "fixed");
-		if(lua_istable(L, -1))
-			nodebox.fixed = read_aabb3f_vector(L, -1, BS);
-		lua_pop(L, 1);
-
-		lua_getfield(L, index, "wall_top");
-		if(lua_istable(L, -1))
-			nodebox.wall_top = read_aabb3f(L, -1, BS);
-		lua_pop(L, 1);
-
-		lua_getfield(L, index, "wall_bottom");
-		if(lua_istable(L, -1))
-			nodebox.wall_bottom = read_aabb3f(L, -1, BS);
-		lua_pop(L, 1);
-
-		lua_getfield(L, index, "wall_side");
-		if(lua_istable(L, -1))
-			nodebox.wall_side = read_aabb3f(L, -1, BS);
-		lua_pop(L, 1);
-	}
-	return nodebox;
-}
-
-/*
-	NoiseParams
-*/
-static NoiseParams *read_noiseparams(lua_State *L, int index)
-{
-	if (index < 0)
-		index = lua_gettop(L) + 1 + index;
-		
-	if (!lua_istable(L, index))
-		return NULL;
-
-	NoiseParams *np = new NoiseParams;
-		
-	np->offset  = getfloatfield_default(L, index, "offset", 0.0);
-	np->scale   = getfloatfield_default(L, index, "scale", 0.0);
-	lua_getfield(L, index, "spread");
-	np->spread  = read_v3f(L, -1);
-	np->seed    = getintfield_default(L, index, "seed", 0);
-	np->octaves = getintfield_default(L, index, "octaves", 0);
-	np->persist = getfloatfield_default(L, index, "persist", 0.0);
-	
-	return np;
-}
-
-/*
-	Groups
-*/
-static void read_groups(lua_State *L, int index,
-		std::map<std::string, int> &result)
-{
-	if (!lua_istable(L,index))
-		return;
-	result.clear();
-	lua_pushnil(L);
-	if(index < 0)
-		index -= 1;
-	while(lua_next(L, index) != 0){
-		// key at index -2 and value at index -1
-		std::string name = luaL_checkstring(L, -2);
-		int rating = luaL_checkinteger(L, -1);
-		result[name] = rating;
-		// removes value, keeps key for next iteration
-		lua_pop(L, 1);
-	}
-}
-
-/*
-	Privileges
-*/
-static void read_privileges(lua_State *L, int index,
-		std::set<std::string> &result)
-{
-	result.clear();
-	lua_pushnil(L);
-	if(index < 0)
-		index -= 1;
-	while(lua_next(L, index) != 0){
-		// key at index -2 and value at index -1
-		std::string key = luaL_checkstring(L, -2);
-		bool value = lua_toboolean(L, -1);
-		if(value)
-			result.insert(key);
-		// removes value, keeps key for next iteration
-		lua_pop(L, 1);
-	}
-}
-
-/*
-	ToolCapabilities
-*/
-
-static ToolCapabilities read_tool_capabilities(
-		lua_State *L, int table)
-{
-	ToolCapabilities toolcap;
-	getfloatfield(L, table, "full_punch_interval", toolcap.full_punch_interval);
-	getintfield(L, table, "max_drop_level", toolcap.max_drop_level);
-	lua_getfield(L, table, "groupcaps");
-	if(lua_istable(L, -1)){
-		int table_groupcaps = lua_gettop(L);
-		lua_pushnil(L);
-		while(lua_next(L, table_groupcaps) != 0){
-			// key at index -2 and value at index -1
-			std::string groupname = luaL_checkstring(L, -2);
-			if(lua_istable(L, -1)){
-				int table_groupcap = lua_gettop(L);
-				// This will be created
-				ToolGroupCap groupcap;
-				// Read simple parameters
-				getintfield(L, table_groupcap, "maxlevel", groupcap.maxlevel);
-				getintfield(L, table_groupcap, "uses", groupcap.uses);
-				// DEPRECATED: maxwear
-				float maxwear = 0;
-				if(getfloatfield(L, table_groupcap, "maxwear", maxwear)){
-					if(maxwear != 0)
-						groupcap.uses = 1.0/maxwear;
-					else
-						groupcap.uses = 0;
-					infostream<<script_get_backtrace(L)<<std::endl;
-					infostream<<"WARNING: field \"maxwear\" is deprecated; "
-							<<"should replace with uses=1/maxwear"<<std::endl;
-				}
-				// Read "times" table
-				lua_getfield(L, table_groupcap, "times");
-				if(lua_istable(L, -1)){
-					int table_times = lua_gettop(L);
-					lua_pushnil(L);
-					while(lua_next(L, table_times) != 0){
-						// key at index -2 and value at index -1
-						int rating = luaL_checkinteger(L, -2);
-						float time = luaL_checknumber(L, -1);
-						groupcap.times[rating] = time;
-						// removes value, keeps key for next iteration
-						lua_pop(L, 1);
-					}
-				}
-				lua_pop(L, 1);
-				// Insert groupcap into toolcap
-				toolcap.groupcaps[groupname] = groupcap;
-			}
-			// removes value, keeps key for next iteration
-			lua_pop(L, 1);
-		}
-	}
-	lua_pop(L, 1);
-	return toolcap;
-}
-
-static void set_tool_capabilities(lua_State *L, int table,
-		const ToolCapabilities &toolcap)
-{
-	setfloatfield(L, table, "full_punch_interval", toolcap.full_punch_interval);
-	setintfield(L, table, "max_drop_level", toolcap.max_drop_level);
-	// Create groupcaps table
-	lua_newtable(L);
-	// For each groupcap
-	for(std::map<std::string, ToolGroupCap>::const_iterator
-			i = toolcap.groupcaps.begin(); i != toolcap.groupcaps.end(); i++){
-		// Create groupcap table
-		lua_newtable(L);
-		const std::string &name = i->first;
-		const ToolGroupCap &groupcap = i->second;
-		// Create subtable "times"
-		lua_newtable(L);
-		for(std::map<int, float>::const_iterator
-				i = groupcap.times.begin(); i != groupcap.times.end(); i++){
-			int rating = i->first;
-			float time = i->second;
-			lua_pushinteger(L, rating);
-			lua_pushnumber(L, time);
-			lua_settable(L, -3);
-		}
-		// Set subtable "times"
-		lua_setfield(L, -2, "times");
-		// Set simple parameters
-		setintfield(L, -1, "maxlevel", groupcap.maxlevel);
-		setintfield(L, -1, "uses", groupcap.uses);
-		// Insert groupcap table into groupcaps table
-		lua_setfield(L, -2, name.c_str());
-	}
-	// Set groupcaps table
-	lua_setfield(L, -2, "groupcaps");
-}
-
-static void push_tool_capabilities(lua_State *L,
-		const ToolCapabilities &prop)
-{
-	lua_newtable(L);
-	set_tool_capabilities(L, -1, prop);
-}
-
-/*
-	DigParams
-*/
-
-static void set_dig_params(lua_State *L, int table,
-		const DigParams &params)
-{
-	setboolfield(L, table, "diggable", params.diggable);
-	setfloatfield(L, table, "time", params.time);
-	setintfield(L, table, "wear", params.wear);
-}
-
-static void push_dig_params(lua_State *L,
-		const DigParams &params)
-{
-	lua_newtable(L);
-	set_dig_params(L, -1, params);
-}
-
-/*
-	HitParams
-*/
-
-static void set_hit_params(lua_State *L, int table,
-		const HitParams &params)
-{
-	setintfield(L, table, "hp", params.hp);
-	setintfield(L, table, "wear", params.wear);
-}
-
-static void push_hit_params(lua_State *L,
-		const HitParams &params)
-{
-	lua_newtable(L);
-	set_hit_params(L, -1, params);
-}
-
-/*
-	PointedThing
-*/
-
-static void push_pointed_thing(lua_State *L, const PointedThing& pointed)
-{
-	lua_newtable(L);
-	if(pointed.type == POINTEDTHING_NODE)
-	{
-		lua_pushstring(L, "node");
-		lua_setfield(L, -2, "type");
-		push_v3s16(L, pointed.node_undersurface);
-		lua_setfield(L, -2, "under");
-		push_v3s16(L, pointed.node_abovesurface);
-		lua_setfield(L, -2, "above");
-	}
-	else if(pointed.type == POINTEDTHING_OBJECT)
-	{
-		lua_pushstring(L, "object");
-		lua_setfield(L, -2, "type");
-		objectref_get(L, pointed.object_id);
-		lua_setfield(L, -2, "ref");
-	}
-	else
-	{
-		lua_pushstring(L, "nothing");
-		lua_setfield(L, -2, "type");
-	}
-}
-
-/*
-	SimpleSoundSpec
-*/
-
-static void read_soundspec(lua_State *L, int index, SimpleSoundSpec &spec)
-{
-	if(index < 0)
-		index = lua_gettop(L) + 1 + index;
-	if(lua_isnil(L, index)){
-	} else if(lua_istable(L, index)){
-		getstringfield(L, index, "name", spec.name);
-		getfloatfield(L, index, "gain", spec.gain);
-	} else if(lua_isstring(L, index)){
-		spec.name = lua_tostring(L, index);
-	}
-}
-
-/*
-	ObjectProperties
-*/
-
-static void read_object_properties(lua_State *L, int index,
-		ObjectProperties *prop)
-{
-	if(index < 0)
-		index = lua_gettop(L) + 1 + index;
-	if(!lua_istable(L, index))
-		return;
-
-	prop->hp_max = getintfield_default(L, -1, "hp_max", 10);
-
-	getboolfield(L, -1, "physical", prop->physical);
-
-	getfloatfield(L, -1, "weight", prop->weight);
-
-	lua_getfield(L, -1, "collisionbox");
-	if(lua_istable(L, -1))
-		prop->collisionbox = read_aabb3f(L, -1, 1.0);
-	lua_pop(L, 1);
-
-	getstringfield(L, -1, "visual", prop->visual);
-
-	getstringfield(L, -1, "mesh", prop->mesh);
-	
-	lua_getfield(L, -1, "visual_size");
-	if(lua_istable(L, -1))
-		prop->visual_size = read_v2f(L, -1);
-	lua_pop(L, 1);
-
-	lua_getfield(L, -1, "textures");
-	if(lua_istable(L, -1)){
-		prop->textures.clear();
-		int table = lua_gettop(L);
-		lua_pushnil(L);
-		while(lua_next(L, table) != 0){
-			// key at index -2 and value at index -1
-			if(lua_isstring(L, -1))
-				prop->textures.push_back(lua_tostring(L, -1));
-			else
-				prop->textures.push_back("");
-			// removes value, keeps key for next iteration
-			lua_pop(L, 1);
-		}
-	}
-	lua_pop(L, 1);
-
-	lua_getfield(L, -1, "colors");
-	if(lua_istable(L, -1)){
-		prop->colors.clear();
-		int table = lua_gettop(L);
-		lua_pushnil(L);
-		while(lua_next(L, table) != 0){
-			// key at index -2 and value at index -1
-			if(lua_isstring(L, -1))
-				prop->colors.push_back(readARGB8(L, -1));
-			else
-				prop->colors.push_back(video::SColor(255, 255, 255, 255));
-			// removes value, keeps key for next iteration
-			lua_pop(L, 1);
-		}
-	}
-	lua_pop(L, 1);
-	
-	lua_getfield(L, -1, "spritediv");
-	if(lua_istable(L, -1))
-		prop->spritediv = read_v2s16(L, -1);
-	lua_pop(L, 1);
-
-	lua_getfield(L, -1, "initial_sprite_basepos");
-	if(lua_istable(L, -1))
-		prop->initial_sprite_basepos = read_v2s16(L, -1);
-	lua_pop(L, 1);
-
-	getboolfield(L, -1, "is_visible", prop->is_visible);
-	getboolfield(L, -1, "makes_footstep_sound", prop->makes_footstep_sound);
-	getfloatfield(L, -1, "automatic_rotate", prop->automatic_rotate);
-}
-
-/*
-	ItemDefinition
-*/
-
-static ItemDefinition read_item_definition(lua_State *L, int index,
-		ItemDefinition default_def = ItemDefinition())
-{
-	if(index < 0)
-		index = lua_gettop(L) + 1 + index;
-
-	// Read the item definition
-	ItemDefinition def = default_def;
-
-	def.type = (ItemType)getenumfield(L, index, "type",
-			es_ItemType, ITEM_NONE);
-	getstringfield(L, index, "name", def.name);
-	getstringfield(L, index, "description", def.description);
-	getstringfield(L, index, "inventory_image", def.inventory_image);
-	getstringfield(L, index, "wield_image", def.wield_image);
-
-	lua_getfield(L, index, "wield_scale");
-	if(lua_istable(L, -1)){
-		def.wield_scale = check_v3f(L, -1);
-	}
-	lua_pop(L, 1);
-
-	def.stack_max = getintfield_default(L, index, "stack_max", def.stack_max);
-	if(def.stack_max == 0)
-		def.stack_max = 1;
-
-	lua_getfield(L, index, "on_use");
-	def.usable = lua_isfunction(L, -1);
-	lua_pop(L, 1);
-
-	getboolfield(L, index, "liquids_pointable", def.liquids_pointable);
-
-	warn_if_field_exists(L, index, "tool_digging_properties",
-			"deprecated: use tool_capabilities");
-
-	lua_getfield(L, index, "tool_capabilities");
-	if(lua_istable(L, -1)){
-		def.tool_capabilities = new ToolCapabilities(
-				read_tool_capabilities(L, -1));
-	}
-
-	// If name is "" (hand), ensure there are ToolCapabilities
-	// because it will be looked up there whenever any other item has
-	// no ToolCapabilities
-	if(def.name == "" && def.tool_capabilities == NULL){
-		def.tool_capabilities = new ToolCapabilities();
-	}
-
-	lua_getfield(L, index, "groups");
-	read_groups(L, -1, def.groups);
-	lua_pop(L, 1);
-
-	// Client shall immediately place this node when player places the item.
-	// Server will update the precise end result a moment later.
-	// "" = no prediction
-	getstringfield(L, index, "node_placement_prediction",
-			def.node_placement_prediction);
-
-	return def;
-}
-
-/*
-	TileDef
-*/
-
-static TileDef read_tiledef(lua_State *L, int index)
-{
-	if(index < 0)
-		index = lua_gettop(L) + 1 + index;
-
-	TileDef tiledef;
-
-	// key at index -2 and value at index
-	if(lua_isstring(L, index)){
-		// "default_lava.png"
-		tiledef.name = lua_tostring(L, index);
-	}
-	else if(lua_istable(L, index))
-	{
-		// {name="default_lava.png", animation={}}
-		tiledef.name = "";
-		getstringfield(L, index, "name", tiledef.name);
-		getstringfield(L, index, "image", tiledef.name); // MaterialSpec compat.
-		tiledef.backface_culling = getboolfield_default(
-					L, index, "backface_culling", true);
-		// animation = {}
-		lua_getfield(L, index, "animation");
-		if(lua_istable(L, -1)){
-			// {type="vertical_frames", aspect_w=16, aspect_h=16, length=2.0}
-			tiledef.animation.type = (TileAnimationType)
-					getenumfield(L, -1, "type", es_TileAnimationType,
-					TAT_NONE);
-			tiledef.animation.aspect_w =
-					getintfield_default(L, -1, "aspect_w", 16);
-			tiledef.animation.aspect_h =
-					getintfield_default(L, -1, "aspect_h", 16);
-			tiledef.animation.length =
-					getfloatfield_default(L, -1, "length", 1.0);
-		}
-		lua_pop(L, 1);
-	}
-
-	return tiledef;
-}
-
-/*
-	ContentFeatures
-*/
-
-static ContentFeatures read_content_features(lua_State *L, int index)
-{
-	if(index < 0)
-		index = lua_gettop(L) + 1 + index;
-
-	ContentFeatures f;
-
-	/* Cache existence of some callbacks */
-	lua_getfield(L, index, "on_construct");
-	if(!lua_isnil(L, -1)) f.has_on_construct = true;
-	lua_pop(L, 1);
-	lua_getfield(L, index, "on_destruct");
-	if(!lua_isnil(L, -1)) f.has_on_destruct = true;
-	lua_pop(L, 1);
-	lua_getfield(L, index, "after_destruct");
-	if(!lua_isnil(L, -1)) f.has_after_destruct = true;
-	lua_pop(L, 1);
-
-	lua_getfield(L, index, "on_rightclick");
-	f.rightclickable = lua_isfunction(L, -1);
-	lua_pop(L, 1);
-
-	/* Name */
-	getstringfield(L, index, "name", f.name);
-
-	/* Groups */
-	lua_getfield(L, index, "groups");
-	read_groups(L, -1, f.groups);
-	lua_pop(L, 1);
-
-	/* Visual definition */
-
-	f.drawtype = (NodeDrawType)getenumfield(L, index, "drawtype", es_DrawType,
-			NDT_NORMAL);
-	getfloatfield(L, index, "visual_scale", f.visual_scale);
-
-	// tiles = {}
-	lua_getfield(L, index, "tiles");
-	// If nil, try the deprecated name "tile_images" instead
-	if(lua_isnil(L, -1)){
-		lua_pop(L, 1);
-		warn_if_field_exists(L, index, "tile_images",
-				"Deprecated; new name is \"tiles\".");
-		lua_getfield(L, index, "tile_images");
-	}
-	if(lua_istable(L, -1)){
-		int table = lua_gettop(L);
-		lua_pushnil(L);
-		int i = 0;
-		while(lua_next(L, table) != 0){
-			// Read tiledef from value
-			f.tiledef[i] = read_tiledef(L, -1);
-			// removes value, keeps key for next iteration
-			lua_pop(L, 1);
-			i++;
-			if(i==6){
-				lua_pop(L, 1);
-				break;
-			}
-		}
-		// Copy last value to all remaining textures
-		if(i >= 1){
-			TileDef lasttile = f.tiledef[i-1];
-			while(i < 6){
-				f.tiledef[i] = lasttile;
-				i++;
-			}
-		}
-	}
-	lua_pop(L, 1);
-
-	// special_tiles = {}
-	lua_getfield(L, index, "special_tiles");
-	// If nil, try the deprecated name "special_materials" instead
-	if(lua_isnil(L, -1)){
-		lua_pop(L, 1);
-		warn_if_field_exists(L, index, "special_materials",
-				"Deprecated; new name is \"special_tiles\".");
-		lua_getfield(L, index, "special_materials");
-	}
-	if(lua_istable(L, -1)){
-		int table = lua_gettop(L);
-		lua_pushnil(L);
-		int i = 0;
-		while(lua_next(L, table) != 0){
-			// Read tiledef from value
-			f.tiledef_special[i] = read_tiledef(L, -1);
-			// removes value, keeps key for next iteration
-			lua_pop(L, 1);
-			i++;
-			if(i==6){
-				lua_pop(L, 1);
-				break;
-			}
-		}
-	}
-	lua_pop(L, 1);
-
-	f.alpha = getintfield_default(L, index, "alpha", 255);
-
-	/* Other stuff */
-
-	lua_getfield(L, index, "post_effect_color");
-	if(!lua_isnil(L, -1))
-		f.post_effect_color = readARGB8(L, -1);
-	lua_pop(L, 1);
-
-	f.param_type = (ContentParamType)getenumfield(L, index, "paramtype",
-			es_ContentParamType, CPT_NONE);
-	f.param_type_2 = (ContentParamType2)getenumfield(L, index, "paramtype2",
-			es_ContentParamType2, CPT2_NONE);
-
-	// Warn about some deprecated fields
-	warn_if_field_exists(L, index, "wall_mounted",
-			"deprecated: use paramtype2 = 'wallmounted'");
-	warn_if_field_exists(L, index, "light_propagates",
-			"deprecated: determined from paramtype");
-	warn_if_field_exists(L, index, "dug_item",
-			"deprecated: use 'drop' field");
-	warn_if_field_exists(L, index, "extra_dug_item",
-			"deprecated: use 'drop' field");
-	warn_if_field_exists(L, index, "extra_dug_item_rarity",
-			"deprecated: use 'drop' field");
-	warn_if_field_exists(L, index, "metadata_name",
-			"deprecated: use on_add and metadata callbacks");
-
-	// True for all ground-like things like stone and mud, false for eg. trees
-	getboolfield(L, index, "is_ground_content", f.is_ground_content);
-	f.light_propagates = (f.param_type == CPT_LIGHT);
-	getboolfield(L, index, "sunlight_propagates", f.sunlight_propagates);
-	// This is used for collision detection.
-	// Also for general solidness queries.
-	getboolfield(L, index, "walkable", f.walkable);
-	// Player can point to these
-	getboolfield(L, index, "pointable", f.pointable);
-	// Player can dig these
-	getboolfield(L, index, "diggable", f.diggable);
-	// Player can climb these
-	getboolfield(L, index, "climbable", f.climbable);
-	// Player can build on these
-	getboolfield(L, index, "buildable_to", f.buildable_to);
-	// Whether the node is non-liquid, source liquid or flowing liquid
-	f.liquid_type = (LiquidType)getenumfield(L, index, "liquidtype",
-			es_LiquidType, LIQUID_NONE);
-	// If the content is liquid, this is the flowing version of the liquid.
-	getstringfield(L, index, "liquid_alternative_flowing",
-			f.liquid_alternative_flowing);
-	// If the content is liquid, this is the source version of the liquid.
-	getstringfield(L, index, "liquid_alternative_source",
-			f.liquid_alternative_source);
-	// Viscosity for fluid flow, ranging from 1 to 7, with
-	// 1 giving almost instantaneous propagation and 7 being
-	// the slowest possible
-	f.liquid_viscosity = getintfield_default(L, index,
-			"liquid_viscosity", f.liquid_viscosity);
-	getboolfield(L, index, "liquid_renewable", f.liquid_renewable);
-	// Amount of light the node emits
-	f.light_source = getintfield_default(L, index,
-			"light_source", f.light_source);
-	f.damage_per_second = getintfield_default(L, index,
-			"damage_per_second", f.damage_per_second);
-
-	lua_getfield(L, index, "node_box");
-	if(lua_istable(L, -1))
-		f.node_box = read_nodebox(L, -1);
-	lua_pop(L, 1);
-
-	lua_getfield(L, index, "selection_box");
-	if(lua_istable(L, -1))
-		f.selection_box = read_nodebox(L, -1);
- 	lua_pop(L, 1);
-
-	// Set to true if paramtype used to be 'facedir_simple'
-	getboolfield(L, index, "legacy_facedir_simple", f.legacy_facedir_simple);
-	// Set to true if wall_mounted used to be set to true
-	getboolfield(L, index, "legacy_wallmounted", f.legacy_wallmounted);
-
-	// Sound table
-	lua_getfield(L, index, "sounds");
-	if(lua_istable(L, -1)){
-		lua_getfield(L, -1, "footstep");
-		read_soundspec(L, -1, f.sound_footstep);
-		lua_pop(L, 1);
-		lua_getfield(L, -1, "dig");
-		read_soundspec(L, -1, f.sound_dig);
-		lua_pop(L, 1);
-		lua_getfield(L, -1, "dug");
-		read_soundspec(L, -1, f.sound_dug);
-		lua_pop(L, 1);
-	}
-	lua_pop(L, 1);
-
-	return f;
-}
-
-/*
-	Inventory stuff
-*/
-
-static ItemStack read_item(lua_State *L, int index);
-static std::vector<ItemStack> read_items(lua_State *L, int index);
-// creates a table of ItemStacks
-static void push_items(lua_State *L, const std::vector<ItemStack> &items);
-
-static void inventory_set_list_from_lua(Inventory *inv, const char *name,
-		lua_State *L, int tableindex, int forcesize=-1)
-{
-	if(tableindex < 0)
-		tableindex = lua_gettop(L) + 1 + tableindex;
-	// If nil, delete list
-	if(lua_isnil(L, tableindex)){
-		inv->deleteList(name);
-		return;
-	}
-	// Otherwise set list
-	std::vector<ItemStack> items = read_items(L, tableindex);
-	int listsize = (forcesize != -1) ? forcesize : items.size();
-	InventoryList *invlist = inv->addList(name, listsize);
-	int index = 0;
-	for(std::vector<ItemStack>::const_iterator
-			i = items.begin(); i != items.end(); i++){
-		if(forcesize != -1 && index == forcesize)
-			break;
-		invlist->changeItem(index, *i);
-		index++;
-	}
-	while(forcesize != -1 && index < forcesize){
-		invlist->deleteItem(index);
-		index++;
-	}
-}
-
-static void inventory_get_list_to_lua(Inventory *inv, const char *name,
-		lua_State *L)
-{
-	InventoryList *invlist = inv->getList(name);
-	if(invlist == NULL){
-		lua_pushnil(L);
-		return;
-	}
-	std::vector<ItemStack> items;
-	for(u32 i=0; i<invlist->getSize(); i++)
-		items.push_back(invlist->getItem(i));
-	push_items(L, items);
-}
-
-/*
-	Helpful macros for userdata classes
-*/
-
-#define method(class, name) {#name, class::l_##name}
-
-/*
-	LuaItemStack
-*/
-
-class LuaItemStack
-{
-private:
-	ItemStack m_stack;
-
-	static const char className[];
-	static const luaL_reg methods[];
-
-	// Exported functions
-
-	// garbage collector
-	static int gc_object(lua_State *L)
-	{
-		LuaItemStack *o = *(LuaItemStack **)(lua_touserdata(L, 1));
-		delete o;
-		return 0;
-	}
-
-	// is_empty(self) -> true/false
-	static int l_is_empty(lua_State *L)
-	{
-		LuaItemStack *o = checkobject(L, 1);
-		ItemStack &item = o->m_stack;
-		lua_pushboolean(L, item.empty());
-		return 1;
-	}
-
-	// get_name(self) -> string
-	static int l_get_name(lua_State *L)
-	{
-		LuaItemStack *o = checkobject(L, 1);
-		ItemStack &item = o->m_stack;
-		lua_pushstring(L, item.name.c_str());
-		return 1;
-	}
-
-	// get_count(self) -> number
-	static int l_get_count(lua_State *L)
-	{
-		LuaItemStack *o = checkobject(L, 1);
-		ItemStack &item = o->m_stack;
-		lua_pushinteger(L, item.count);
-		return 1;
-	}
-
-	// get_wear(self) -> number
-	static int l_get_wear(lua_State *L)
-	{
-		LuaItemStack *o = checkobject(L, 1);
-		ItemStack &item = o->m_stack;
-		lua_pushinteger(L, item.wear);
-		return 1;
-	}
-
-	// get_metadata(self) -> string
-	static int l_get_metadata(lua_State *L)
-	{
-		LuaItemStack *o = checkobject(L, 1);
-		ItemStack &item = o->m_stack;
-		lua_pushlstring(L, item.metadata.c_str(), item.metadata.size());
-		return 1;
-	}
-
-	// clear(self) -> true
-	static int l_clear(lua_State *L)
-	{
-		LuaItemStack *o = checkobject(L, 1);
-		o->m_stack.clear();
-		lua_pushboolean(L, true);
-		return 1;
-	}
-
-	// replace(self, itemstack or itemstring or table or nil) -> true
-	static int l_replace(lua_State *L)
-	{
-		LuaItemStack *o = checkobject(L, 1);
-		o->m_stack = read_item(L, 2);
-		lua_pushboolean(L, true);
-		return 1;
-	}
-
-	// to_string(self) -> string
-	static int l_to_string(lua_State *L)
-	{
-		LuaItemStack *o = checkobject(L, 1);
-		std::string itemstring = o->m_stack.getItemString();
-		lua_pushstring(L, itemstring.c_str());
-		return 1;
-	}
-
-	// to_table(self) -> table or nil
-	static int l_to_table(lua_State *L)
-	{
-		LuaItemStack *o = checkobject(L, 1);
-		const ItemStack &item = o->m_stack;
-		if(item.empty())
-		{
-			lua_pushnil(L);
-		}
-		else
-		{
-			lua_newtable(L);
-			lua_pushstring(L, item.name.c_str());
-			lua_setfield(L, -2, "name");
-			lua_pushinteger(L, item.count);
-			lua_setfield(L, -2, "count");
-			lua_pushinteger(L, item.wear);
-			lua_setfield(L, -2, "wear");
-			lua_pushlstring(L, item.metadata.c_str(), item.metadata.size());
-			lua_setfield(L, -2, "metadata");
-		}
-		return 1;
-	}
-
-	// get_stack_max(self) -> number
-	static int l_get_stack_max(lua_State *L)
-	{
-		LuaItemStack *o = checkobject(L, 1);
-		ItemStack &item = o->m_stack;
-		lua_pushinteger(L, item.getStackMax(get_server(L)->idef()));
-		return 1;
-	}
-
-	// get_free_space(self) -> number
-	static int l_get_free_space(lua_State *L)
-	{
-		LuaItemStack *o = checkobject(L, 1);
-		ItemStack &item = o->m_stack;
-		lua_pushinteger(L, item.freeSpace(get_server(L)->idef()));
-		return 1;
-	}
-
-	// is_known(self) -> true/false
-	// Checks if the item is defined.
-	static int l_is_known(lua_State *L)
-	{
-		LuaItemStack *o = checkobject(L, 1);
-		ItemStack &item = o->m_stack;
-		bool is_known = item.isKnown(get_server(L)->idef());
-		lua_pushboolean(L, is_known);
-		return 1;
-	}
-
-	// get_definition(self) -> table
-	// Returns the item definition table from minetest.registered_items,
-	// or a fallback one (name="unknown")
-	static int l_get_definition(lua_State *L)
-	{
-		LuaItemStack *o = checkobject(L, 1);
-		ItemStack &item = o->m_stack;
-
-		// Get minetest.registered_items[name]
-		lua_getglobal(L, "minetest");
-		lua_getfield(L, -1, "registered_items");
-		luaL_checktype(L, -1, LUA_TTABLE);
-		lua_getfield(L, -1, item.name.c_str());
-		if(lua_isnil(L, -1))
-		{
-			lua_pop(L, 1);
-			lua_getfield(L, -1, "unknown");
-		}
-		return 1;
-	}
-
-	// get_tool_capabilities(self) -> table
-	// Returns the effective tool digging properties.
-	// Returns those of the hand ("") if this item has none associated.
-	static int l_get_tool_capabilities(lua_State *L)
-	{
-		LuaItemStack *o = checkobject(L, 1);
-		ItemStack &item = o->m_stack;
-		const ToolCapabilities &prop =
-			item.getToolCapabilities(get_server(L)->idef());
-		push_tool_capabilities(L, prop);
-		return 1;
-	}
-
-	// add_wear(self, amount) -> true/false
-	// The range for "amount" is [0,65535]. Wear is only added if the item
-	// is a tool. Adding wear might destroy the item.
-	// Returns true if the item is (or was) a tool.
-	static int l_add_wear(lua_State *L)
-	{
-		LuaItemStack *o = checkobject(L, 1);
-		ItemStack &item = o->m_stack;
-		int amount = lua_tointeger(L, 2);
-		bool result = item.addWear(amount, get_server(L)->idef());
-		lua_pushboolean(L, result);
-		return 1;
-	}
-
-	// add_item(self, itemstack or itemstring or table or nil) -> itemstack
-	// Returns leftover item stack
-	static int l_add_item(lua_State *L)
-	{
-		LuaItemStack *o = checkobject(L, 1);
-		ItemStack &item = o->m_stack;
-		ItemStack newitem = read_item(L, 2);
-		ItemStack leftover = item.addItem(newitem, get_server(L)->idef());
-		create(L, leftover);
-		return 1;
-	}
-
-	// item_fits(self, itemstack or itemstring or table or nil) -> true/false, itemstack
-	// First return value is true iff the new item fits fully into the stack
-	// Second return value is the would-be-left-over item stack
-	static int l_item_fits(lua_State *L)
-	{
-		LuaItemStack *o = checkobject(L, 1);
-		ItemStack &item = o->m_stack;
-		ItemStack newitem = read_item(L, 2);
-		ItemStack restitem;
-		bool fits = item.itemFits(newitem, &restitem, get_server(L)->idef());
-		lua_pushboolean(L, fits);  // first return value
-		create(L, restitem);       // second return value
-		return 2;
-	}
-
-	// take_item(self, takecount=1) -> itemstack
-	static int l_take_item(lua_State *L)
-	{
-		LuaItemStack *o = checkobject(L, 1);
-		ItemStack &item = o->m_stack;
-		u32 takecount = 1;
-		if(!lua_isnone(L, 2))
-			takecount = luaL_checkinteger(L, 2);
-		ItemStack taken = item.takeItem(takecount);
-		create(L, taken);
-		return 1;
-	}
-
-	// peek_item(self, peekcount=1) -> itemstack
-	static int l_peek_item(lua_State *L)
-	{
-		LuaItemStack *o = checkobject(L, 1);
-		ItemStack &item = o->m_stack;
-		u32 peekcount = 1;
-		if(!lua_isnone(L, 2))
-			peekcount = lua_tointeger(L, 2);
-		ItemStack peekaboo = item.peekItem(peekcount);
-		create(L, peekaboo);
-		return 1;
-	}
-
-public:
-	LuaItemStack(const ItemStack &item):
-		m_stack(item)
-	{
-	}
-
-	~LuaItemStack()
-	{
-	}
-
-	const ItemStack& getItem() const
-	{
-		return m_stack;
-	}
-	ItemStack& getItem()
-	{
-		return m_stack;
-	}
-
-	// LuaItemStack(itemstack or itemstring or table or nil)
-	// Creates an LuaItemStack and leaves it on top of stack
-	static int create_object(lua_State *L)
-	{
-		ItemStack item = read_item(L, 1);
-		LuaItemStack *o = new LuaItemStack(item);
-		*(void **)(lua_newuserdata(L, sizeof(void *))) = o;
-		luaL_getmetatable(L, className);
-		lua_setmetatable(L, -2);
-		return 1;
-	}
-	// Not callable from Lua
-	static int create(lua_State *L, const ItemStack &item)
-	{
-		LuaItemStack *o = new LuaItemStack(item);
-		*(void **)(lua_newuserdata(L, sizeof(void *))) = o;
-		luaL_getmetatable(L, className);
-		lua_setmetatable(L, -2);
-		return 1;
-	}
-
-	static LuaItemStack* checkobject(lua_State *L, int narg)
-	{
-		luaL_checktype(L, narg, LUA_TUSERDATA);
-		void *ud = luaL_checkudata(L, narg, className);
-		if(!ud) luaL_typerror(L, narg, className);
-		return *(LuaItemStack**)ud;  // unbox pointer
-	}
-
-	static void Register(lua_State *L)
-	{
-		lua_newtable(L);
-		int methodtable = lua_gettop(L);
-		luaL_newmetatable(L, className);
-		int metatable = lua_gettop(L);
-
-		lua_pushliteral(L, "__metatable");
-		lua_pushvalue(L, methodtable);
-		lua_settable(L, metatable);  // hide metatable from Lua getmetatable()
-
-		lua_pushliteral(L, "__index");
-		lua_pushvalue(L, methodtable);
-		lua_settable(L, metatable);
-
-		lua_pushliteral(L, "__gc");
-		lua_pushcfunction(L, gc_object);
-		lua_settable(L, metatable);
-
-		lua_pop(L, 1);  // drop metatable
-
-		luaL_openlib(L, 0, methods, 0);  // fill methodtable
-		lua_pop(L, 1);  // drop methodtable
-
-		// Can be created from Lua (LuaItemStack(itemstack or itemstring or table or nil))
-		lua_register(L, className, create_object);
-	}
-};
-const char LuaItemStack::className[] = "ItemStack";
-const luaL_reg LuaItemStack::methods[] = {
-	method(LuaItemStack, is_empty),
-	method(LuaItemStack, get_name),
-	method(LuaItemStack, get_count),
-	method(LuaItemStack, get_wear),
-	method(LuaItemStack, get_metadata),
-	method(LuaItemStack, clear),
-	method(LuaItemStack, replace),
-	method(LuaItemStack, to_string),
-	method(LuaItemStack, to_table),
-	method(LuaItemStack, get_stack_max),
-	method(LuaItemStack, get_free_space),
-	method(LuaItemStack, is_known),
-	method(LuaItemStack, get_definition),
-	method(LuaItemStack, get_tool_capabilities),
-	method(LuaItemStack, add_wear),
-	method(LuaItemStack, add_item),
-	method(LuaItemStack, item_fits),
-	method(LuaItemStack, take_item),
-	method(LuaItemStack, peek_item),
-	{0,0}
-};
-
-static ItemStack read_item(lua_State *L, int index)
-{
-	if(index < 0)
-		index = lua_gettop(L) + 1 + index;
-
-	if(lua_isnil(L, index))
-	{
-		return ItemStack();
-	}
-	else if(lua_isuserdata(L, index))
-	{
-		// Convert from LuaItemStack
-		LuaItemStack *o = LuaItemStack::checkobject(L, index);
-		return o->getItem();
-	}
-	else if(lua_isstring(L, index))
-	{
-		// Convert from itemstring
-		std::string itemstring = lua_tostring(L, index);
-		IItemDefManager *idef = get_server(L)->idef();
-		try
-		{
-			ItemStack item;
-			item.deSerialize(itemstring, idef);
-			return item;
-		}
-		catch(SerializationError &e)
-		{
-			infostream<<"WARNING: unable to create item from itemstring"
-					<<": "<<itemstring<<std::endl;
-			return ItemStack();
-		}
-	}
-	else if(lua_istable(L, index))
-	{
-		// Convert from table
-		IItemDefManager *idef = get_server(L)->idef();
-		std::string name = getstringfield_default(L, index, "name", "");
-		int count = getintfield_default(L, index, "count", 1);
-		int wear = getintfield_default(L, index, "wear", 0);
-		std::string metadata = getstringfield_default(L, index, "metadata", "");
-		return ItemStack(name, count, wear, metadata, idef);
-	}
-	else
-	{
-		throw LuaError(L, "Expecting itemstack, itemstring, table or nil");
-	}
-}
-
-static std::vector<ItemStack> read_items(lua_State *L, int index)
-{
-	if(index < 0)
-		index = lua_gettop(L) + 1 + index;
-
-	std::vector<ItemStack> items;
-	luaL_checktype(L, index, LUA_TTABLE);
-	lua_pushnil(L);
-	while(lua_next(L, index) != 0){
-		// key at index -2 and value at index -1
-		items.push_back(read_item(L, -1));
-		// removes value, keeps key for next iteration
-		lua_pop(L, 1);
-	}
-	return items;
-}
-
-// creates a table of ItemStacks
-static void push_items(lua_State *L, const std::vector<ItemStack> &items)
-{
-	// Get the table insert function
-	lua_getglobal(L, "table");
-	lua_getfield(L, -1, "insert");
-	int table_insert = lua_gettop(L);
-	// Create and fill table
-	lua_newtable(L);
-	int table = lua_gettop(L);
-	for(u32 i=0; i<items.size(); i++){
-		ItemStack item = items[i];
-		lua_pushvalue(L, table_insert);
-		lua_pushvalue(L, table);
-		LuaItemStack::create(L, item);
-		if(lua_pcall(L, 2, 0, 0))
-			script_error(L, "error: %s", lua_tostring(L, -1));
-	}
-	lua_remove(L, -2); // Remove table
-	lua_remove(L, -2); // Remove insert
-}
-
-/*
-	InvRef
-*/
-
-class InvRef
-{
-private:
-	InventoryLocation m_loc;
-
-	static const char className[];
-	static const luaL_reg methods[];
-
-	static InvRef *checkobject(lua_State *L, int narg)
-	{
-		luaL_checktype(L, narg, LUA_TUSERDATA);
-		void *ud = luaL_checkudata(L, narg, className);
-		if(!ud) luaL_typerror(L, narg, className);
-		return *(InvRef**)ud;  // unbox pointer
-	}
-
-	static Inventory* getinv(lua_State *L, InvRef *ref)
-	{
-		return get_server(L)->getInventory(ref->m_loc);
-	}
-
-	static InventoryList* getlist(lua_State *L, InvRef *ref,
-			const char *listname)
-	{
-		Inventory *inv = getinv(L, ref);
-		if(!inv)
-			return NULL;
-		return inv->getList(listname);
-	}
-
-	static void reportInventoryChange(lua_State *L, InvRef *ref)
-	{
-		// Inform other things that the inventory has changed
-		get_server(L)->setInventoryModified(ref->m_loc);
-	}
-
-	// Exported functions
-
-	// garbage collector
-	static int gc_object(lua_State *L) {
-		InvRef *o = *(InvRef **)(lua_touserdata(L, 1));
-		delete o;
-		return 0;
-	}
-
-	// is_empty(self, listname) -> true/false
-	static int l_is_empty(lua_State *L)
-	{
-		InvRef *ref = checkobject(L, 1);
-		const char *listname = luaL_checkstring(L, 2);
-		InventoryList *list = getlist(L, ref, listname);
-		if(list && list->getUsedSlots() > 0){
-			lua_pushboolean(L, false);
-		} else {
-			lua_pushboolean(L, true);
-		}
-		return 1;
-	}
-
-	// get_size(self, listname)
-	static int l_get_size(lua_State *L)
-	{
-		InvRef *ref = checkobject(L, 1);
-		const char *listname = luaL_checkstring(L, 2);
-		InventoryList *list = getlist(L, ref, listname);
-		if(list){
-			lua_pushinteger(L, list->getSize());
-		} else {
-			lua_pushinteger(L, 0);
-		}
-		return 1;
-	}
-
-	// get_width(self, listname)
-	static int l_get_width(lua_State *L)
-	{
-		InvRef *ref = checkobject(L, 1);
-		const char *listname = luaL_checkstring(L, 2);
-		InventoryList *list = getlist(L, ref, listname);
-		if(list){
-			lua_pushinteger(L, list->getWidth());
-		} else {
-			lua_pushinteger(L, 0);
-		}
-		return 1;
-	}
-
-	// set_size(self, listname, size)
-	static int l_set_size(lua_State *L)
-	{
-		InvRef *ref = checkobject(L, 1);
-		const char *listname = luaL_checkstring(L, 2);
-		int newsize = luaL_checknumber(L, 3);
-		Inventory *inv = getinv(L, ref);
-		if(newsize == 0){
-			inv->deleteList(listname);
-			reportInventoryChange(L, ref);
-			return 0;
-		}
-		InventoryList *list = inv->getList(listname);
-		if(list){
-			list->setSize(newsize);
-		} else {
-			list = inv->addList(listname, newsize);
-		}
-		reportInventoryChange(L, ref);
-		return 0;
-	}
-
-	// set_width(self, listname, size)
-	static int l_set_width(lua_State *L)
-	{
-		InvRef *ref = checkobject(L, 1);
-		const char *listname = luaL_checkstring(L, 2);
-		int newwidth = luaL_checknumber(L, 3);
-		Inventory *inv = getinv(L, ref);
-		InventoryList *list = inv->getList(listname);
-		if(list){
-			list->setWidth(newwidth);
-		} else {
-			return 0;
-		}
-		reportInventoryChange(L, ref);
-		return 0;
-	}
-
-	// get_stack(self, listname, i) -> itemstack
-	static int l_get_stack(lua_State *L)
-	{
-		InvRef *ref = checkobject(L, 1);
-		const char *listname = luaL_checkstring(L, 2);
-		int i = luaL_checknumber(L, 3) - 1;
-		InventoryList *list = getlist(L, ref, listname);
-		ItemStack item;
-		if(list != NULL && i >= 0 && i < (int) list->getSize())
-			item = list->getItem(i);
-		LuaItemStack::create(L, item);
-		return 1;
-	}
-
-	// set_stack(self, listname, i, stack) -> true/false
-	static int l_set_stack(lua_State *L)
-	{
-		InvRef *ref = checkobject(L, 1);
-		const char *listname = luaL_checkstring(L, 2);
-		int i = luaL_checknumber(L, 3) - 1;
-		ItemStack newitem = read_item(L, 4);
-		InventoryList *list = getlist(L, ref, listname);
-		if(list != NULL && i >= 0 && i < (int) list->getSize()){
-			list->changeItem(i, newitem);
-			reportInventoryChange(L, ref);
-			lua_pushboolean(L, true);
-		} else {
-			lua_pushboolean(L, false);
-		}
-		return 1;
-	}
-
-	// get_list(self, listname) -> list or nil
-	static int l_get_list(lua_State *L)
-	{
-		InvRef *ref = checkobject(L, 1);
-		const char *listname = luaL_checkstring(L, 2);
-		Inventory *inv = getinv(L, ref);
-		inventory_get_list_to_lua(inv, listname, L);
-		return 1;
-	}
-
-	// set_list(self, listname, list)
-	static int l_set_list(lua_State *L)
-	{
-		InvRef *ref = checkobject(L, 1);
-		const char *listname = luaL_checkstring(L, 2);
-		Inventory *inv = getinv(L, ref);
-		InventoryList *list = inv->getList(listname);
-		if(list)
-			inventory_set_list_from_lua(inv, listname, L, 3,
-					list->getSize());
-		else
-			inventory_set_list_from_lua(inv, listname, L, 3);
-		reportInventoryChange(L, ref);
-		return 0;
-	}
-
-	// add_item(self, listname, itemstack or itemstring or table or nil) -> itemstack
-	// Returns the leftover stack
-	static int l_add_item(lua_State *L)
-	{
-		InvRef *ref = checkobject(L, 1);
-		const char *listname = luaL_checkstring(L, 2);
-		ItemStack item = read_item(L, 3);
-		InventoryList *list = getlist(L, ref, listname);
-		if(list){
-			ItemStack leftover = list->addItem(item);
-			if(leftover.count != item.count)
-				reportInventoryChange(L, ref);
-			LuaItemStack::create(L, leftover);
-		} else {
-			LuaItemStack::create(L, item);
-		}
-		return 1;
-	}
-
-	// room_for_item(self, listname, itemstack or itemstring or table or nil) -> true/false
-	// Returns true if the item completely fits into the list
-	static int l_room_for_item(lua_State *L)
-	{
-		InvRef *ref = checkobject(L, 1);
-		const char *listname = luaL_checkstring(L, 2);
-		ItemStack item = read_item(L, 3);
-		InventoryList *list = getlist(L, ref, listname);
-		if(list){
-			lua_pushboolean(L, list->roomForItem(item));
-		} else {
-			lua_pushboolean(L, false);
-		}
-		return 1;
-	}
-
-	// contains_item(self, listname, itemstack or itemstring or table or nil) -> true/false
-	// Returns true if the list contains the given count of the given item name
-	static int l_contains_item(lua_State *L)
-	{
-		InvRef *ref = checkobject(L, 1);
-		const char *listname = luaL_checkstring(L, 2);
-		ItemStack item = read_item(L, 3);
-		InventoryList *list = getlist(L, ref, listname);
-		if(list){
-			lua_pushboolean(L, list->containsItem(item));
-		} else {
-			lua_pushboolean(L, false);
-		}
-		return 1;
-	}
-
-	// remove_item(self, listname, itemstack or itemstring or table or nil) -> itemstack
-	// Returns the items that were actually removed
-	static int l_remove_item(lua_State *L)
-	{
-		InvRef *ref = checkobject(L, 1);
-		const char *listname = luaL_checkstring(L, 2);
-		ItemStack item = read_item(L, 3);
-		InventoryList *list = getlist(L, ref, listname);
-		if(list){
-			ItemStack removed = list->removeItem(item);
-			if(!removed.empty())
-				reportInventoryChange(L, ref);
-			LuaItemStack::create(L, removed);
-		} else {
-			LuaItemStack::create(L, ItemStack());
-		}
-		return 1;
-	}
-
-	// get_location() -> location (like minetest.get_inventory(location))
-	static int l_get_location(lua_State *L)
-	{
-		InvRef *ref = checkobject(L, 1);
-		const InventoryLocation &loc = ref->m_loc;
-		switch(loc.type){
-		case InventoryLocation::PLAYER:
-			lua_newtable(L);
-			lua_pushstring(L, "player");
-			lua_setfield(L, -2, "type");
-			lua_pushstring(L, loc.name.c_str());
-			lua_setfield(L, -2, "name");
-			return 1;
-		case InventoryLocation::NODEMETA:
-			lua_newtable(L);
-			lua_pushstring(L, "nodemeta");
-			lua_setfield(L, -2, "type");
-			push_v3s16(L, loc.p);
-			lua_setfield(L, -2, "name");
-			return 1;
-		case InventoryLocation::DETACHED:
-			lua_newtable(L);
-			lua_pushstring(L, "detached");
-			lua_setfield(L, -2, "type");
-			lua_pushstring(L, loc.name.c_str());
-			lua_setfield(L, -2, "name");
-			return 1;
-		case InventoryLocation::UNDEFINED:
-		case InventoryLocation::CURRENT_PLAYER:
-			break;
-		}
-		lua_newtable(L);
-		lua_pushstring(L, "undefined");
-		lua_setfield(L, -2, "type");
-		return 1;
-	}
-
-public:
-	InvRef(const InventoryLocation &loc):
-		m_loc(loc)
-	{
-	}
-
-	~InvRef()
-	{
-	}
-
-	// Creates an InvRef and leaves it on top of stack
-	// Not callable from Lua; all references are created on the C side.
-	static void create(lua_State *L, const InventoryLocation &loc)
-	{
-		InvRef *o = new InvRef(loc);
-		*(void **)(lua_newuserdata(L, sizeof(void *))) = o;
-		luaL_getmetatable(L, className);
-		lua_setmetatable(L, -2);
-	}
-	static void createPlayer(lua_State *L, Player *player)
-	{
-		InventoryLocation loc;
-		loc.setPlayer(player->getName());
-		create(L, loc);
-	}
-	static void createNodeMeta(lua_State *L, v3s16 p)
-	{
-		InventoryLocation loc;
-		loc.setNodeMeta(p);
-		create(L, loc);
-	}
-
-	static void Register(lua_State *L)
-	{
-		lua_newtable(L);
-		int methodtable = lua_gettop(L);
-		luaL_newmetatable(L, className);
-		int metatable = lua_gettop(L);
-
-		lua_pushliteral(L, "__metatable");
-		lua_pushvalue(L, methodtable);
-		lua_settable(L, metatable);  // hide metatable from Lua getmetatable()
-
-		lua_pushliteral(L, "__index");
-		lua_pushvalue(L, methodtable);
-		lua_settable(L, metatable);
-
-		lua_pushliteral(L, "__gc");
-		lua_pushcfunction(L, gc_object);
-		lua_settable(L, metatable);
-
-		lua_pop(L, 1);  // drop metatable
-
-		luaL_openlib(L, 0, methods, 0);  // fill methodtable
-		lua_pop(L, 1);  // drop methodtable
-
-		// Cannot be created from Lua
-		//lua_register(L, className, create_object);
-	}
-};
-const char InvRef::className[] = "InvRef";
-const luaL_reg InvRef::methods[] = {
-	method(InvRef, is_empty),
-	method(InvRef, get_size),
-	method(InvRef, set_size),
-	method(InvRef, get_width),
-	method(InvRef, set_width),
-	method(InvRef, get_stack),
-	method(InvRef, set_stack),
-	method(InvRef, get_list),
-	method(InvRef, set_list),
-	method(InvRef, add_item),
-	method(InvRef, room_for_item),
-	method(InvRef, contains_item),
-	method(InvRef, remove_item),
-	method(InvRef, get_location),
-	{0,0}
-};
-
-/*
-	NodeMetaRef
-*/
-
-class NodeMetaRef
-{
-private:
-	v3s16 m_p;
-	ServerEnvironment *m_env;
-
-	static const char className[];
-	static const luaL_reg methods[];
-
-	static NodeMetaRef *checkobject(lua_State *L, int narg)
-	{
-		luaL_checktype(L, narg, LUA_TUSERDATA);
-		void *ud = luaL_checkudata(L, narg, className);
-		if(!ud) luaL_typerror(L, narg, className);
-		return *(NodeMetaRef**)ud;  // unbox pointer
-	}
-
-	static NodeMetadata* getmeta(NodeMetaRef *ref, bool auto_create)
-	{
-		NodeMetadata *meta = ref->m_env->getMap().getNodeMetadata(ref->m_p);
-		if(meta == NULL && auto_create)
-		{
-			meta = new NodeMetadata(ref->m_env->getGameDef());
-			ref->m_env->getMap().setNodeMetadata(ref->m_p, meta);
-		}
-		return meta;
-	}
-
-	static void reportMetadataChange(NodeMetaRef *ref)
-	{
-		// NOTE: This same code is in rollback_interface.cpp
-		// Inform other things that the metadata has changed
-		v3s16 blockpos = getNodeBlockPos(ref->m_p);
-		MapEditEvent event;
-		event.type = MEET_BLOCK_NODE_METADATA_CHANGED;
-		event.p = blockpos;
-		ref->m_env->getMap().dispatchEvent(&event);
-		// Set the block to be saved
-		MapBlock *block = ref->m_env->getMap().getBlockNoCreateNoEx(blockpos);
-		if(block)
-			block->raiseModified(MOD_STATE_WRITE_NEEDED,
-					"NodeMetaRef::reportMetadataChange");
-	}
-
-	// Exported functions
-
-	// garbage collector
-	static int gc_object(lua_State *L) {
-		NodeMetaRef *o = *(NodeMetaRef **)(lua_touserdata(L, 1));
-		delete o;
-		return 0;
-	}
-
-	// get_string(self, name)
-	static int l_get_string(lua_State *L)
-	{
-		NodeMetaRef *ref = checkobject(L, 1);
-		std::string name = luaL_checkstring(L, 2);
-
-		NodeMetadata *meta = getmeta(ref, false);
-		if(meta == NULL){
-			lua_pushlstring(L, "", 0);
-			return 1;
-		}
-		std::string str = meta->getString(name);
-		lua_pushlstring(L, str.c_str(), str.size());
-		return 1;
-	}
-
-	// set_string(self, name, var)
-	static int l_set_string(lua_State *L)
-	{
-		NodeMetaRef *ref = checkobject(L, 1);
-		std::string name = luaL_checkstring(L, 2);
-		size_t len = 0;
-		const char *s = lua_tolstring(L, 3, &len);
-		std::string str(s, len);
-
-		NodeMetadata *meta = getmeta(ref, !str.empty());
-		if(meta == NULL || str == meta->getString(name))
-			return 0;
-		meta->setString(name, str);
-		reportMetadataChange(ref);
-		return 0;
-	}
-
-	// get_int(self, name)
-	static int l_get_int(lua_State *L)
-	{
-		NodeMetaRef *ref = checkobject(L, 1);
-		std::string name = lua_tostring(L, 2);
-
-		NodeMetadata *meta = getmeta(ref, false);
-		if(meta == NULL){
-			lua_pushnumber(L, 0);
-			return 1;
-		}
-		std::string str = meta->getString(name);
-		lua_pushnumber(L, stoi(str));
-		return 1;
-	}
-
-	// set_int(self, name, var)
-	static int l_set_int(lua_State *L)
-	{
-		NodeMetaRef *ref = checkobject(L, 1);
-		std::string name = lua_tostring(L, 2);
-		int a = lua_tointeger(L, 3);
-		std::string str = itos(a);
-
-		NodeMetadata *meta = getmeta(ref, true);
-		if(meta == NULL || str == meta->getString(name))
-			return 0;
-		meta->setString(name, str);
-		reportMetadataChange(ref);
-		return 0;
-	}
-
-	// get_float(self, name)
-	static int l_get_float(lua_State *L)
-	{
-		NodeMetaRef *ref = checkobject(L, 1);
-		std::string name = lua_tostring(L, 2);
-
-		NodeMetadata *meta = getmeta(ref, false);
-		if(meta == NULL){
-			lua_pushnumber(L, 0);
-			return 1;
-		}
-		std::string str = meta->getString(name);
-		lua_pushnumber(L, stof(str));
-		return 1;
-	}
-
-	// set_float(self, name, var)
-	static int l_set_float(lua_State *L)
-	{
-		NodeMetaRef *ref = checkobject(L, 1);
-		std::string name = lua_tostring(L, 2);
-		float a = lua_tonumber(L, 3);
-		std::string str = ftos(a);
-
-		NodeMetadata *meta = getmeta(ref, true);
-		if(meta == NULL || str == meta->getString(name))
-			return 0;
-		meta->setString(name, str);
-		reportMetadataChange(ref);
-		return 0;
-	}
-
-	// get_inventory(self)
-	static int l_get_inventory(lua_State *L)
-	{
-		NodeMetaRef *ref = checkobject(L, 1);
-		getmeta(ref, true);  // try to ensure the metadata exists
-		InvRef::createNodeMeta(L, ref->m_p);
-		return 1;
-	}
-
-	// to_table(self)
-	static int l_to_table(lua_State *L)
-	{
-		NodeMetaRef *ref = checkobject(L, 1);
-
-		NodeMetadata *meta = getmeta(ref, true);
-		if(meta == NULL){
-			lua_pushnil(L);
-			return 1;
-		}
-		lua_newtable(L);
-		// fields
-		lua_newtable(L);
-		{
-			std::map<std::string, std::string> fields = meta->getStrings();
-			for(std::map<std::string, std::string>::const_iterator
-					i = fields.begin(); i != fields.end(); i++){
-				const std::string &name = i->first;
-				const std::string &value = i->second;
-				lua_pushlstring(L, name.c_str(), name.size());
-				lua_pushlstring(L, value.c_str(), value.size());
-				lua_settable(L, -3);
-			}
-		}
-		lua_setfield(L, -2, "fields");
-		// inventory
-		lua_newtable(L);
-		Inventory *inv = meta->getInventory();
-		if(inv){
-			std::vector<const InventoryList*> lists = inv->getLists();
-			for(std::vector<const InventoryList*>::const_iterator
-					i = lists.begin(); i != lists.end(); i++){
-				inventory_get_list_to_lua(inv, (*i)->getName().c_str(), L);
-				lua_setfield(L, -2, (*i)->getName().c_str());
-			}
-		}
-		lua_setfield(L, -2, "inventory");
-		return 1;
-	}
-
-	// from_table(self, table)
-	static int l_from_table(lua_State *L)
-	{
-		NodeMetaRef *ref = checkobject(L, 1);
-		int base = 2;
-
-		if(lua_isnil(L, base)){
-			// No metadata
-			ref->m_env->getMap().removeNodeMetadata(ref->m_p);
-			lua_pushboolean(L, true);
-			return 1;
-		}
-
-		// Has metadata; clear old one first
-		ref->m_env->getMap().removeNodeMetadata(ref->m_p);
-		// Create new metadata
-		NodeMetadata *meta = getmeta(ref, true);
-		// Set fields
-		lua_getfield(L, base, "fields");
-		int fieldstable = lua_gettop(L);
-		lua_pushnil(L);
-		while(lua_next(L, fieldstable) != 0){
-			// key at index -2 and value at index -1
-			std::string name = lua_tostring(L, -2);
-			size_t cl;
-			const char *cs = lua_tolstring(L, -1, &cl);
-			std::string value(cs, cl);
-			meta->setString(name, value);
-			lua_pop(L, 1); // removes value, keeps key for next iteration
-		}
-		// Set inventory
-		Inventory *inv = meta->getInventory();
-		lua_getfield(L, base, "inventory");
-		int inventorytable = lua_gettop(L);
-		lua_pushnil(L);
-		while(lua_next(L, inventorytable) != 0){
-			// key at index -2 and value at index -1
-			std::string name = lua_tostring(L, -2);
-			inventory_set_list_from_lua(inv, name.c_str(), L, -1);
-			lua_pop(L, 1); // removes value, keeps key for next iteration
-		}
-		reportMetadataChange(ref);
-		lua_pushboolean(L, true);
-		return 1;
-	}
-
-public:
-	NodeMetaRef(v3s16 p, ServerEnvironment *env):
-		m_p(p),
-		m_env(env)
-	{
-	}
-
-	~NodeMetaRef()
-	{
-	}
-
-	// Creates an NodeMetaRef and leaves it on top of stack
-	// Not callable from Lua; all references are created on the C side.
-	static void create(lua_State *L, v3s16 p, ServerEnvironment *env)
-	{
-		NodeMetaRef *o = new NodeMetaRef(p, env);
-		//infostream<<"NodeMetaRef::create: o="<<o<<std::endl;
-		*(void **)(lua_newuserdata(L, sizeof(void *))) = o;
-		luaL_getmetatable(L, className);
-		lua_setmetatable(L, -2);
-	}
-
-	static void Register(lua_State *L)
-	{
-		lua_newtable(L);
-		int methodtable = lua_gettop(L);
-		luaL_newmetatable(L, className);
-		int metatable = lua_gettop(L);
-
-		lua_pushliteral(L, "__metatable");
-		lua_pushvalue(L, methodtable);
-		lua_settable(L, metatable);  // hide metatable from Lua getmetatable()
-
-		lua_pushliteral(L, "__index");
-		lua_pushvalue(L, methodtable);
-		lua_settable(L, metatable);
-
-		lua_pushliteral(L, "__gc");
-		lua_pushcfunction(L, gc_object);
-		lua_settable(L, metatable);
-
-		lua_pop(L, 1);  // drop metatable
-
-		luaL_openlib(L, 0, methods, 0);  // fill methodtable
-		lua_pop(L, 1);  // drop methodtable
-
-		// Cannot be created from Lua
-		//lua_register(L, className, create_object);
-	}
-};
-const char NodeMetaRef::className[] = "NodeMetaRef";
-const luaL_reg NodeMetaRef::methods[] = {
-	method(NodeMetaRef, get_string),
-	method(NodeMetaRef, set_string),
-	method(NodeMetaRef, get_int),
-	method(NodeMetaRef, set_int),
-	method(NodeMetaRef, get_float),
-	method(NodeMetaRef, set_float),
-	method(NodeMetaRef, get_inventory),
-	method(NodeMetaRef, to_table),
-	method(NodeMetaRef, from_table),
-	{0,0}
-};
-
-/*
-	ObjectRef
-*/
-
-class ObjectRef
-{
-private:
-	ServerActiveObject *m_object;
-
-	static const char className[];
-	static const luaL_reg methods[];
-public:
-	static ObjectRef *checkobject(lua_State *L, int narg)
-	{
-		luaL_checktype(L, narg, LUA_TUSERDATA);
-		void *ud = luaL_checkudata(L, narg, className);
-		if(!ud) luaL_typerror(L, narg, className);
-		return *(ObjectRef**)ud;  // unbox pointer
-	}
-
-	static ServerActiveObject* getobject(ObjectRef *ref)
-	{
-		ServerActiveObject *co = ref->m_object;
-		return co;
-	}
-private:
-	static LuaEntitySAO* getluaobject(ObjectRef *ref)
-	{
-		ServerActiveObject *obj = getobject(ref);
-		if(obj == NULL)
-			return NULL;
-		if(obj->getType() != ACTIVEOBJECT_TYPE_LUAENTITY)
-			return NULL;
-		return (LuaEntitySAO*)obj;
-	}
-
-	static PlayerSAO* getplayersao(ObjectRef *ref)
-	{
-		ServerActiveObject *obj = getobject(ref);
-		if(obj == NULL)
-			return NULL;
-		if(obj->getType() != ACTIVEOBJECT_TYPE_PLAYER)
-			return NULL;
-		return (PlayerSAO*)obj;
-	}
-
-	static Player* getplayer(ObjectRef *ref)
-	{
-		PlayerSAO *playersao = getplayersao(ref);
-		if(playersao == NULL)
-			return NULL;
-		return playersao->getPlayer();
-	}
-
-	// Exported functions
-
-	// garbage collector
-	static int gc_object(lua_State *L) {
-		ObjectRef *o = *(ObjectRef **)(lua_touserdata(L, 1));
-		//infostream<<"ObjectRef::gc_object: o="<<o<<std::endl;
-		delete o;
-		return 0;
-	}
-
-	// remove(self)
-	static int l_remove(lua_State *L)
-	{
-		ObjectRef *ref = checkobject(L, 1);
-		ServerActiveObject *co = getobject(ref);
-		if(co == NULL) return 0;
-		verbosestream<<"ObjectRef::l_remove(): id="<<co->getId()<<std::endl;
-		co->m_removed = true;
-		return 0;
-	}
-
-	// getpos(self)
-	// returns: {x=num, y=num, z=num}
-	static int l_getpos(lua_State *L)
-	{
-		ObjectRef *ref = checkobject(L, 1);
-		ServerActiveObject *co = getobject(ref);
-		if(co == NULL) return 0;
-		v3f pos = co->getBasePosition() / BS;
-		lua_newtable(L);
-		lua_pushnumber(L, pos.X);
-		lua_setfield(L, -2, "x");
-		lua_pushnumber(L, pos.Y);
-		lua_setfield(L, -2, "y");
-		lua_pushnumber(L, pos.Z);
-		lua_setfield(L, -2, "z");
-		return 1;
-	}
-
-	// setpos(self, pos)
-	static int l_setpos(lua_State *L)
-	{
-		ObjectRef *ref = checkobject(L, 1);
-		//LuaEntitySAO *co = getluaobject(ref);
-		ServerActiveObject *co = getobject(ref);
-		if(co == NULL) return 0;
-		// pos
-		v3f pos = checkFloatPos(L, 2);
-		// Do it
-		co->setPos(pos);
-		return 0;
-	}
-
-	// moveto(self, pos, continuous=false)
-	static int l_moveto(lua_State *L)
-	{
-		ObjectRef *ref = checkobject(L, 1);
-		//LuaEntitySAO *co = getluaobject(ref);
-		ServerActiveObject *co = getobject(ref);
-		if(co == NULL) return 0;
-		// pos
-		v3f pos = checkFloatPos(L, 2);
-		// continuous
-		bool continuous = lua_toboolean(L, 3);
-		// Do it
-		co->moveTo(pos, continuous);
-		return 0;
-	}
-
-	// punch(self, puncher, time_from_last_punch, tool_capabilities, dir)
-	static int l_punch(lua_State *L)
-	{
-		ObjectRef *ref = checkobject(L, 1);
-		ObjectRef *puncher_ref = checkobject(L, 2);
-		ServerActiveObject *co = getobject(ref);
-		ServerActiveObject *puncher = getobject(puncher_ref);
-		if(co == NULL) return 0;
-		if(puncher == NULL) return 0;
-		v3f dir;
-		if(lua_type(L, 5) != LUA_TTABLE)
-			dir = co->getBasePosition() - puncher->getBasePosition();
-		else
-			dir = read_v3f(L, 5);
-		float time_from_last_punch = 1000000;
-		if(lua_isnumber(L, 3))
-			time_from_last_punch = lua_tonumber(L, 3);
-		ToolCapabilities toolcap = read_tool_capabilities(L, 4);
-		dir.normalize();
-		// Do it
-		co->punch(dir, &toolcap, puncher, time_from_last_punch);
-		return 0;
-	}
-
-	// right_click(self, clicker); clicker = an another ObjectRef
-	static int l_right_click(lua_State *L)
-	{
-		ObjectRef *ref = checkobject(L, 1);
-		ObjectRef *ref2 = checkobject(L, 2);
-		ServerActiveObject *co = getobject(ref);
-		ServerActiveObject *co2 = getobject(ref2);
-		if(co == NULL) return 0;
-		if(co2 == NULL) return 0;
-		// Do it
-		co->rightClick(co2);
-		return 0;
-	}
-
-	// set_hp(self, hp)
-	// hp = number of hitpoints (2 * number of hearts)
-	// returns: nil
-	static int l_set_hp(lua_State *L)
-	{
-		ObjectRef *ref = checkobject(L, 1);
-		luaL_checknumber(L, 2);
-		ServerActiveObject *co = getobject(ref);
-		if(co == NULL) return 0;
-		int hp = lua_tonumber(L, 2);
-		/*infostream<<"ObjectRef::l_set_hp(): id="<<co->getId()
-				<<" hp="<<hp<<std::endl;*/
-		// Do it
-		co->setHP(hp);
-		// Return
-		return 0;
-	}
-
-	// get_hp(self)
-	// returns: number of hitpoints (2 * number of hearts)
-	// 0 if not applicable to this type of object
-	static int l_get_hp(lua_State *L)
-	{
-		ObjectRef *ref = checkobject(L, 1);
-		ServerActiveObject *co = getobject(ref);
-		if(co == NULL){
-			// Default hp is 1
-			lua_pushnumber(L, 1);
-			return 1;
-		}
-		int hp = co->getHP();
-		/*infostream<<"ObjectRef::l_get_hp(): id="<<co->getId()
-				<<" hp="<<hp<<std::endl;*/
-		// Return
-		lua_pushnumber(L, hp);
-		return 1;
-	}
-
-	// get_inventory(self)
-	static int l_get_inventory(lua_State *L)
-	{
-		ObjectRef *ref = checkobject(L, 1);
-		ServerActiveObject *co = getobject(ref);
-		if(co == NULL) return 0;
-		// Do it
-		InventoryLocation loc = co->getInventoryLocation();
-		if(get_server(L)->getInventory(loc) != NULL)
-			InvRef::create(L, loc);
-		else
-			lua_pushnil(L); // An object may have no inventory (nil)
-		return 1;
-	}
-
-	// get_wield_list(self)
-	static int l_get_wield_list(lua_State *L)
-	{
-		ObjectRef *ref = checkobject(L, 1);
-		ServerActiveObject *co = getobject(ref);
-		if(co == NULL) return 0;
-		// Do it
-		lua_pushstring(L, co->getWieldList().c_str());
-		return 1;
-	}
-
-	// get_wield_index(self)
-	static int l_get_wield_index(lua_State *L)
-	{
-		ObjectRef *ref = checkobject(L, 1);
-		ServerActiveObject *co = getobject(ref);
-		if(co == NULL) return 0;
-		// Do it
-		lua_pushinteger(L, co->getWieldIndex() + 1);
-		return 1;
-	}
-
-	// get_wielded_item(self)
-	static int l_get_wielded_item(lua_State *L)
-	{
-		ObjectRef *ref = checkobject(L, 1);
-		ServerActiveObject *co = getobject(ref);
-		if(co == NULL){
-			// Empty ItemStack
-			LuaItemStack::create(L, ItemStack());
-			return 1;
-		}
-		// Do it
-		LuaItemStack::create(L, co->getWieldedItem());
-		return 1;
-	}
-
-	// set_wielded_item(self, itemstack or itemstring or table or nil)
-	static int l_set_wielded_item(lua_State *L)
-	{
-		ObjectRef *ref = checkobject(L, 1);
-		ServerActiveObject *co = getobject(ref);
-		if(co == NULL) return 0;
-		// Do it
-		ItemStack item = read_item(L, 2);
-		bool success = co->setWieldedItem(item);
-		lua_pushboolean(L, success);
-		return 1;
-	}
-
-	// set_armor_groups(self, groups)
-	static int l_set_armor_groups(lua_State *L)
-	{
-		ObjectRef *ref = checkobject(L, 1);
-		ServerActiveObject *co = getobject(ref);
-		if(co == NULL) return 0;
-		// Do it
-		ItemGroupList groups;
-		read_groups(L, 2, groups);
-		co->setArmorGroups(groups);
-		return 0;
-	}
-
-	// set_animation(self, frame_range, frame_speed, frame_blend)
-	static int l_set_animation(lua_State *L)
-	{
-		ObjectRef *ref = checkobject(L, 1);
-		ServerActiveObject *co = getobject(ref);
-		if(co == NULL) return 0;
-		// Do it
-		v2f frames = v2f(1, 1);
-		if(!lua_isnil(L, 2))
-			frames = read_v2f(L, 2);
-		float frame_speed = 15;
-		if(!lua_isnil(L, 3))
-			frame_speed = lua_tonumber(L, 3);
-		float frame_blend = 0;
-		if(!lua_isnil(L, 4))
-			frame_blend = lua_tonumber(L, 4);
-		co->setAnimation(frames, frame_speed, frame_blend);
-		return 0;
-	}
-
-	// set_bone_position(self, std::string bone, v3f position, v3f rotation)
-	static int l_set_bone_position(lua_State *L)
-	{
-		ObjectRef *ref = checkobject(L, 1);
-		ServerActiveObject *co = getobject(ref);
-		if(co == NULL) return 0;
-		// Do it
-		std::string bone = "";
-		if(!lua_isnil(L, 2))
-			bone = lua_tostring(L, 2);
-		v3f position = v3f(0, 0, 0);
-		if(!lua_isnil(L, 3))
-			position = read_v3f(L, 3);
-		v3f rotation = v3f(0, 0, 0);
-		if(!lua_isnil(L, 4))
-			rotation = read_v3f(L, 4);
-		co->setBonePosition(bone, position, rotation);
-		return 0;
-	}
-
-	// set_attach(self, parent, bone, position, rotation)
-	static int l_set_attach(lua_State *L)
-	{
-		ObjectRef *ref = checkobject(L, 1);
-		ObjectRef *parent_ref = checkobject(L, 2);
-		ServerActiveObject *co = getobject(ref);
-		ServerActiveObject *parent = getobject(parent_ref);
-		if(co == NULL) return 0;
-		if(parent == NULL) return 0;
-		// Do it
-		std::string bone = "";
-		if(!lua_isnil(L, 3))
-			bone = lua_tostring(L, 3);
-		v3f position = v3f(0, 0, 0);
-		if(!lua_isnil(L, 4))
-			position = read_v3f(L, 4);
-		v3f rotation = v3f(0, 0, 0);
-		if(!lua_isnil(L, 5))
-			rotation = read_v3f(L, 5);
-		co->setAttachment(parent->getId(), bone, position, rotation);
-		return 0;
-	}
-
-	// set_detach(self)
-	static int l_set_detach(lua_State *L)
-	{
-		ObjectRef *ref = checkobject(L, 1);
-		ServerActiveObject *co = getobject(ref);
-		if(co == NULL) return 0;
-		// Do it
-		co->setAttachment(0, "", v3f(0,0,0), v3f(0,0,0));
-		return 0;
-	}
-
-	// set_properties(self, properties)
-	static int l_set_properties(lua_State *L)
-	{
-		ObjectRef *ref = checkobject(L, 1);
-		ServerActiveObject *co = getobject(ref);
-		if(co == NULL) return 0;
-		ObjectProperties *prop = co->accessObjectProperties();
-		if(!prop)
-			return 0;
-		read_object_properties(L, 2, prop);
-		co->notifyObjectPropertiesModified();
-		return 0;
-	}
-
-	/* LuaEntitySAO-only */
-
-	// setvelocity(self, {x=num, y=num, z=num})
-	static int l_setvelocity(lua_State *L)
-	{
-		ObjectRef *ref = checkobject(L, 1);
-		LuaEntitySAO *co = getluaobject(ref);
-		if(co == NULL) return 0;
-		v3f pos = checkFloatPos(L, 2);
-		// Do it
-		co->setVelocity(pos);
-		return 0;
-	}
-
-	// getvelocity(self)
-	static int l_getvelocity(lua_State *L)
-	{
-		ObjectRef *ref = checkobject(L, 1);
-		LuaEntitySAO *co = getluaobject(ref);
-		if(co == NULL) return 0;
-		// Do it
-		v3f v = co->getVelocity();
-		pushFloatPos(L, v);
-		return 1;
-	}
-
-	// setacceleration(self, {x=num, y=num, z=num})
-	static int l_setacceleration(lua_State *L)
-	{
-		ObjectRef *ref = checkobject(L, 1);
-		LuaEntitySAO *co = getluaobject(ref);
-		if(co == NULL) return 0;
-		// pos
-		v3f pos = checkFloatPos(L, 2);
-		// Do it
-		co->setAcceleration(pos);
-		return 0;
-	}
-
-	// getacceleration(self)
-	static int l_getacceleration(lua_State *L)
-	{
-		ObjectRef *ref = checkobject(L, 1);
-		LuaEntitySAO *co = getluaobject(ref);
-		if(co == NULL) return 0;
-		// Do it
-		v3f v = co->getAcceleration();
-		pushFloatPos(L, v);
-		return 1;
-	}
-
-	// setyaw(self, radians)
-	static int l_setyaw(lua_State *L)
-	{
-		ObjectRef *ref = checkobject(L, 1);
-		LuaEntitySAO *co = getluaobject(ref);
-		if(co == NULL) return 0;
-		float yaw = luaL_checknumber(L, 2) * core::RADTODEG;
-		// Do it
-		co->setYaw(yaw);
-		return 0;
-	}
-
-	// getyaw(self)
-	static int l_getyaw(lua_State *L)
-	{
-		ObjectRef *ref = checkobject(L, 1);
-		LuaEntitySAO *co = getluaobject(ref);
-		if(co == NULL) return 0;
-		// Do it
-		float yaw = co->getYaw() * core::DEGTORAD;
-		lua_pushnumber(L, yaw);
-		return 1;
-	}
-
-	// settexturemod(self, mod)
-	static int l_settexturemod(lua_State *L)
-	{
-		ObjectRef *ref = checkobject(L, 1);
-		LuaEntitySAO *co = getluaobject(ref);
-		if(co == NULL) return 0;
-		// Do it
-		std::string mod = luaL_checkstring(L, 2);
-		co->setTextureMod(mod);
-		return 0;
-	}
-
-	// setsprite(self, p={x=0,y=0}, num_frames=1, framelength=0.2,
-	//           select_horiz_by_yawpitch=false)
-	static int l_setsprite(lua_State *L)
-	{
-		ObjectRef *ref = checkobject(L, 1);
-		LuaEntitySAO *co = getluaobject(ref);
-		if(co == NULL) return 0;
-		// Do it
-		v2s16 p(0,0);
-		if(!lua_isnil(L, 2))
-			p = read_v2s16(L, 2);
-		int num_frames = 1;
-		if(!lua_isnil(L, 3))
-			num_frames = lua_tonumber(L, 3);
-		float framelength = 0.2;
-		if(!lua_isnil(L, 4))
-			framelength = lua_tonumber(L, 4);
-		bool select_horiz_by_yawpitch = false;
-		if(!lua_isnil(L, 5))
-			select_horiz_by_yawpitch = lua_toboolean(L, 5);
-		co->setSprite(p, num_frames, framelength, select_horiz_by_yawpitch);
-		return 0;
-	}
-
-	// DEPRECATED
-	// get_entity_name(self)
-	static int l_get_entity_name(lua_State *L)
-	{
-		ObjectRef *ref = checkobject(L, 1);
-		LuaEntitySAO *co = getluaobject(ref);
-		if(co == NULL) return 0;
-		// Do it
-		std::string name = co->getName();
-		lua_pushstring(L, name.c_str());
-		return 1;
-	}
-
-	// get_luaentity(self)
-	static int l_get_luaentity(lua_State *L)
-	{
-		ObjectRef *ref = checkobject(L, 1);
-		LuaEntitySAO *co = getluaobject(ref);
-		if(co == NULL) return 0;
-		// Do it
-		luaentity_get(L, co->getId());
-		return 1;
-	}
-
-	/* Player-only */
-
-	// is_player(self)
-	static int l_is_player(lua_State *L)
-	{
-		ObjectRef *ref = checkobject(L, 1);
-		Player *player = getplayer(ref);
-		lua_pushboolean(L, (player != NULL));
-		return 1;
-	}
-
-	// get_player_name(self)
-	static int l_get_player_name(lua_State *L)
-	{
-		ObjectRef *ref = checkobject(L, 1);
-		Player *player = getplayer(ref);
-		if(player == NULL){
-			lua_pushlstring(L, "", 0);
-			return 1;
-		}
-		// Do it
-		lua_pushstring(L, player->getName());
-		return 1;
-	}
-
-	// get_look_dir(self)
-	static int l_get_look_dir(lua_State *L)
-	{
-		ObjectRef *ref = checkobject(L, 1);
-		Player *player = getplayer(ref);
-		if(player == NULL) return 0;
-		// Do it
-		float pitch = player->getRadPitch();
-		float yaw = player->getRadYaw();
-		v3f v(cos(pitch)*cos(yaw), sin(pitch), cos(pitch)*sin(yaw));
-		push_v3f(L, v);
-		return 1;
-	}
-
-	// get_look_pitch(self)
-	static int l_get_look_pitch(lua_State *L)
-	{
-		ObjectRef *ref = checkobject(L, 1);
-		Player *player = getplayer(ref);
-		if(player == NULL) return 0;
-		// Do it
-		lua_pushnumber(L, player->getRadPitch());
-		return 1;
-	}
-
-	// get_look_yaw(self)
-	static int l_get_look_yaw(lua_State *L)
-	{
-		ObjectRef *ref = checkobject(L, 1);
-		Player *player = getplayer(ref);
-		if(player == NULL) return 0;
-		// Do it
-		lua_pushnumber(L, player->getRadYaw());
-		return 1;
-	}
-
-	// set_inventory_formspec(self, formspec)
-	static int l_set_inventory_formspec(lua_State *L)
-	{
-		ObjectRef *ref = checkobject(L, 1);
-		Player *player = getplayer(ref);
-		if(player == NULL) return 0;
-		std::string formspec = luaL_checkstring(L, 2);
-
-		player->inventory_formspec = formspec;
-		get_server(L)->reportInventoryFormspecModified(player->getName());
-		lua_pushboolean(L, true);
-		return 1;
-	}
-
-	// get_inventory_formspec(self) -> formspec
-	static int l_get_inventory_formspec(lua_State *L)
-	{
-		ObjectRef *ref = checkobject(L, 1);
-		Player *player = getplayer(ref);
-		if(player == NULL) return 0;
-
-		std::string formspec = player->inventory_formspec;
-		lua_pushlstring(L, formspec.c_str(), formspec.size());
-		return 1;
-	}
-	
-	// get_player_control(self)
-	static int l_get_player_control(lua_State *L)
-	{
-		ObjectRef *ref = checkobject(L, 1);
-		Player *player = getplayer(ref);
-		if(player == NULL){
-			lua_pushlstring(L, "", 0);
-			return 1;
-		}
-		// Do it
-		PlayerControl control = player->getPlayerControl();
-		lua_newtable(L);
-		lua_pushboolean(L, control.up);
-		lua_setfield(L, -2, "up");
-		lua_pushboolean(L, control.down);
-		lua_setfield(L, -2, "down");
-		lua_pushboolean(L, control.left);
-		lua_setfield(L, -2, "left");
-		lua_pushboolean(L, control.right);
-		lua_setfield(L, -2, "right");
-		lua_pushboolean(L, control.jump);
-		lua_setfield(L, -2, "jump");
-		lua_pushboolean(L, control.aux1);
-		lua_setfield(L, -2, "aux1");
-		lua_pushboolean(L, control.sneak);
-		lua_setfield(L, -2, "sneak");
-		lua_pushboolean(L, control.LMB);
-		lua_setfield(L, -2, "LMB");
-		lua_pushboolean(L, control.RMB);
-		lua_setfield(L, -2, "RMB");
-		return 1;
-	}
-	
-	// get_player_control_bits(self)
-	static int l_get_player_control_bits(lua_State *L)
-	{
-		ObjectRef *ref = checkobject(L, 1);
-		Player *player = getplayer(ref);
-		if(player == NULL){
-			lua_pushlstring(L, "", 0);
-			return 1;
-		}
-		// Do it	
-		lua_pushnumber(L, player->keyPressed);
-		return 1;
-	}
-	
-public:
-	ObjectRef(ServerActiveObject *object):
-		m_object(object)
-	{
-		//infostream<<"ObjectRef created for id="<<m_object->getId()<<std::endl;
-	}
-
-	~ObjectRef()
-	{
-		/*if(m_object)
-			infostream<<"ObjectRef destructing for id="
-					<<m_object->getId()<<std::endl;
-		else
-			infostream<<"ObjectRef destructing for id=unknown"<<std::endl;*/
-	}
-
-	// Creates an ObjectRef and leaves it on top of stack
-	// Not callable from Lua; all references are created on the C side.
-	static void create(lua_State *L, ServerActiveObject *object)
-	{
-		ObjectRef *o = new ObjectRef(object);
-		//infostream<<"ObjectRef::create: o="<<o<<std::endl;
-		*(void **)(lua_newuserdata(L, sizeof(void *))) = o;
-		luaL_getmetatable(L, className);
-		lua_setmetatable(L, -2);
-	}
-
-	static void set_null(lua_State *L)
-	{
-		ObjectRef *o = checkobject(L, -1);
-		o->m_object = NULL;
-	}
-
-	static void Register(lua_State *L)
-	{
-		lua_newtable(L);
-		int methodtable = lua_gettop(L);
-		luaL_newmetatable(L, className);
-		int metatable = lua_gettop(L);
-
-		lua_pushliteral(L, "__metatable");
-		lua_pushvalue(L, methodtable);
-		lua_settable(L, metatable);  // hide metatable from Lua getmetatable()
-
-		lua_pushliteral(L, "__index");
-		lua_pushvalue(L, methodtable);
-		lua_settable(L, metatable);
-
-		lua_pushliteral(L, "__gc");
-		lua_pushcfunction(L, gc_object);
-		lua_settable(L, metatable);
-
-		lua_pop(L, 1);  // drop metatable
-
-		luaL_openlib(L, 0, methods, 0);  // fill methodtable
-		lua_pop(L, 1);  // drop methodtable
-
-		// Cannot be created from Lua
-		//lua_register(L, className, create_object);
-	}
-};
-const char ObjectRef::className[] = "ObjectRef";
-const luaL_reg ObjectRef::methods[] = {
-	// ServerActiveObject
-	method(ObjectRef, remove),
-	method(ObjectRef, getpos),
-	method(ObjectRef, setpos),
-	method(ObjectRef, moveto),
-	method(ObjectRef, punch),
-	method(ObjectRef, right_click),
-	method(ObjectRef, set_hp),
-	method(ObjectRef, get_hp),
-	method(ObjectRef, get_inventory),
-	method(ObjectRef, get_wield_list),
-	method(ObjectRef, get_wield_index),
-	method(ObjectRef, get_wielded_item),
-	method(ObjectRef, set_wielded_item),
-	method(ObjectRef, set_armor_groups),
-	method(ObjectRef, set_animation),
-	method(ObjectRef, set_bone_position),
-	method(ObjectRef, set_attach),
-	method(ObjectRef, set_detach),
-	method(ObjectRef, set_properties),
-	// LuaEntitySAO-only
-	method(ObjectRef, setvelocity),
-	method(ObjectRef, getvelocity),
-	method(ObjectRef, setacceleration),
-	method(ObjectRef, getacceleration),
-	method(ObjectRef, setyaw),
-	method(ObjectRef, getyaw),
-	method(ObjectRef, settexturemod),
-	method(ObjectRef, setsprite),
-	method(ObjectRef, get_entity_name),
-	method(ObjectRef, get_luaentity),
-	// Player-only
-	method(ObjectRef, is_player),
-	method(ObjectRef, get_player_name),
-	method(ObjectRef, get_look_dir),
-	method(ObjectRef, get_look_pitch),
-	method(ObjectRef, get_look_yaw),
-	method(ObjectRef, set_inventory_formspec),
-	method(ObjectRef, get_inventory_formspec),
-	method(ObjectRef, get_player_control),
-	method(ObjectRef, get_player_control_bits),
-	{0,0}
-};
-
-// Creates a new anonymous reference if cobj=NULL or id=0
-static void objectref_get_or_create(lua_State *L,
-		ServerActiveObject *cobj)
-{
-	if(cobj == NULL || cobj->getId() == 0){
-		ObjectRef::create(L, cobj);
-	} else {
-		objectref_get(L, cobj->getId());
-	}
-}
-
-class LuaPerlinNoise
-{
-private:
-	int seed;
-	int octaves;
-	float persistence;
-	float scale;
-	static const char className[];
-	static const luaL_reg methods[];
-
-	// Exported functions
-
-	// garbage collector
-	static int gc_object(lua_State *L)
-	{
-		LuaPerlinNoise *o = *(LuaPerlinNoise **)(lua_touserdata(L, 1));
-		delete o;
-		return 0;
-	}
-
-	static int l_get2d(lua_State *L)
-	{
-		LuaPerlinNoise *o = checkobject(L, 1);
-		v2f pos2d = read_v2f(L,2);
-		lua_Number val = noise2d_perlin(pos2d.X/o->scale, pos2d.Y/o->scale, o->seed, o->octaves, o->persistence);
-		lua_pushnumber(L, val);
-		return 1;
-	}
-	static int l_get3d(lua_State *L)
-	{
-		LuaPerlinNoise *o = checkobject(L, 1);
-		v3f pos3d = read_v3f(L,2);
-		lua_Number val = noise3d_perlin(pos3d.X/o->scale, pos3d.Y/o->scale, pos3d.Z/o->scale, o->seed, o->octaves, o->persistence);
-		lua_pushnumber(L, val);
-		return 1;
-	}
-
-public:
-	LuaPerlinNoise(int a_seed, int a_octaves, float a_persistence,
-			float a_scale):
-		seed(a_seed),
-		octaves(a_octaves),
-		persistence(a_persistence),
-		scale(a_scale)
-	{
-	}
-
-	~LuaPerlinNoise()
-	{
-	}
-
-	// LuaPerlinNoise(seed, octaves, persistence, scale)
-	// Creates an LuaPerlinNoise and leaves it on top of stack
-	static int create_object(lua_State *L)
-	{
-		int seed = luaL_checkint(L, 1);
-		int octaves = luaL_checkint(L, 2);
-		float persistence = luaL_checknumber(L, 3);
-		float scale = luaL_checknumber(L, 4);
-		LuaPerlinNoise *o = new LuaPerlinNoise(seed, octaves, persistence, scale);
-		*(void **)(lua_newuserdata(L, sizeof(void *))) = o;
-		luaL_getmetatable(L, className);
-		lua_setmetatable(L, -2);
-		return 1;
-	}
-
-	static LuaPerlinNoise* checkobject(lua_State *L, int narg)
-	{
-		luaL_checktype(L, narg, LUA_TUSERDATA);
-		void *ud = luaL_checkudata(L, narg, className);
-		if(!ud) luaL_typerror(L, narg, className);
-		return *(LuaPerlinNoise**)ud;  // unbox pointer
-	}
-
-	static void Register(lua_State *L)
-	{
-		lua_newtable(L);
-		int methodtable = lua_gettop(L);
-		luaL_newmetatable(L, className);
-		int metatable = lua_gettop(L);
-
-		lua_pushliteral(L, "__metatable");
-		lua_pushvalue(L, methodtable);
-		lua_settable(L, metatable);  // hide metatable from Lua getmetatable()
-
-		lua_pushliteral(L, "__index");
-		lua_pushvalue(L, methodtable);
-		lua_settable(L, metatable);
-
-		lua_pushliteral(L, "__gc");
-		lua_pushcfunction(L, gc_object);
-		lua_settable(L, metatable);
-
-		lua_pop(L, 1);  // drop metatable
-
-		luaL_openlib(L, 0, methods, 0);  // fill methodtable
-		lua_pop(L, 1);  // drop methodtable
-
-		// Can be created from Lua (PerlinNoise(seed, octaves, persistence)
-		lua_register(L, className, create_object);
-	}
-};
-const char LuaPerlinNoise::className[] = "PerlinNoise";
-const luaL_reg LuaPerlinNoise::methods[] = {
-	method(LuaPerlinNoise, get2d),
-	method(LuaPerlinNoise, get3d),
-	{0,0}
-};
-
-/*
-  PerlinNoiseMap
- */
-class LuaPerlinNoiseMap
-{
-private:
-	Noise *noise;
-	static const char className[];
-	static const luaL_reg methods[];
-
-	static int gc_object(lua_State *L)
-	{
-		LuaPerlinNoiseMap *o = *(LuaPerlinNoiseMap **)(lua_touserdata(L, 1));
-		delete o;
-		return 0;
-	}
-
-	static int l_get2dMap(lua_State *L)
-	{
-		int i = 0;
-
-		LuaPerlinNoiseMap *o = checkobject(L, 1);
-		v2f p = read_v2f(L, 2);
-		
-		Noise *n = o->noise;
-		n->perlinMap2D(p.X, p.Y);
-		
-		lua_newtable(L);
-		for (int y = 0; y != n->sy; y++) {
-			lua_newtable(L);
-			for (int x = 0; x != n->sx; x++) {
-				float noiseval = n->np->offset + n->np->scale * n->result[i++];
-				lua_pushnumber(L, noiseval);
-				lua_rawseti(L, -2, x + 1);
-			}
-			lua_rawseti(L, -2, y + 1);
-		}
-		return 1;
-	}
-	
-	static int l_get3dMap(lua_State *L)
-	{
-		int i = 0;
-		
-		LuaPerlinNoiseMap *o = checkobject(L, 1);
-		v3f p = read_v3f(L, 2);
-		
-		Noise *n = o->noise;
-		n->perlinMap3D(p.X, p.Y, p.Z);
-
-		lua_newtable(L);
-		for (int z = 0; z != n->sz; z++) {
-			lua_newtable(L);
-			for (int y = 0; y != n->sy; y++) {
-				lua_newtable(L);
-				for (int x = 0; x != n->sx; x++) {
-					lua_pushnumber(L, n->np->offset + n->np->scale * n->result[i++]);
-					lua_rawseti(L, -2, x + 1);
-				}
-				lua_rawseti(L, -2, y + 1);
-			}
-			lua_rawseti(L, -2, z + 1);
-		}
-		return 1;
-	}
-
-public:
-	LuaPerlinNoiseMap(NoiseParams *np, int seed, v3s16 size) {
-		noise = new Noise(np, seed, size.X, size.Y, size.Z);
-	}
-
-	~LuaPerlinNoiseMap()
-	{
-		delete noise->np;
-		delete noise;
-	}
-
-	// LuaPerlinNoiseMap(np, size)
-	// Creates an LuaPerlinNoiseMap and leaves it on top of stack
-	static int create_object(lua_State *L)
-	{
-		NoiseParams *np = read_noiseparams(L, 1);
-		if (!np)
-			return 0;
-		v3s16 size = read_v3s16(L, 2);
-
-		LuaPerlinNoiseMap *o = new LuaPerlinNoiseMap(np, 0, size);
-		*(void **)(lua_newuserdata(L, sizeof(void *))) = o;
-		luaL_getmetatable(L, className);
-		lua_setmetatable(L, -2);
-		return 1;
-	}
-
-	static LuaPerlinNoiseMap *checkobject(lua_State *L, int narg)
-	{
-		luaL_checktype(L, narg, LUA_TUSERDATA);
-		
-		void *ud = luaL_checkudata(L, narg, className);
-		if (!ud)
-			luaL_typerror(L, narg, className);
-		
-		return *(LuaPerlinNoiseMap **)ud;  // unbox pointer
-	}
-
-	static void Register(lua_State *L)
-	{
-		lua_newtable(L);
-		int methodtable = lua_gettop(L);
-		luaL_newmetatable(L, className);
-		int metatable = lua_gettop(L);
-
-		lua_pushliteral(L, "__metatable");
-		lua_pushvalue(L, methodtable);
-		lua_settable(L, metatable);  // hide metatable from Lua getmetatable()
-
-		lua_pushliteral(L, "__index");
-		lua_pushvalue(L, methodtable);
-		lua_settable(L, metatable);
-
-		lua_pushliteral(L, "__gc");
-		lua_pushcfunction(L, gc_object);
-		lua_settable(L, metatable);
-
-		lua_pop(L, 1);  // drop metatable
-
-		luaL_openlib(L, 0, methods, 0);  // fill methodtable
-		lua_pop(L, 1);  // drop methodtable
-
-		// Can be created from Lua (PerlinNoiseMap(np, size)
-		lua_register(L, className, create_object);
-	}
-};
-const char LuaPerlinNoiseMap::className[] = "PerlinNoiseMap";
-const luaL_reg LuaPerlinNoiseMap::methods[] = {
-	method(LuaPerlinNoiseMap, get2dMap),
-	method(LuaPerlinNoiseMap, get3dMap),
-	{0,0}
-};
-
-/*
-	NodeTimerRef
-*/
-
-class NodeTimerRef
-{
-private:
-	v3s16 m_p;
-	ServerEnvironment *m_env;
-
-	static const char className[];
-	static const luaL_reg methods[];
-
-	static int gc_object(lua_State *L) {
-		NodeTimerRef *o = *(NodeTimerRef **)(lua_touserdata(L, 1));
-		delete o;
-		return 0;
-	}
-
-	static NodeTimerRef *checkobject(lua_State *L, int narg)
-	{
-		luaL_checktype(L, narg, LUA_TUSERDATA);
-		void *ud = luaL_checkudata(L, narg, className);
-		if(!ud) luaL_typerror(L, narg, className);
-		return *(NodeTimerRef**)ud;  // unbox pointer
-	}
-
-	static int l_set(lua_State *L)
-	{
-		NodeTimerRef *o = checkobject(L, 1);
-		ServerEnvironment *env = o->m_env;
-		if(env == NULL) return 0;
-		f32 t = luaL_checknumber(L,2);
-		f32 e = luaL_checknumber(L,3);
-		env->getMap().setNodeTimer(o->m_p,NodeTimer(t,e));
-		return 0;
-	}
-
-	static int l_start(lua_State *L)
-	{
-		NodeTimerRef *o = checkobject(L, 1);
-		ServerEnvironment *env = o->m_env;
-		if(env == NULL) return 0;
-		f32 t = luaL_checknumber(L,2);
-		env->getMap().setNodeTimer(o->m_p,NodeTimer(t,0));
-		return 0;
-	}
-
-	static int l_stop(lua_State *L)
-	{
-		NodeTimerRef *o = checkobject(L, 1);
-		ServerEnvironment *env = o->m_env;
-		if(env == NULL) return 0;
-		env->getMap().removeNodeTimer(o->m_p);
-		return 0;
-	}
-
-	static int l_is_started(lua_State *L)
-	{
-		NodeTimerRef *o = checkobject(L, 1);
-		ServerEnvironment *env = o->m_env;
-		if(env == NULL) return 0;
-
-		NodeTimer t = env->getMap().getNodeTimer(o->m_p);
-		lua_pushboolean(L,(t.timeout != 0));
-		return 1;
-	}
-
-	static int l_get_timeout(lua_State *L)
-	{
-		NodeTimerRef *o = checkobject(L, 1);
-		ServerEnvironment *env = o->m_env;
-		if(env == NULL) return 0;
-
-		NodeTimer t = env->getMap().getNodeTimer(o->m_p);
-		lua_pushnumber(L,t.timeout);
-		return 1;
-	}
-
-	static int l_get_elapsed(lua_State *L)
-	{
-		NodeTimerRef *o = checkobject(L, 1);
-		ServerEnvironment *env = o->m_env;
-		if(env == NULL) return 0;
-
-		NodeTimer t = env->getMap().getNodeTimer(o->m_p);
-		lua_pushnumber(L,t.elapsed);
-		return 1;
-	}
-
-public:
-	NodeTimerRef(v3s16 p, ServerEnvironment *env):
-		m_p(p),
-		m_env(env)
-	{
-	}
-
-	~NodeTimerRef()
-	{
-	}
-
-	// Creates an NodeTimerRef and leaves it on top of stack
-	// Not callable from Lua; all references are created on the C side.
-	static void create(lua_State *L, v3s16 p, ServerEnvironment *env)
-	{
-		NodeTimerRef *o = new NodeTimerRef(p, env);
-		*(void **)(lua_newuserdata(L, sizeof(void *))) = o;
-		luaL_getmetatable(L, className);
-		lua_setmetatable(L, -2);
-	}
-
-	static void set_null(lua_State *L)
-	{
-		NodeTimerRef *o = checkobject(L, -1);
-		o->m_env = NULL;
-	}
-
-	static void Register(lua_State *L)
-	{
-		lua_newtable(L);
-		int methodtable = lua_gettop(L);
-		luaL_newmetatable(L, className);
-		int metatable = lua_gettop(L);
-
-		lua_pushliteral(L, "__metatable");
-		lua_pushvalue(L, methodtable);
-		lua_settable(L, metatable);  // hide metatable from Lua getmetatable()
-
-		lua_pushliteral(L, "__index");
-		lua_pushvalue(L, methodtable);
-		lua_settable(L, metatable);
-
-		lua_pushliteral(L, "__gc");
-		lua_pushcfunction(L, gc_object);
-		lua_settable(L, metatable);
-
-		lua_pop(L, 1);  // drop metatable
-
-		luaL_openlib(L, 0, methods, 0);  // fill methodtable
-		lua_pop(L, 1);  // drop methodtable
-
-		// Cannot be created from Lua
-		//lua_register(L, className, create_object);
-	}
-};
-const char NodeTimerRef::className[] = "NodeTimerRef";
-const luaL_reg NodeTimerRef::methods[] = {
-	method(NodeTimerRef, start),
-	method(NodeTimerRef, set),
-	method(NodeTimerRef, stop),
-	method(NodeTimerRef, is_started),
-	method(NodeTimerRef, get_timeout),
-	method(NodeTimerRef, get_elapsed),
-	{0,0}
-};
-
-/*
-	EnvRef
-*/
-
-class EnvRef
-{
-private:
-	ServerEnvironment *m_env;
-
-	static const char className[];
-	static const luaL_reg methods[];
-
-	static int gc_object(lua_State *L) {
-		EnvRef *o = *(EnvRef **)(lua_touserdata(L, 1));
-		delete o;
-		return 0;
-	}
-
-	static EnvRef *checkobject(lua_State *L, int narg)
-	{
-		luaL_checktype(L, narg, LUA_TUSERDATA);
-		void *ud = luaL_checkudata(L, narg, className);
-		if(!ud) luaL_typerror(L, narg, className);
-		return *(EnvRef**)ud;  // unbox pointer
-	}
-
-	// Exported functions
-
-	// EnvRef:set_node(pos, node)
-	// pos = {x=num, y=num, z=num}
-	static int l_set_node(lua_State *L)
-	{
-		EnvRef *o = checkobject(L, 1);
-		ServerEnvironment *env = o->m_env;
-		if(env == NULL) return 0;
-		INodeDefManager *ndef = env->getGameDef()->ndef();
-		// parameters
-		v3s16 pos = read_v3s16(L, 2);
-		MapNode n = readnode(L, 3, ndef);
-		// Do it
-		bool succeeded = env->setNode(pos, n);
-		lua_pushboolean(L, succeeded);
-		return 1;
-	}
-
-	static int l_add_node(lua_State *L)
-	{
-		return l_set_node(L);
-	}
-
-	// EnvRef:remove_node(pos)
-	// pos = {x=num, y=num, z=num}
-	static int l_remove_node(lua_State *L)
-	{
-		EnvRef *o = checkobject(L, 1);
-		ServerEnvironment *env = o->m_env;
-		if(env == NULL) return 0;
-		INodeDefManager *ndef = env->getGameDef()->ndef();
-		// parameters
-		v3s16 pos = read_v3s16(L, 2);
-		// Do it
-		bool succeeded = env->removeNode(pos);
-		lua_pushboolean(L, succeeded);
-		return 1;
-	}
-
-	// EnvRef:get_node(pos)
-	// pos = {x=num, y=num, z=num}
-	static int l_get_node(lua_State *L)
-	{
-		EnvRef *o = checkobject(L, 1);
-		ServerEnvironment *env = o->m_env;
-		if(env == NULL) return 0;
-		// pos
-		v3s16 pos = read_v3s16(L, 2);
-		// Do it
-		MapNode n = env->getMap().getNodeNoEx(pos);
-		// Return node
-		pushnode(L, n, env->getGameDef()->ndef());
-		return 1;
-	}
-
-	// EnvRef:get_node_or_nil(pos)
-	// pos = {x=num, y=num, z=num}
-	static int l_get_node_or_nil(lua_State *L)
-	{
-		EnvRef *o = checkobject(L, 1);
-		ServerEnvironment *env = o->m_env;
-		if(env == NULL) return 0;
-		// pos
-		v3s16 pos = read_v3s16(L, 2);
-		// Do it
-		try{
-			MapNode n = env->getMap().getNode(pos);
-			// Return node
-			pushnode(L, n, env->getGameDef()->ndef());
-			return 1;
-		} catch(InvalidPositionException &e)
-		{
-			lua_pushnil(L);
-			return 1;
-		}
-	}
-
-	// EnvRef:get_node_light(pos, timeofday)
-	// pos = {x=num, y=num, z=num}
-	// timeofday: nil = current time, 0 = night, 0.5 = day
-	static int l_get_node_light(lua_State *L)
-	{
-		EnvRef *o = checkobject(L, 1);
-		ServerEnvironment *env = o->m_env;
-		if(env == NULL) return 0;
-		// Do it
-		v3s16 pos = read_v3s16(L, 2);
-		u32 time_of_day = env->getTimeOfDay();
-		if(lua_isnumber(L, 3))
-			time_of_day = 24000.0 * lua_tonumber(L, 3);
-		time_of_day %= 24000;
-		u32 dnr = time_to_daynight_ratio(time_of_day, true);
-		MapNode n = env->getMap().getNodeNoEx(pos);
-		try{
-			MapNode n = env->getMap().getNode(pos);
-			INodeDefManager *ndef = env->getGameDef()->ndef();
-			lua_pushinteger(L, n.getLightBlend(dnr, ndef));
-			return 1;
-		} catch(InvalidPositionException &e)
-		{
-			lua_pushnil(L);
-			return 1;
-		}
-	}
-
-	// EnvRef:place_node(pos, node)
-	// pos = {x=num, y=num, z=num}
-	static int l_place_node(lua_State *L)
-	{
-		EnvRef *o = checkobject(L, 1);
-		ServerEnvironment *env = o->m_env;
-		if(env == NULL) return 0;
-		v3s16 pos = read_v3s16(L, 2);
-		MapNode n = readnode(L, 3, env->getGameDef()->ndef());
-
-		// Don't attempt to load non-loaded area as of now
-		MapNode n_old = env->getMap().getNodeNoEx(pos);
-		if(n_old.getContent() == CONTENT_IGNORE){
-			lua_pushboolean(L, false);
-			return 1;
-		}
-		// Create item to place
-		INodeDefManager *ndef = get_server(L)->ndef();
-		IItemDefManager *idef = get_server(L)->idef();
-		ItemStack item(ndef->get(n).name, 1, 0, "", idef);
-		// Make pointed position
-		PointedThing pointed;
-		pointed.type = POINTEDTHING_NODE;
-		pointed.node_abovesurface = pos;
-		pointed.node_undersurface = pos + v3s16(0,-1,0);
-		// Place it with a NULL placer (appears in Lua as a non-functional
-		// ObjectRef)
-		bool success = scriptapi_item_on_place(L, item, NULL, pointed);
-		lua_pushboolean(L, success);
-		return 1;
-	}
-
-	// EnvRef:dig_node(pos)
-	// pos = {x=num, y=num, z=num}
-	static int l_dig_node(lua_State *L)
-	{
-		EnvRef *o = checkobject(L, 1);
-		ServerEnvironment *env = o->m_env;
-		if(env == NULL) return 0;
-		v3s16 pos = read_v3s16(L, 2);
-
-		// Don't attempt to load non-loaded area as of now
-		MapNode n = env->getMap().getNodeNoEx(pos);
-		if(n.getContent() == CONTENT_IGNORE){
-			lua_pushboolean(L, false);
-			return 1;
-		}
-		// Dig it out with a NULL digger (appears in Lua as a
-		// non-functional ObjectRef)
-		bool success = scriptapi_node_on_dig(L, pos, n, NULL);
-		lua_pushboolean(L, success);
-		return 1;
-	}
-
-	// EnvRef:punch_node(pos)
-	// pos = {x=num, y=num, z=num}
-	static int l_punch_node(lua_State *L)
-	{
-		EnvRef *o = checkobject(L, 1);
-		ServerEnvironment *env = o->m_env;
-		if(env == NULL) return 0;
-		v3s16 pos = read_v3s16(L, 2);
-
-		// Don't attempt to load non-loaded area as of now
-		MapNode n = env->getMap().getNodeNoEx(pos);
-		if(n.getContent() == CONTENT_IGNORE){
-			lua_pushboolean(L, false);
-			return 1;
-		}
-		// Punch it with a NULL puncher (appears in Lua as a non-functional
-		// ObjectRef)
-		bool success = scriptapi_node_on_punch(L, pos, n, NULL);
-		lua_pushboolean(L, success);
-		return 1;
-	}
-
-	// EnvRef:get_meta(pos)
-	static int l_get_meta(lua_State *L)
-	{
-		//infostream<<"EnvRef::l_get_meta()"<<std::endl;
-		EnvRef *o = checkobject(L, 1);
-		ServerEnvironment *env = o->m_env;
-		if(env == NULL) return 0;
-		// Do it
-		v3s16 p = read_v3s16(L, 2);
-		NodeMetaRef::create(L, p, env);
-		return 1;
-	}
-
-	// EnvRef:get_node_timer(pos)
-	static int l_get_node_timer(lua_State *L)
-	{
-		EnvRef *o = checkobject(L, 1);
-		ServerEnvironment *env = o->m_env;
-		if(env == NULL) return 0;
-		// Do it
-		v3s16 p = read_v3s16(L, 2);
-		NodeTimerRef::create(L, p, env);
-		return 1;
-	}
-
-	// EnvRef:add_entity(pos, entityname) -> ObjectRef or nil
-	// pos = {x=num, y=num, z=num}
-	static int l_add_entity(lua_State *L)
-	{
-		//infostream<<"EnvRef::l_add_entity()"<<std::endl;
-		EnvRef *o = checkobject(L, 1);
-		ServerEnvironment *env = o->m_env;
-		if(env == NULL) return 0;
-		// pos
-		v3f pos = checkFloatPos(L, 2);
-		// content
-		const char *name = luaL_checkstring(L, 3);
-		// Do it
-		ServerActiveObject *obj = new LuaEntitySAO(env, pos, name, "");
-		int objectid = env->addActiveObject(obj);
-		// If failed to add, return nothing (reads as nil)
-		if(objectid == 0)
-			return 0;
-		// Return ObjectRef
-		objectref_get_or_create(L, obj);
-		return 1;
-	}
-
-	// EnvRef:add_item(pos, itemstack or itemstring or table) -> ObjectRef or nil
-	// pos = {x=num, y=num, z=num}
-	static int l_add_item(lua_State *L)
-	{
-		//infostream<<"EnvRef::l_add_item()"<<std::endl;
-		EnvRef *o = checkobject(L, 1);
-		ServerEnvironment *env = o->m_env;
-		if(env == NULL) return 0;
-		// pos
-		v3f pos = checkFloatPos(L, 2);
-		// item
-		ItemStack item = read_item(L, 3);
-		if(item.empty() || !item.isKnown(get_server(L)->idef()))
-			return 0;
-		// Use minetest.spawn_item to spawn a __builtin:item
-		lua_getglobal(L, "minetest");
-		lua_getfield(L, -1, "spawn_item");
-		if(lua_isnil(L, -1))
-			return 0;
-		lua_pushvalue(L, 2);
-		lua_pushstring(L, item.getItemString().c_str());
-		if(lua_pcall(L, 2, 1, 0))
-			script_error(L, "error: %s", lua_tostring(L, -1));
-		return 1;
-		/*lua_pushvalue(L, 1);
-		lua_pushstring(L, "__builtin:item");
-		lua_pushstring(L, item.getItemString().c_str());
-		return l_add_entity(L);*/
-		/*// Do it
-		ServerActiveObject *obj = createItemSAO(env, pos, item.getItemString());
-		int objectid = env->addActiveObject(obj);
-		// If failed to add, return nothing (reads as nil)
-		if(objectid == 0)
-			return 0;
-		// Return ObjectRef
-		objectref_get_or_create(L, obj);
-		return 1;*/
-	}
-
-	// EnvRef:add_rat(pos)
-	// pos = {x=num, y=num, z=num}
-	static int l_add_rat(lua_State *L)
-	{
-		infostream<<"EnvRef::l_add_rat(): C++ mobs have been removed."
-				<<" Doing nothing."<<std::endl;
-		return 0;
-	}
-
-	// EnvRef:add_firefly(pos)
-	// pos = {x=num, y=num, z=num}
-	static int l_add_firefly(lua_State *L)
-	{
-		infostream<<"EnvRef::l_add_firefly(): C++ mobs have been removed."
-				<<" Doing nothing."<<std::endl;
-		return 0;
-	}
-
-	// EnvRef:get_player_by_name(name)
-	static int l_get_player_by_name(lua_State *L)
-	{
-		EnvRef *o = checkobject(L, 1);
-		ServerEnvironment *env = o->m_env;
-		if(env == NULL) return 0;
-		// Do it
-		const char *name = luaL_checkstring(L, 2);
-		Player *player = env->getPlayer(name);
-		if(player == NULL){
-			lua_pushnil(L);
-			return 1;
-		}
-		PlayerSAO *sao = player->getPlayerSAO();
-		if(sao == NULL){
-			lua_pushnil(L);
-			return 1;
-		}
-		// Put player on stack
-		objectref_get_or_create(L, sao);
-		return 1;
-	}
-
-	// EnvRef:get_objects_inside_radius(pos, radius)
-	static int l_get_objects_inside_radius(lua_State *L)
-	{
-		// Get the table insert function
-		lua_getglobal(L, "table");
-		lua_getfield(L, -1, "insert");
-		int table_insert = lua_gettop(L);
-		// Get environemnt
-		EnvRef *o = checkobject(L, 1);
-		ServerEnvironment *env = o->m_env;
-		if(env == NULL) return 0;
-		// Do it
-		v3f pos = checkFloatPos(L, 2);
-		float radius = luaL_checknumber(L, 3) * BS;
-		std::set<u16> ids = env->getObjectsInsideRadius(pos, radius);
-		lua_newtable(L);
-		int table = lua_gettop(L);
-		for(std::set<u16>::const_iterator
-				i = ids.begin(); i != ids.end(); i++){
-			ServerActiveObject *obj = env->getActiveObject(*i);
-			// Insert object reference into table
-			lua_pushvalue(L, table_insert);
-			lua_pushvalue(L, table);
-			objectref_get_or_create(L, obj);
-			if(lua_pcall(L, 2, 0, 0))
-				script_error(L, "error: %s", lua_tostring(L, -1));
-		}
-		return 1;
-	}
-
-	// EnvRef:set_timeofday(val)
-	// val = 0...1
-	static int l_set_timeofday(lua_State *L)
-	{
-		EnvRef *o = checkobject(L, 1);
-		ServerEnvironment *env = o->m_env;
-		if(env == NULL) return 0;
-		// Do it
-		float timeofday_f = luaL_checknumber(L, 2);
-		assert(timeofday_f >= 0.0 && timeofday_f <= 1.0);
-		int timeofday_mh = (int)(timeofday_f * 24000.0);
-		// This should be set directly in the environment but currently
-		// such changes aren't immediately sent to the clients, so call
-		// the server instead.
-		//env->setTimeOfDay(timeofday_mh);
-		get_server(L)->setTimeOfDay(timeofday_mh);
-		return 0;
-	}
-
-	// EnvRef:get_timeofday() -> 0...1
-	static int l_get_timeofday(lua_State *L)
-	{
-		EnvRef *o = checkobject(L, 1);
-		ServerEnvironment *env = o->m_env;
-		if(env == NULL) return 0;
-		// Do it
-		int timeofday_mh = env->getTimeOfDay();
-		float timeofday_f = (float)timeofday_mh / 24000.0;
-		lua_pushnumber(L, timeofday_f);
-		return 1;
-	}
-
-
-	// EnvRef:find_node_near(pos, radius, nodenames) -> pos or nil
-	// nodenames: eg. {"ignore", "group:tree"} or "default:dirt"
-	static int l_find_node_near(lua_State *L)
-	{
-		EnvRef *o = checkobject(L, 1);
-		ServerEnvironment *env = o->m_env;
-		if(env == NULL) return 0;
-		INodeDefManager *ndef = get_server(L)->ndef();
-		v3s16 pos = read_v3s16(L, 2);
-		int radius = luaL_checkinteger(L, 3);
-		std::set<content_t> filter;
-		if(lua_istable(L, 4)){
-			int table = 4;
-			lua_pushnil(L);
-			while(lua_next(L, table) != 0){
-				// key at index -2 and value at index -1
-				luaL_checktype(L, -1, LUA_TSTRING);
-				ndef->getIds(lua_tostring(L, -1), filter);
-				// removes value, keeps key for next iteration
-				lua_pop(L, 1);
-			}
-		} else if(lua_isstring(L, 4)){
-			ndef->getIds(lua_tostring(L, 4), filter);
-		}
-
-		for(int d=1; d<=radius; d++){
-			core::list<v3s16> list;
-			getFacePositions(list, d);
-			for(core::list<v3s16>::Iterator i = list.begin();
-					i != list.end(); i++){
-				v3s16 p = pos + (*i);
-				content_t c = env->getMap().getNodeNoEx(p).getContent();
-				if(filter.count(c) != 0){
-					push_v3s16(L, p);
-					return 1;
-				}
-			}
-		}
-		return 0;
-	}
-
-	// EnvRef:find_nodes_in_area(minp, maxp, nodenames) -> list of positions
-	// nodenames: eg. {"ignore", "group:tree"} or "default:dirt"
-	static int l_find_nodes_in_area(lua_State *L)
-	{
-		EnvRef *o = checkobject(L, 1);
-		ServerEnvironment *env = o->m_env;
-		if(env == NULL) return 0;
-		INodeDefManager *ndef = get_server(L)->ndef();
-		v3s16 minp = read_v3s16(L, 2);
-		v3s16 maxp = read_v3s16(L, 3);
-		std::set<content_t> filter;
-		if(lua_istable(L, 4)){
-			int table = 4;
-			lua_pushnil(L);
-			while(lua_next(L, table) != 0){
-				// key at index -2 and value at index -1
-				luaL_checktype(L, -1, LUA_TSTRING);
-				ndef->getIds(lua_tostring(L, -1), filter);
-				// removes value, keeps key for next iteration
-				lua_pop(L, 1);
-			}
-		} else if(lua_isstring(L, 4)){
-			ndef->getIds(lua_tostring(L, 4), filter);
-		}
-
-		// Get the table insert function
-		lua_getglobal(L, "table");
-		lua_getfield(L, -1, "insert");
-		int table_insert = lua_gettop(L);
-
-		lua_newtable(L);
-		int table = lua_gettop(L);
-		for(s16 x=minp.X; x<=maxp.X; x++)
-		for(s16 y=minp.Y; y<=maxp.Y; y++)
-		for(s16 z=minp.Z; z<=maxp.Z; z++)
-		{
-			v3s16 p(x,y,z);
-			content_t c = env->getMap().getNodeNoEx(p).getContent();
-			if(filter.count(c) != 0){
-				lua_pushvalue(L, table_insert);
-				lua_pushvalue(L, table);
-				push_v3s16(L, p);
-				if(lua_pcall(L, 2, 0, 0))
-					script_error(L, "error: %s", lua_tostring(L, -1));
-			}
-		}
-		return 1;
-	}
-
-	//	EnvRef:get_perlin(seeddiff, octaves, persistence, scale)
-	//  returns world-specific PerlinNoise
-	static int l_get_perlin(lua_State *L)
-	{
-		EnvRef *o = checkobject(L, 1);
-		ServerEnvironment *env = o->m_env;
-		if(env == NULL) return 0;
-
-		int seeddiff = luaL_checkint(L, 2);
-		int octaves = luaL_checkint(L, 3);
-		float persistence = luaL_checknumber(L, 4);
-		float scale = luaL_checknumber(L, 5);
-
-		LuaPerlinNoise *n = new LuaPerlinNoise(seeddiff + int(env->getServerMap().getSeed()), octaves, persistence, scale);
-		*(void **)(lua_newuserdata(L, sizeof(void *))) = n;
-		luaL_getmetatable(L, "PerlinNoise");
-		lua_setmetatable(L, -2);
-		return 1;
-	}
-    
-	//  EnvRef:get_perlin_map(noiseparams, size)
-	//  returns world-specific PerlinNoiseMap
-	static int l_get_perlin_map(lua_State *L)
-	{
-		EnvRef *o = checkobject(L, 1);
-		ServerEnvironment *env = o->m_env;
-		if (env == NULL)
-			return 0;
-		
-		NoiseParams *np = read_noiseparams(L, 2);
-		if (!np)
-			return 0;
-		v3s16 size = read_v3s16(L, 3);
-		
-		int seed = (int)(env->getServerMap().getSeed());
-		LuaPerlinNoiseMap *n = new LuaPerlinNoiseMap(np, seed, size);
-		*(void **)(lua_newuserdata(L, sizeof(void *))) = n;
-		luaL_getmetatable(L, "PerlinNoiseMap");
-		lua_setmetatable(L, -2);
-		return 1;
-	}
-
-	// EnvRef:clear_objects()
-	// clear all objects in the environment
-	static int l_clear_objects(lua_State *L)
-	{
-		EnvRef *o = checkobject(L, 1);
-		o->m_env->clearAllObjects();
-		return 0;
-	}
-
-	static int l_spawn_tree(lua_State *L)
-	{
-		EnvRef *o = checkobject(L, 1);
-		ServerEnvironment *env = o->m_env;
-		if(env == NULL) return 0;
-		v3s16 p0 = read_v3s16(L, 2);
-
-		treegen::TreeDef tree_def;
-		std::string trunk,leaves,fruit;
-		INodeDefManager *ndef = env->getGameDef()->ndef();
-
-		if(lua_istable(L, 3))
-		{
-			getstringfield(L, 3, "axiom", tree_def.initial_axiom);
-			getstringfield(L, 3, "rules_a", tree_def.rules_a);
-			getstringfield(L, 3, "rules_b", tree_def.rules_b);
-			getstringfield(L, 3, "rules_c", tree_def.rules_c);
-			getstringfield(L, 3, "rules_d", tree_def.rules_d);
-			getstringfield(L, 3, "trunk", trunk);
-			tree_def.trunknode=ndef->getId(trunk);
-			getstringfield(L, 3, "leaves", leaves);
-			tree_def.leavesnode=ndef->getId(leaves);
-			tree_def.leaves2_chance=0;
-			getstringfield(L, 3, "leaves2", leaves);
-			if (leaves !="")
-			{
-				tree_def.leaves2node=ndef->getId(leaves);
-				getintfield(L, 3, "leaves2_chance", tree_def.leaves2_chance);
-			}
-			getintfield(L, 3, "angle", tree_def.angle);
-			getintfield(L, 3, "iterations", tree_def.iterations);
-			getintfield(L, 3, "random_level", tree_def.iterations_random_level);
-			getstringfield(L, 3, "trunk_type", tree_def.trunk_type);
-			getboolfield(L, 3, "thin_branches", tree_def.thin_branches);
-			tree_def.fruit_chance=0;
-			getstringfield(L, 3, "fruit", fruit);
-			if (fruit != "")
-			{
-				tree_def.fruitnode=ndef->getId(fruit);
-				getintfield(L, 3, "fruit_chance",tree_def.fruit_chance);
-			}
-			getintfield(L, 3, "seed", tree_def.seed);
-		}
-		else
-			return 0;
-		treegen::spawn_ltree (env, p0, ndef, tree_def);
-		return 1;
-	}
-
-public:
-	EnvRef(ServerEnvironment *env):
-		m_env(env)
-	{
-		//infostream<<"EnvRef created"<<std::endl;
-	}
-
-	~EnvRef()
-	{
-		//infostream<<"EnvRef destructing"<<std::endl;
-	}
-
-	// Creates an EnvRef and leaves it on top of stack
-	// Not callable from Lua; all references are created on the C side.
-	static void create(lua_State *L, ServerEnvironment *env)
-	{
-		EnvRef *o = new EnvRef(env);
-		//infostream<<"EnvRef::create: o="<<o<<std::endl;
-		*(void **)(lua_newuserdata(L, sizeof(void *))) = o;
-		luaL_getmetatable(L, className);
-		lua_setmetatable(L, -2);
-	}
-
-	static void set_null(lua_State *L)
-	{
-		EnvRef *o = checkobject(L, -1);
-		o->m_env = NULL;
-	}
-
-	static void Register(lua_State *L)
-	{
-		lua_newtable(L);
-		int methodtable = lua_gettop(L);
-		luaL_newmetatable(L, className);
-		int metatable = lua_gettop(L);
-
-		lua_pushliteral(L, "__metatable");
-		lua_pushvalue(L, methodtable);
-		lua_settable(L, metatable);  // hide metatable from Lua getmetatable()
-
-		lua_pushliteral(L, "__index");
-		lua_pushvalue(L, methodtable);
-		lua_settable(L, metatable);
-
-		lua_pushliteral(L, "__gc");
-		lua_pushcfunction(L, gc_object);
-		lua_settable(L, metatable);
-
-		lua_pop(L, 1);  // drop metatable
-
-		luaL_openlib(L, 0, methods, 0);  // fill methodtable
-		lua_pop(L, 1);  // drop methodtable
-
-		// Cannot be created from Lua
-		//lua_register(L, className, create_object);
-	}
-};
-const char EnvRef::className[] = "EnvRef";
-const luaL_reg EnvRef::methods[] = {
-	method(EnvRef, set_node),
-	method(EnvRef, add_node),
-	method(EnvRef, remove_node),
-	method(EnvRef, get_node),
-	method(EnvRef, get_node_or_nil),
-	method(EnvRef, get_node_light),
-	method(EnvRef, place_node),
-	method(EnvRef, dig_node),
-	method(EnvRef, punch_node),
-	method(EnvRef, add_entity),
-	method(EnvRef, add_item),
-	method(EnvRef, add_rat),
-	method(EnvRef, add_firefly),
-	method(EnvRef, get_meta),
-	method(EnvRef, get_node_timer),
-	method(EnvRef, get_player_by_name),
-	method(EnvRef, get_objects_inside_radius),
-	method(EnvRef, set_timeofday),
-	method(EnvRef, get_timeofday),
-	method(EnvRef, find_node_near),
-	method(EnvRef, find_nodes_in_area),
-	method(EnvRef, get_perlin),
-	method(EnvRef, get_perlin_map),
-	method(EnvRef, clear_objects),
-	method(EnvRef, spawn_tree),
-	{0,0}
-};
-
-/*
-	LuaPseudoRandom
-*/
-
-
-class LuaPseudoRandom
-{
-private:
-	PseudoRandom m_pseudo;
-
-	static const char className[];
-	static const luaL_reg methods[];
-
-	// Exported functions
-
-	// garbage collector
-	static int gc_object(lua_State *L)
-	{
-		LuaPseudoRandom *o = *(LuaPseudoRandom **)(lua_touserdata(L, 1));
-		delete o;
-		return 0;
-	}
-
-	// next(self, min=0, max=32767) -> get next value
-	static int l_next(lua_State *L)
-	{
-		LuaPseudoRandom *o = checkobject(L, 1);
-		int min = 0;
-		int max = 32767;
-		lua_settop(L, 3); // Fill 2 and 3 with nil if they don't exist
-		if(!lua_isnil(L, 2))
-			min = luaL_checkinteger(L, 2);
-		if(!lua_isnil(L, 3))
-			max = luaL_checkinteger(L, 3);
-		if(max < min){
-			errorstream<<"PseudoRandom.next(): max="<<max<<" min="<<min<<std::endl;
-			throw LuaError(L, "PseudoRandom.next(): max < min");
-		}
-		if(max - min != 32767 && max - min > 32767/5)
-			throw LuaError(L, "PseudoRandom.next() max-min is not 32767 and is > 32768/5. This is disallowed due to the bad random distribution the implementation would otherwise make.");
-		PseudoRandom &pseudo = o->m_pseudo;
-		int val = pseudo.next();
-		val = (val % (max-min+1)) + min;
-		lua_pushinteger(L, val);
-		return 1;
-	}
-
-public:
-	LuaPseudoRandom(int seed):
-		m_pseudo(seed)
-	{
-	}
-
-	~LuaPseudoRandom()
-	{
-	}
-
-	const PseudoRandom& getItem() const
-	{
-		return m_pseudo;
-	}
-	PseudoRandom& getItem()
-	{
-		return m_pseudo;
-	}
-
-	// LuaPseudoRandom(seed)
-	// Creates an LuaPseudoRandom and leaves it on top of stack
-	static int create_object(lua_State *L)
-	{
-		int seed = luaL_checknumber(L, 1);
-		LuaPseudoRandom *o = new LuaPseudoRandom(seed);
-		*(void **)(lua_newuserdata(L, sizeof(void *))) = o;
-		luaL_getmetatable(L, className);
-		lua_setmetatable(L, -2);
-		return 1;
-	}
-
-	static LuaPseudoRandom* checkobject(lua_State *L, int narg)
-	{
-		luaL_checktype(L, narg, LUA_TUSERDATA);
-		void *ud = luaL_checkudata(L, narg, className);
-		if(!ud) luaL_typerror(L, narg, className);
-		return *(LuaPseudoRandom**)ud;  // unbox pointer
-	}
-
-	static void Register(lua_State *L)
-	{
-		lua_newtable(L);
-		int methodtable = lua_gettop(L);
-		luaL_newmetatable(L, className);
-		int metatable = lua_gettop(L);
-
-		lua_pushliteral(L, "__metatable");
-		lua_pushvalue(L, methodtable);
-		lua_settable(L, metatable);  // hide metatable from Lua getmetatable()
-
-		lua_pushliteral(L, "__index");
-		lua_pushvalue(L, methodtable);
-		lua_settable(L, metatable);
-
-		lua_pushliteral(L, "__gc");
-		lua_pushcfunction(L, gc_object);
-		lua_settable(L, metatable);
-
-		lua_pop(L, 1);  // drop metatable
-
-		luaL_openlib(L, 0, methods, 0);  // fill methodtable
-		lua_pop(L, 1);  // drop methodtable
-
-		// Can be created from Lua (LuaPseudoRandom(seed))
-		lua_register(L, className, create_object);
-	}
-};
-const char LuaPseudoRandom::className[] = "PseudoRandom";
-const luaL_reg LuaPseudoRandom::methods[] = {
-	method(LuaPseudoRandom, next),
-	{0,0}
-};
-
-
-
-/*
-	LuaABM
-*/
-
-class LuaABM : public ActiveBlockModifier
-{
-private:
-	lua_State *m_lua;
-	int m_id;
-
-	std::set<std::string> m_trigger_contents;
-	std::set<std::string> m_required_neighbors;
-	float m_trigger_interval;
-	u32 m_trigger_chance;
-public:
-	LuaABM(lua_State *L, int id,
-			const std::set<std::string> &trigger_contents,
-			const std::set<std::string> &required_neighbors,
-			float trigger_interval, u32 trigger_chance):
-		m_lua(L),
-		m_id(id),
-		m_trigger_contents(trigger_contents),
-		m_required_neighbors(required_neighbors),
-		m_trigger_interval(trigger_interval),
-		m_trigger_chance(trigger_chance)
-	{
-	}
-	virtual std::set<std::string> getTriggerContents()
-	{
-		return m_trigger_contents;
-	}
-	virtual std::set<std::string> getRequiredNeighbors()
-	{
-		return m_required_neighbors;
-	}
-	virtual float getTriggerInterval()
-	{
-		return m_trigger_interval;
-	}
-	virtual u32 getTriggerChance()
-	{
-		return m_trigger_chance;
-	}
-	virtual void trigger(ServerEnvironment *env, v3s16 p, MapNode n,
-			u32 active_object_count, u32 active_object_count_wider)
-	{
-		lua_State *L = m_lua;
-
-		realitycheck(L);
-		assert(lua_checkstack(L, 20));
-		StackUnroller stack_unroller(L);
-
-		// Get minetest.registered_abms
-		lua_getglobal(L, "minetest");
-		lua_getfield(L, -1, "registered_abms");
-		luaL_checktype(L, -1, LUA_TTABLE);
-		int registered_abms = lua_gettop(L);
-
-		// Get minetest.registered_abms[m_id]
-		lua_pushnumber(L, m_id);
-		lua_gettable(L, registered_abms);
-		if(lua_isnil(L, -1))
-			assert(0);
-
-		// Call action
-		luaL_checktype(L, -1, LUA_TTABLE);
-		lua_getfield(L, -1, "action");
-		luaL_checktype(L, -1, LUA_TFUNCTION);
-		push_v3s16(L, p);
-		pushnode(L, n, env->getGameDef()->ndef());
-		lua_pushnumber(L, active_object_count);
-		lua_pushnumber(L, active_object_count_wider);
-		if(lua_pcall(L, 4, 0, 0))
-			script_error(L, "error: %s", lua_tostring(L, -1));
-	}
-};
-
-/*
-	ServerSoundParams
-*/
-
-static void read_server_sound_params(lua_State *L, int index,
-		ServerSoundParams &params)
-{
-	if(index < 0)
-		index = lua_gettop(L) + 1 + index;
-	// Clear
-	params = ServerSoundParams();
-	if(lua_istable(L, index)){
-		getfloatfield(L, index, "gain", params.gain);
-		getstringfield(L, index, "to_player", params.to_player);
-		lua_getfield(L, index, "pos");
-		if(!lua_isnil(L, -1)){
-			v3f p = read_v3f(L, -1)*BS;
-			params.pos = p;
-			params.type = ServerSoundParams::SSP_POSITIONAL;
-		}
-		lua_pop(L, 1);
-		lua_getfield(L, index, "object");
-		if(!lua_isnil(L, -1)){
-			ObjectRef *ref = ObjectRef::checkobject(L, -1);
-			ServerActiveObject *sao = ObjectRef::getobject(ref);
-			if(sao){
-				params.object = sao->getId();
-				params.type = ServerSoundParams::SSP_OBJECT;
-			}
-		}
-		lua_pop(L, 1);
-		params.max_hear_distance = BS*getfloatfield_default(L, index,
-				"max_hear_distance", params.max_hear_distance/BS);
-		getboolfield(L, index, "loop", params.loop);
-	}
-}
-
-/*
-	Global functions
-*/
-
-// debug(text)
-// Writes a line to dstream
-static int l_debug(lua_State *L)
-{
-	std::string text = lua_tostring(L, 1);
-	dstream << text << std::endl;
-	return 0;
-}
-
-// log([level,] text)
-// Writes a line to the logger.
-// The one-argument version logs to infostream.
-// The two-argument version accept a log level: error, action, info, or verbose.
-static int l_log(lua_State *L)
-{
-	std::string text;
-	LogMessageLevel level = LMT_INFO;
-	if(lua_isnone(L, 2))
-	{
-		text = lua_tostring(L, 1);
-	}
-	else
-	{
-		std::string levelname = luaL_checkstring(L, 1);
-		text = luaL_checkstring(L, 2);
-		if(levelname == "error")
-			level = LMT_ERROR;
-		else if(levelname == "action")
-			level = LMT_ACTION;
-		else if(levelname == "verbose")
-			level = LMT_VERBOSE;
-	}
-	log_printline(level, text);
-	return 0;
-}
-
-// request_shutdown()
-static int l_request_shutdown(lua_State *L)
-{
-	get_server(L)->requestShutdown();
-	return 0;
-}
-
-// get_server_status()
-static int l_get_server_status(lua_State *L)
-{
-	lua_pushstring(L, wide_to_narrow(get_server(L)->getStatusString()).c_str());
-	return 1;
-}
-
-// register_biome_groups({frequencies})
-static int l_register_biome_groups(lua_State *L)
-{
-	luaL_checktype(L, 1, LUA_TTABLE);
-	int index = 1;
-	if (!lua_istable(L, index))
-		throw LuaError(L, "register_biome_groups: parameter is not a table");
-
-	BiomeDefManager *bmgr = get_server(L)->getBiomeDef();
-	if (!bmgr) {
-		verbosestream << "register_biome_groups: BiomeDefManager not active" << std::endl;
-		return 0;
-	}
-
-	lua_pushnil(L);
-	for (int i = 1; lua_next(L, index) != 0; i++) {
-		bmgr->addBiomeGroup(lua_tonumber(L, -1));
-		lua_pop(L, 1);
-	}
-	lua_pop(L, 1);
-
-	return 0;
-}
-
-// register_biome({lots of stuff})
-static int l_register_biome(lua_State *L)
-{
-	luaL_checktype(L, 1, LUA_TTABLE);
-	int index = 1, groupid;
-	std::string nodename;
-
-	IWritableNodeDefManager *ndef = get_server(L)->getWritableNodeDefManager();
-	BiomeDefManager *bmgr = get_server(L)->getBiomeDef();
-	if (!bmgr) {
-		verbosestream << "register_biome: BiomeDefManager not active" << std::endl;
-		return 0;
-	}
-
-	groupid = getintfield_default(L, index, "group_id", 0);
-
-	enum BiomeTerrainType terrain = (BiomeTerrainType)getenumfield(L, index,
-					"terrain_type", es_BiomeTerrainType, BIOME_TERRAIN_NORMAL);
-	Biome *b = bmgr->createBiome(terrain);
-
-	b->name = getstringfield_default(L, index, "name", "");
-
-	if (getstringfield(L, index, "node_top", nodename))
-		b->n_top = MapNode(ndef->getId(nodename));
-	else
-		b->n_top = MapNode(CONTENT_IGNORE);
-
-	if (getstringfield(L, index, "node_filler", nodename))
-		b->n_filler = MapNode(ndef->getId(nodename));
-	else
-		b->n_filler = b->n_top;
-
-	b->ntopnodes = getintfield_default(L, index, "num_top_nodes", 0);
-
-	b->height_min   = getintfield_default(L, index, "height_min", 0);
-	b->height_max   = getintfield_default(L, index, "height_max", 0);
-	b->heat_min     = getfloatfield_default(L, index, "heat_min", 0.);
-	b->heat_max     = getfloatfield_default(L, index, "heat_max", 0.);
-	b->humidity_min = getfloatfield_default(L, index, "humidity_min", 0.);
-	b->humidity_max = getfloatfield_default(L, index, "humidity_max", 0.);
-
-	b->np = new NoiseParams; // should read an entire NoiseParams later on...
-	getfloatfield(L, index, "scale", b->np->scale);
-	getfloatfield(L, index, "offset", b->np->offset);
-
-	b->groupid = (s8)groupid;
-	b->flags   = 0; //reserved
-
-	bmgr->addBiome(b);
-
-	verbosestream << "register_biome: " << b->name << std::endl;
-	return 0;
-}
-
-// register_item_raw({lots of stuff})
-static int l_register_item_raw(lua_State *L)
-{
-	luaL_checktype(L, 1, LUA_TTABLE);
-	int table = 1;
-
-	// Get the writable item and node definition managers from the server
-	IWritableItemDefManager *idef =
-			get_server(L)->getWritableItemDefManager();
-	IWritableNodeDefManager *ndef =
-			get_server(L)->getWritableNodeDefManager();
-
-	// Check if name is defined
-	std::string name;
-	lua_getfield(L, table, "name");
-	if(lua_isstring(L, -1)){
-		name = lua_tostring(L, -1);
-		verbosestream<<"register_item_raw: "<<name<<std::endl;
-	} else {
-		throw LuaError(L, "register_item_raw: name is not defined or not a string");
-	}
-
-	// Check if on_use is defined
-
-	ItemDefinition def;
-	// Set a distinctive default value to check if this is set
-	def.node_placement_prediction = "__default";
-
-	// Read the item definition
-	def = read_item_definition(L, table, def);
-
-	// Default to having client-side placement prediction for nodes
-	// ("" in item definition sets it off)
-	if(def.node_placement_prediction == "__default"){
-		if(def.type == ITEM_NODE)
-			def.node_placement_prediction = name;
-		else
-			def.node_placement_prediction = "";
-	}
-
-	// Register item definition
-	idef->registerItem(def);
-
-	// Read the node definition (content features) and register it
-	if(def.type == ITEM_NODE)
-	{
-		ContentFeatures f = read_content_features(L, table);
-		ndef->set(f.name, f);
-	}
-
-	return 0; /* number of results */
-}
-
-// register_alias_raw(name, convert_to_name)
-static int l_register_alias_raw(lua_State *L)
-{
-	std::string name = luaL_checkstring(L, 1);
-	std::string convert_to = luaL_checkstring(L, 2);
-
-	// Get the writable item definition manager from the server
-	IWritableItemDefManager *idef =
-			get_server(L)->getWritableItemDefManager();
-
-	idef->registerAlias(name, convert_to);
-
-	return 0; /* number of results */
-}
-
-// helper for register_craft
-static bool read_craft_recipe_shaped(lua_State *L, int index,
-		int &width, std::vector<std::string> &recipe)
-{
-	if(index < 0)
-		index = lua_gettop(L) + 1 + index;
-
-	if(!lua_istable(L, index))
-		return false;
-
-	lua_pushnil(L);
-	int rowcount = 0;
-	while(lua_next(L, index) != 0){
-		int colcount = 0;
-		// key at index -2 and value at index -1
-		if(!lua_istable(L, -1))
-			return false;
-		int table2 = lua_gettop(L);
-		lua_pushnil(L);
-		while(lua_next(L, table2) != 0){
-			// key at index -2 and value at index -1
-			if(!lua_isstring(L, -1))
-				return false;
-			recipe.push_back(lua_tostring(L, -1));
-			// removes value, keeps key for next iteration
-			lua_pop(L, 1);
-			colcount++;
-		}
-		if(rowcount == 0){
-			width = colcount;
-		} else {
-			if(colcount != width)
-				return false;
-		}
-		// removes value, keeps key for next iteration
-		lua_pop(L, 1);
-		rowcount++;
-	}
-	return width != 0;
-}
-
-// helper for register_craft
-static bool read_craft_recipe_shapeless(lua_State *L, int index,
-		std::vector<std::string> &recipe)
-{
-	if(index < 0)
-		index = lua_gettop(L) + 1 + index;
-
-	if(!lua_istable(L, index))
-		return false;
-
-	lua_pushnil(L);
-	while(lua_next(L, index) != 0){
-		// key at index -2 and value at index -1
-		if(!lua_isstring(L, -1))
-			return false;
-		recipe.push_back(lua_tostring(L, -1));
-		// removes value, keeps key for next iteration
-		lua_pop(L, 1);
-	}
-	return true;
-}
-
-// helper for register_craft
-static bool read_craft_replacements(lua_State *L, int index,
-		CraftReplacements &replacements)
-{
-	if(index < 0)
-		index = lua_gettop(L) + 1 + index;
-
-	if(!lua_istable(L, index))
-		return false;
-
-	lua_pushnil(L);
-	while(lua_next(L, index) != 0){
-		// key at index -2 and value at index -1
-		if(!lua_istable(L, -1))
-			return false;
-		lua_rawgeti(L, -1, 1);
-		if(!lua_isstring(L, -1))
-			return false;
-		std::string replace_from = lua_tostring(L, -1);
-		lua_pop(L, 1);
-		lua_rawgeti(L, -1, 2);
-		if(!lua_isstring(L, -1))
-			return false;
-		std::string replace_to = lua_tostring(L, -1);
-		lua_pop(L, 1);
-		replacements.pairs.push_back(
-				std::make_pair(replace_from, replace_to));
-		// removes value, keeps key for next iteration
-		lua_pop(L, 1);
-	}
-	return true;
-}
-// register_craft({output=item, recipe={{item00,item10},{item01,item11}})
-static int l_register_craft(lua_State *L)
-{
-	//infostream<<"register_craft"<<std::endl;
-	luaL_checktype(L, 1, LUA_TTABLE);
-	int table = 1;
-
-	// Get the writable craft definition manager from the server
-	IWritableCraftDefManager *craftdef =
-			get_server(L)->getWritableCraftDefManager();
-
-	std::string type = getstringfield_default(L, table, "type", "shaped");
-
-	/*
-		CraftDefinitionShaped
-	*/
-	if(type == "shaped"){
-		std::string output = getstringfield_default(L, table, "output", "");
-		if(output == "")
-			throw LuaError(L, "Crafting definition is missing an output");
-
-		int width = 0;
-		std::vector<std::string> recipe;
-		lua_getfield(L, table, "recipe");
-		if(lua_isnil(L, -1))
-			throw LuaError(L, "Crafting definition is missing a recipe"
-					" (output=\"" + output + "\")");
-		if(!read_craft_recipe_shaped(L, -1, width, recipe))
-			throw LuaError(L, "Invalid crafting recipe"
-					" (output=\"" + output + "\")");
-
-		CraftReplacements replacements;
-		lua_getfield(L, table, "replacements");
-		if(!lua_isnil(L, -1))
-		{
-			if(!read_craft_replacements(L, -1, replacements))
-				throw LuaError(L, "Invalid replacements"
-						" (output=\"" + output + "\")");
-		}
-
-		CraftDefinition *def = new CraftDefinitionShaped(
-				output, width, recipe, replacements);
-		craftdef->registerCraft(def);
-	}
-	/*
-		CraftDefinitionShapeless
-	*/
-	else if(type == "shapeless"){
-		std::string output = getstringfield_default(L, table, "output", "");
-		if(output == "")
-			throw LuaError(L, "Crafting definition (shapeless)"
-					" is missing an output");
-
-		std::vector<std::string> recipe;
-		lua_getfield(L, table, "recipe");
-		if(lua_isnil(L, -1))
-			throw LuaError(L, "Crafting definition (shapeless)"
-					" is missing a recipe"
-					" (output=\"" + output + "\")");
-		if(!read_craft_recipe_shapeless(L, -1, recipe))
-			throw LuaError(L, "Invalid crafting recipe"
-					" (output=\"" + output + "\")");
-
-		CraftReplacements replacements;
-		lua_getfield(L, table, "replacements");
-		if(!lua_isnil(L, -1))
-		{
-			if(!read_craft_replacements(L, -1, replacements))
-				throw LuaError(L, "Invalid replacements"
-						" (output=\"" + output + "\")");
-		}
-
-		CraftDefinition *def = new CraftDefinitionShapeless(
-				output, recipe, replacements);
-		craftdef->registerCraft(def);
-	}
-	/*
-		CraftDefinitionToolRepair
-	*/
-	else if(type == "toolrepair"){
-		float additional_wear = getfloatfield_default(L, table,
-				"additional_wear", 0.0);
-
-		CraftDefinition *def = new CraftDefinitionToolRepair(
-				additional_wear);
-		craftdef->registerCraft(def);
-	}
-	/*
-		CraftDefinitionCooking
-	*/
-	else if(type == "cooking"){
-		std::string output = getstringfield_default(L, table, "output", "");
-		if(output == "")
-			throw LuaError(L, "Crafting definition (cooking)"
-					" is missing an output");
-
-		std::string recipe = getstringfield_default(L, table, "recipe", "");
-		if(recipe == "")
-			throw LuaError(L, "Crafting definition (cooking)"
-					" is missing a recipe"
-					" (output=\"" + output + "\")");
-
-		float cooktime = getfloatfield_default(L, table, "cooktime", 3.0);
-
-		CraftReplacements replacements;
-		lua_getfield(L, table, "replacements");
-		if(!lua_isnil(L, -1))
-		{
-			if(!read_craft_replacements(L, -1, replacements))
-				throw LuaError(L, "Invalid replacements"
-						" (cooking output=\"" + output + "\")");
-		}
-
-		CraftDefinition *def = new CraftDefinitionCooking(
-				output, recipe, cooktime, replacements);
-		craftdef->registerCraft(def);
-	}
-	/*
-		CraftDefinitionFuel
-	*/
-	else if(type == "fuel"){
-		std::string recipe = getstringfield_default(L, table, "recipe", "");
-		if(recipe == "")
-			throw LuaError(L, "Crafting definition (fuel)"
-					" is missing a recipe");
-
-		float burntime = getfloatfield_default(L, table, "burntime", 1.0);
-
-		CraftReplacements replacements;
-		lua_getfield(L, table, "replacements");
-		if(!lua_isnil(L, -1))
-		{
-			if(!read_craft_replacements(L, -1, replacements))
-				throw LuaError(L, "Invalid replacements"
-						" (fuel recipe=\"" + recipe + "\")");
-		}
-
-		CraftDefinition *def = new CraftDefinitionFuel(
-				recipe, burntime, replacements);
-		craftdef->registerCraft(def);
-	}
-	else
-	{
-		throw LuaError(L, "Unknown crafting definition type: \"" + type + "\"");
-	}
-
-	lua_pop(L, 1);
-	return 0; /* number of results */
-}
-
-// setting_set(name, value)
-static int l_setting_set(lua_State *L)
-{
-	const char *name = luaL_checkstring(L, 1);
-	const char *value = luaL_checkstring(L, 2);
-	g_settings->set(name, value);
-	return 0;
-}
-
-// setting_get(name)
-static int l_setting_get(lua_State *L)
-{
-	const char *name = luaL_checkstring(L, 1);
-	try{
-		std::string value = g_settings->get(name);
-		lua_pushstring(L, value.c_str());
-	} catch(SettingNotFoundException &e){
-		lua_pushnil(L);
-	}
-	return 1;
-}
-
-// setting_getbool(name)
-static int l_setting_getbool(lua_State *L)
-{
-	const char *name = luaL_checkstring(L, 1);
-	try{
-		bool value = g_settings->getBool(name);
-		lua_pushboolean(L, value);
-	} catch(SettingNotFoundException &e){
-		lua_pushnil(L);
-	}
-	return 1;
-}
-
-// setting_save()
-static int l_setting_save(lua_State *L)
-{
-	get_server(L)->saveConfig();
-	return 0;
-}
-
-// chat_send_all(text)
-static int l_chat_send_all(lua_State *L)
-{
-	const char *text = luaL_checkstring(L, 1);
-	// Get server from registry
-	Server *server = get_server(L);
-	// Send
-	server->notifyPlayers(narrow_to_wide(text));
-	return 0;
-}
-
-// chat_send_player(name, text)
-static int l_chat_send_player(lua_State *L)
-{
-	const char *name = luaL_checkstring(L, 1);
-	const char *text = luaL_checkstring(L, 2);
-	// Get server from registry
-	Server *server = get_server(L);
-	// Send
-	server->notifyPlayer(name, narrow_to_wide(text));
-	return 0;
-}
-
-// get_player_privs(name, text)
-static int l_get_player_privs(lua_State *L)
-{
-	const char *name = luaL_checkstring(L, 1);
-	// Get server from registry
-	Server *server = get_server(L);
-	// Do it
-	lua_newtable(L);
-	int table = lua_gettop(L);
-	std::set<std::string> privs_s = server->getPlayerEffectivePrivs(name);
-	for(std::set<std::string>::const_iterator
-			i = privs_s.begin(); i != privs_s.end(); i++){
-		lua_pushboolean(L, true);
-		lua_setfield(L, table, i->c_str());
-	}
-	lua_pushvalue(L, table);
-	return 1;
-}
-
-// get_ban_list()
-static int l_get_ban_list(lua_State *L)
-{
-	lua_pushstring(L, get_server(L)->getBanDescription("").c_str());
-	return 1;
-}
-
-// get_ban_description()
-static int l_get_ban_description(lua_State *L)
-{
-	const char * ip_or_name = luaL_checkstring(L, 1);
-	lua_pushstring(L, get_server(L)->getBanDescription(std::string(ip_or_name)).c_str());
-	return 1;
-}
-
-// ban_player()
-static int l_ban_player(lua_State *L)
-{
-	const char * name = luaL_checkstring(L, 1);
-	Player *player = get_env(L)->getPlayer(name);
-	if(player == NULL)
-	{
-		lua_pushboolean(L, false); // no such player
-		return 1;
-	}
-	try
-	{
-		Address addr = get_server(L)->getPeerAddress(get_env(L)->getPlayer(name)->peer_id);
-		std::string ip_str = addr.serializeString();
-		get_server(L)->setIpBanned(ip_str, name);
-	}
-	catch(con::PeerNotFoundException) // unlikely
-	{
-		dstream << __FUNCTION_NAME << ": peer was not found" << std::endl;
-		lua_pushboolean(L, false); // error
-		return 1;
-	}
-	lua_pushboolean(L, true);
-	return 1;
-}
-
-// unban_player_or_ip()
-static int l_unban_player_of_ip(lua_State *L)
-{
-	const char * ip_or_name = luaL_checkstring(L, 1);
-	get_server(L)->unsetIpBanned(ip_or_name);
-	lua_pushboolean(L, true);
-	return 1;
-}
-
-// get_inventory(location)
-static int l_get_inventory(lua_State *L)
-{
-	InventoryLocation loc;
-
-	std::string type = checkstringfield(L, 1, "type");
-	if(type == "player"){
-		std::string name = checkstringfield(L, 1, "name");
-		loc.setPlayer(name);
-	} else if(type == "node"){
-		lua_getfield(L, 1, "pos");
-		v3s16 pos = check_v3s16(L, -1);
-		loc.setNodeMeta(pos);
-	} else if(type == "detached"){
-		std::string name = checkstringfield(L, 1, "name");
-		loc.setDetached(name);
-	}
-
-	if(get_server(L)->getInventory(loc) != NULL)
-		InvRef::create(L, loc);
-	else
-		lua_pushnil(L);
-	return 1;
-}
-
-// create_detached_inventory_raw(name)
-static int l_create_detached_inventory_raw(lua_State *L)
-{
-	const char *name = luaL_checkstring(L, 1);
-	if(get_server(L)->createDetachedInventory(name) != NULL){
-		InventoryLocation loc;
-		loc.setDetached(name);
-		InvRef::create(L, loc);
-	}else{
-		lua_pushnil(L);
-	}
-	return 1;
-}
-
-// show_formspec(playername,formname,formspec)
-static int l_show_formspec(lua_State *L)
-{
-	const char *playername = luaL_checkstring(L, 1);
-	const char *formname = luaL_checkstring(L, 2);
-	const char *formspec = luaL_checkstring(L, 3);
-
-	if(get_server(L)->showFormspec(playername,formspec,formname))
-	{
-		lua_pushboolean(L, true);
-	}else{
-		lua_pushboolean(L, false);
-	}
-	return 1;
-}
-
-// get_dig_params(groups, tool_capabilities[, time_from_last_punch])
-static int l_get_dig_params(lua_State *L)
-{
-	std::map<std::string, int> groups;
-	read_groups(L, 1, groups);
-	ToolCapabilities tp = read_tool_capabilities(L, 2);
-	if(lua_isnoneornil(L, 3))
-		push_dig_params(L, getDigParams(groups, &tp));
-	else
-		push_dig_params(L, getDigParams(groups, &tp,
-					luaL_checknumber(L, 3)));
-	return 1;
-}
-
-// get_hit_params(groups, tool_capabilities[, time_from_last_punch])
-static int l_get_hit_params(lua_State *L)
-{
-	std::map<std::string, int> groups;
-	read_groups(L, 1, groups);
-	ToolCapabilities tp = read_tool_capabilities(L, 2);
-	if(lua_isnoneornil(L, 3))
-		push_hit_params(L, getHitParams(groups, &tp));
-	else
-		push_hit_params(L, getHitParams(groups, &tp,
-					luaL_checknumber(L, 3)));
-	return 1;
-}
-
-// get_current_modname()
-static int l_get_current_modname(lua_State *L)
-{
-	lua_getfield(L, LUA_REGISTRYINDEX, "minetest_current_modname");
-	return 1;
-}
-
-// get_modpath(modname)
-static int l_get_modpath(lua_State *L)
-{
-	std::string modname = luaL_checkstring(L, 1);
-	// Do it
-	if(modname == "__builtin"){
-		std::string path = get_server(L)->getBuiltinLuaPath();
-		lua_pushstring(L, path.c_str());
-		return 1;
-	}
-	const ModSpec *mod = get_server(L)->getModSpec(modname);
-	if(!mod){
-		lua_pushnil(L);
-		return 1;
-	}
-	lua_pushstring(L, mod->path.c_str());
-	return 1;
-}
-
-// get_modnames()
-// the returned list is sorted alphabetically for you
-static int l_get_modnames(lua_State *L)
-{
-	// Get a list of mods
-	core::list<std::string> mods_unsorted, mods_sorted;
-	get_server(L)->getModNames(mods_unsorted);
-
-	// Take unsorted items from mods_unsorted and sort them into
-	// mods_sorted; not great performance but the number of mods on a
-	// server will likely be small.
-	for(core::list<std::string>::Iterator i = mods_unsorted.begin();
-	    i != mods_unsorted.end(); i++)
-	{
-		bool added = false;
-		for(core::list<std::string>::Iterator x = mods_sorted.begin();
-		    x != mods_unsorted.end(); x++)
-		{
-			// I doubt anybody using Minetest will be using
-			// anything not ASCII based :)
-			if((*i).compare(*x) <= 0)
-			{
-				mods_sorted.insert_before(x, *i);
-				added = true;
-				break;
-			}
-		}
-		if(!added)
-			mods_sorted.push_back(*i);
-	}
-
-	// Get the table insertion function from Lua.
-	lua_getglobal(L, "table");
-	lua_getfield(L, -1, "insert");
-	int insertion_func = lua_gettop(L);
-
-	// Package them up for Lua
-	lua_newtable(L);
-	int new_table = lua_gettop(L);
-	core::list<std::string>::Iterator i = mods_sorted.begin();
-	while(i != mods_sorted.end())
-	{
-		lua_pushvalue(L, insertion_func);
-		lua_pushvalue(L, new_table);
-		lua_pushstring(L, (*i).c_str());
-		if(lua_pcall(L, 2, 0, 0) != 0)
-		{
-			script_error(L, "error: %s", lua_tostring(L, -1));
-		}
-		i++;
-	}
-	return 1;
-}
-
-// get_worldpath()
-static int l_get_worldpath(lua_State *L)
-{
-	std::string worldpath = get_server(L)->getWorldPath();
-	lua_pushstring(L, worldpath.c_str());
-	return 1;
-}
-
-// sound_play(spec, parameters)
-static int l_sound_play(lua_State *L)
-{
-	SimpleSoundSpec spec;
-	read_soundspec(L, 1, spec);
-	ServerSoundParams params;
-	read_server_sound_params(L, 2, params);
-	s32 handle = get_server(L)->playSound(spec, params);
-	lua_pushinteger(L, handle);
-	return 1;
-}
-
-// sound_stop(handle)
-static int l_sound_stop(lua_State *L)
-{
-	int handle = luaL_checkinteger(L, 1);
-	get_server(L)->stopSound(handle);
-	return 0;
-}
-
-// is_singleplayer()
-static int l_is_singleplayer(lua_State *L)
-{
-	lua_pushboolean(L, get_server(L)->isSingleplayer());
-	return 1;
-}
-
-// get_password_hash(name, raw_password)
-static int l_get_password_hash(lua_State *L)
-{
-	std::string name = luaL_checkstring(L, 1);
-	std::string raw_password = luaL_checkstring(L, 2);
-	std::string hash = translatePassword(name,
-			narrow_to_wide(raw_password));
-	lua_pushstring(L, hash.c_str());
-	return 1;
-}
-
-// notify_authentication_modified(name)
-static int l_notify_authentication_modified(lua_State *L)
-{
-	std::string name = "";
-	if(lua_isstring(L, 1))
-		name = lua_tostring(L, 1);
-	get_server(L)->reportPrivsModified(name);
-	return 0;
-}
-
-// get_craft_result(input)
-static int l_get_craft_result(lua_State *L)
-{
-	int input_i = 1;
-	std::string method_s = getstringfield_default(L, input_i, "method", "normal");
-	enum CraftMethod method = (CraftMethod)getenumfield(L, input_i, "method",
-				es_CraftMethod, CRAFT_METHOD_NORMAL);
-	int width = 1;
-	lua_getfield(L, input_i, "width");
-	if(lua_isnumber(L, -1))
-		width = luaL_checkinteger(L, -1);
-	lua_pop(L, 1);
-	lua_getfield(L, input_i, "items");
-	std::vector<ItemStack> items = read_items(L, -1);
-	lua_pop(L, 1); // items
-
-	IGameDef *gdef = get_server(L);
-	ICraftDefManager *cdef = gdef->cdef();
-	CraftInput input(method, width, items);
-	CraftOutput output;
-	bool got = cdef->getCraftResult(input, output, true, gdef);
-	lua_newtable(L); // output table
-	if(got){
-		ItemStack item;
-		item.deSerialize(output.item, gdef->idef());
-		LuaItemStack::create(L, item);
-		lua_setfield(L, -2, "item");
-		setintfield(L, -1, "time", output.time);
-	} else {
-		LuaItemStack::create(L, ItemStack());
-		lua_setfield(L, -2, "item");
-		setintfield(L, -1, "time", 0);
-	}
-	lua_newtable(L); // decremented input table
-	lua_pushstring(L, method_s.c_str());
-	lua_setfield(L, -2, "method");
-	lua_pushinteger(L, width);
-	lua_setfield(L, -2, "width");
-	push_items(L, input.items);
-	lua_setfield(L, -2, "items");
-	return 2;
-}
-
-// get_craft_recipe(result item)
-static int l_get_craft_recipe(lua_State *L)
-{
-	int k = 0;
-	char tmp[20];
-	int input_i = 1;
-	std::string o_item = luaL_checkstring(L,input_i);
-
-	IGameDef *gdef = get_server(L);
-	ICraftDefManager *cdef = gdef->cdef();
-	CraftInput input;
-	CraftOutput output(o_item,0);
-	bool got = cdef->getCraftRecipe(input, output, gdef);
-	lua_newtable(L); // output table
-	if(got){
-		lua_newtable(L);
-		for(std::vector<ItemStack>::const_iterator
-			i = input.items.begin();
-			i != input.items.end(); i++, k++)
-		{
-			if (i->empty())
-			{
-				continue;
-			}
-			sprintf(tmp,"%d",k);
-			lua_pushstring(L,tmp);
-			lua_pushstring(L,i->name.c_str());
-			lua_settable(L, -3);
-		}
-		lua_setfield(L, -2, "items");
-		setintfield(L, -1, "width", input.width);
-		switch (input.method) {
-		case CRAFT_METHOD_NORMAL:
-			lua_pushstring(L,"normal");
-			break;
-		case CRAFT_METHOD_COOKING:
-			lua_pushstring(L,"cooking");
-			break;
-		case CRAFT_METHOD_FUEL:
-			lua_pushstring(L,"fuel");
-			break;
-		default:
-			lua_pushstring(L,"unknown");
-		}
-		lua_setfield(L, -2, "type");
-	} else {
-		lua_pushnil(L);
-		lua_setfield(L, -2, "items");
-		setintfield(L, -1, "width", 0);
-	}
-	return 1;
-}
-
-// rollback_get_last_node_actor(p, range, seconds) -> actor, p, seconds
-static int l_rollback_get_last_node_actor(lua_State *L)
-{
-	v3s16 p = read_v3s16(L, 1);
-	int range = luaL_checknumber(L, 2);
-	int seconds = luaL_checknumber(L, 3);
-	Server *server = get_server(L);
-	IRollbackManager *rollback = server->getRollbackManager();
-	v3s16 act_p;
-	int act_seconds = 0;
-	std::string actor = rollback->getLastNodeActor(p, range, seconds, &act_p, &act_seconds);
-	lua_pushstring(L, actor.c_str());
-	push_v3s16(L, act_p);
-	lua_pushnumber(L, act_seconds);
-	return 3;
-}
-
-// rollback_revert_actions_by(actor, seconds) -> bool, log messages
-static int l_rollback_revert_actions_by(lua_State *L)
-{
-	std::string actor = luaL_checkstring(L, 1);
-	int seconds = luaL_checknumber(L, 2);
-	Server *server = get_server(L);
-	IRollbackManager *rollback = server->getRollbackManager();
-	std::list<RollbackAction> actions = rollback->getRevertActions(actor, seconds);
-	std::list<std::string> log;
-	bool success = server->rollbackRevertActions(actions, &log);
-	// Push boolean result
-	lua_pushboolean(L, success);
-	// Get the table insert function and push the log table
-	lua_getglobal(L, "table");
-	lua_getfield(L, -1, "insert");
-	int table_insert = lua_gettop(L);
-	lua_newtable(L);
-	int table = lua_gettop(L);
-	for(std::list<std::string>::const_iterator i = log.begin();
-			i != log.end(); i++)
-	{
-		lua_pushvalue(L, table_insert);
-		lua_pushvalue(L, table);
-		lua_pushstring(L, i->c_str());
-		if(lua_pcall(L, 2, 0, 0))
-			script_error(L, "error: %s", lua_tostring(L, -1));
-	}
-	lua_remove(L, -2); // Remove table
-	lua_remove(L, -2); // Remove insert
-	return 2;
-}
-
-static const struct luaL_Reg minetest_f [] = {
-	{"debug", l_debug},
-	{"log", l_log},
-	{"request_shutdown", l_request_shutdown},
-	{"get_server_status", l_get_server_status},
-	{"register_item_raw", l_register_item_raw},
-	{"register_alias_raw", l_register_alias_raw},
-	{"register_craft", l_register_craft},
-	{"register_biome", l_register_biome},
-	{"register_biome_groups", l_register_biome_groups},
-	{"setting_set", l_setting_set},
-	{"setting_get", l_setting_get},
-	{"setting_getbool", l_setting_getbool},
-	{"setting_save",l_setting_save},
-	{"chat_send_all", l_chat_send_all},
-	{"chat_send_player", l_chat_send_player},
-	{"get_player_privs", l_get_player_privs},
-	{"get_ban_list", l_get_ban_list},
-	{"get_ban_description", l_get_ban_description},
-	{"ban_player", l_ban_player},
-	{"unban_player_or_ip", l_unban_player_of_ip},
-	{"get_inventory", l_get_inventory},
-	{"create_detached_inventory_raw", l_create_detached_inventory_raw},
-	{"show_formspec", l_show_formspec},
-	{"get_dig_params", l_get_dig_params},
-	{"get_hit_params", l_get_hit_params},
-	{"get_current_modname", l_get_current_modname},
-	{"get_modpath", l_get_modpath},
-	{"get_modnames", l_get_modnames},
-	{"get_worldpath", l_get_worldpath},
-	{"sound_play", l_sound_play},
-	{"sound_stop", l_sound_stop},
-	{"is_singleplayer", l_is_singleplayer},
-	{"get_password_hash", l_get_password_hash},
-	{"notify_authentication_modified", l_notify_authentication_modified},
-	{"get_craft_result", l_get_craft_result},
-	{"get_craft_recipe", l_get_craft_recipe},
-	{"rollback_get_last_node_actor", l_rollback_get_last_node_actor},
-	{"rollback_revert_actions_by", l_rollback_revert_actions_by},
-	{NULL, NULL}
-};
-
-/*
-	Main export function
-*/
-
-void scriptapi_export(lua_State *L, Server *server)
-{
-	realitycheck(L);
-	assert(lua_checkstack(L, 20));
-	verbosestream<<"scriptapi_export()"<<std::endl;
-	StackUnroller stack_unroller(L);
-
-	// Store server as light userdata in registry
-	lua_pushlightuserdata(L, server);
-	lua_setfield(L, LUA_REGISTRYINDEX, "minetest_server");
-
-	// Register global functions in table minetest
-	lua_newtable(L);
-	luaL_register(L, NULL, minetest_f);
-	lua_setglobal(L, "minetest");
-
-	// Get the main minetest table
-	lua_getglobal(L, "minetest");
-
-	// Add tables to minetest
-	lua_newtable(L);
-	lua_setfield(L, -2, "object_refs");
-	lua_newtable(L);
-	lua_setfield(L, -2, "luaentities");
-
-	// Register wrappers
-	LuaItemStack::Register(L);
-	InvRef::Register(L);
-	NodeMetaRef::Register(L);
-	NodeTimerRef::Register(L);
-	ObjectRef::Register(L);
-	EnvRef::Register(L);
-	LuaPseudoRandom::Register(L);
-	LuaPerlinNoise::Register(L);
-	LuaPerlinNoiseMap::Register(L);
-}
-
-bool scriptapi_loadmod(lua_State *L, const std::string &scriptpath,
-		const std::string &modname)
-{
-	ModNameStorer modnamestorer(L, modname);
-
-	if(!string_allowed(modname, "abcdefghijklmnopqrstuvwxyz"
-			"0123456789_")){
-		errorstream<<"Error loading mod \""<<modname
-				<<"\": modname does not follow naming conventions: "
-				<<"Only chararacters [a-z0-9_] are allowed."<<std::endl;
-		return false;
-	}
-
-	bool success = false;
-
-	try{
-		success = script_load(L, scriptpath.c_str());
-	}
-	catch(LuaError &e){
-		errorstream<<"Error loading mod \""<<modname
-				<<"\": "<<e.what()<<std::endl;
-	}
-
-	return success;
-}
-
-void scriptapi_add_environment(lua_State *L, ServerEnvironment *env)
-{
-	realitycheck(L);
-	assert(lua_checkstack(L, 20));
-	verbosestream<<"scriptapi_add_environment"<<std::endl;
-	StackUnroller stack_unroller(L);
-
-	// Create EnvRef on stack
-	EnvRef::create(L, env);
-	int envref = lua_gettop(L);
-
-	// minetest.env = envref
-	lua_getglobal(L, "minetest");
-	luaL_checktype(L, -1, LUA_TTABLE);
-	lua_pushvalue(L, envref);
-	lua_setfield(L, -2, "env");
-
-	// Store environment as light userdata in registry
-	lua_pushlightuserdata(L, env);
-	lua_setfield(L, LUA_REGISTRYINDEX, "minetest_env");
-
-	/*
-		Add ActiveBlockModifiers to environment
-	*/
-
-	// Get minetest.registered_abms
-	lua_getglobal(L, "minetest");
-	lua_getfield(L, -1, "registered_abms");
-	luaL_checktype(L, -1, LUA_TTABLE);
-	int registered_abms = lua_gettop(L);
-
-	if(lua_istable(L, registered_abms)){
-		int table = lua_gettop(L);
-		lua_pushnil(L);
-		while(lua_next(L, table) != 0){
-			// key at index -2 and value at index -1
-			int id = lua_tonumber(L, -2);
-			int current_abm = lua_gettop(L);
-
-			std::set<std::string> trigger_contents;
-			lua_getfield(L, current_abm, "nodenames");
-			if(lua_istable(L, -1)){
-				int table = lua_gettop(L);
-				lua_pushnil(L);
-				while(lua_next(L, table) != 0){
-					// key at index -2 and value at index -1
-					luaL_checktype(L, -1, LUA_TSTRING);
-					trigger_contents.insert(lua_tostring(L, -1));
-					// removes value, keeps key for next iteration
-					lua_pop(L, 1);
-				}
-			} else if(lua_isstring(L, -1)){
-				trigger_contents.insert(lua_tostring(L, -1));
-			}
-			lua_pop(L, 1);
-
-			std::set<std::string> required_neighbors;
-			lua_getfield(L, current_abm, "neighbors");
-			if(lua_istable(L, -1)){
-				int table = lua_gettop(L);
-				lua_pushnil(L);
-				while(lua_next(L, table) != 0){
-					// key at index -2 and value at index -1
-					luaL_checktype(L, -1, LUA_TSTRING);
-					required_neighbors.insert(lua_tostring(L, -1));
-					// removes value, keeps key for next iteration
-					lua_pop(L, 1);
-				}
-			} else if(lua_isstring(L, -1)){
-				required_neighbors.insert(lua_tostring(L, -1));
-			}
-			lua_pop(L, 1);
-
-			float trigger_interval = 10.0;
-			getfloatfield(L, current_abm, "interval", trigger_interval);
-
-			int trigger_chance = 50;
-			getintfield(L, current_abm, "chance", trigger_chance);
-
-			LuaABM *abm = new LuaABM(L, id, trigger_contents,
-					required_neighbors, trigger_interval, trigger_chance);
-
-			env->addActiveBlockModifier(abm);
-
-			// removes value, keeps key for next iteration
-			lua_pop(L, 1);
-		}
-	}
-	lua_pop(L, 1);
-}
-
-#if 0
-// Dump stack top with the dump2 function
-static void dump2(lua_State *L, const char *name)
-{
-	// Dump object (debug)
-	lua_getglobal(L, "dump2");
-	luaL_checktype(L, -1, LUA_TFUNCTION);
-	lua_pushvalue(L, -2); // Get previous stack top as first parameter
-	lua_pushstring(L, name);
-	if(lua_pcall(L, 2, 0, 0))
-		script_error(L, "error: %s", lua_tostring(L, -1));
-}
-#endif
-
-/*
-	object_reference
-*/
-
-void scriptapi_add_object_reference(lua_State *L, ServerActiveObject *cobj)
-{
-	realitycheck(L);
-	assert(lua_checkstack(L, 20));
-	//infostream<<"scriptapi_add_object_reference: id="<<cobj->getId()<<std::endl;
-	StackUnroller stack_unroller(L);
-
-	// Create object on stack
-	ObjectRef::create(L, cobj); // Puts ObjectRef (as userdata) on stack
-	int object = lua_gettop(L);
-
-	// Get minetest.object_refs table
-	lua_getglobal(L, "minetest");
-	lua_getfield(L, -1, "object_refs");
-	luaL_checktype(L, -1, LUA_TTABLE);
-	int objectstable = lua_gettop(L);
-
-	// object_refs[id] = object
-	lua_pushnumber(L, cobj->getId()); // Push id
-	lua_pushvalue(L, object); // Copy object to top of stack
-	lua_settable(L, objectstable);
-}
-
-void scriptapi_rm_object_reference(lua_State *L, ServerActiveObject *cobj)
-{
-	realitycheck(L);
-	assert(lua_checkstack(L, 20));
-	//infostream<<"scriptapi_rm_object_reference: id="<<cobj->getId()<<std::endl;
-	StackUnroller stack_unroller(L);
-
-	// Get minetest.object_refs table
-	lua_getglobal(L, "minetest");
-	lua_getfield(L, -1, "object_refs");
-	luaL_checktype(L, -1, LUA_TTABLE);
-	int objectstable = lua_gettop(L);
-
-	// Get object_refs[id]
-	lua_pushnumber(L, cobj->getId()); // Push id
-	lua_gettable(L, objectstable);
-	// Set object reference to NULL
-	ObjectRef::set_null(L);
-	lua_pop(L, 1); // pop object
-
-	// Set object_refs[id] = nil
-	lua_pushnumber(L, cobj->getId()); // Push id
-	lua_pushnil(L);
-	lua_settable(L, objectstable);
-}
-
-/*
-	misc
-*/
-
-// What scriptapi_run_callbacks does with the return values of callbacks.
-// Regardless of the mode, if only one callback is defined,
-// its return value is the total return value.
-// Modes only affect the case where 0 or >= 2 callbacks are defined.
-enum RunCallbacksMode
-{
-	// Returns the return value of the first callback
-	// Returns nil if list of callbacks is empty
-	RUN_CALLBACKS_MODE_FIRST,
-	// Returns the return value of the last callback
-	// Returns nil if list of callbacks is empty
-	RUN_CALLBACKS_MODE_LAST,
-	// If any callback returns a false value, the first such is returned
-	// Otherwise, the first callback's return value (trueish) is returned
-	// Returns true if list of callbacks is empty
-	RUN_CALLBACKS_MODE_AND,
-	// Like above, but stops calling callbacks (short circuit)
-	// after seeing the first false value
-	RUN_CALLBACKS_MODE_AND_SC,
-	// If any callback returns a true value, the first such is returned
-	// Otherwise, the first callback's return value (falseish) is returned
-	// Returns false if list of callbacks is empty
-	RUN_CALLBACKS_MODE_OR,
-	// Like above, but stops calling callbacks (short circuit)
-	// after seeing the first true value
-	RUN_CALLBACKS_MODE_OR_SC,
-	// Note: "a true value" and "a false value" refer to values that
-	// are converted by lua_toboolean to true or false, respectively.
-};
-
-// Push the list of callbacks (a lua table).
-// Then push nargs arguments.
-// Then call this function, which
-// - runs the callbacks
-// - removes the table and arguments from the lua stack
-// - pushes the return value, computed depending on mode
-static void scriptapi_run_callbacks(lua_State *L, int nargs,
-		RunCallbacksMode mode)
-{
-	// Insert the return value into the lua stack, below the table
-	assert(lua_gettop(L) >= nargs + 1);
-	lua_pushnil(L);
-	lua_insert(L, -(nargs + 1) - 1);
-	// Stack now looks like this:
-	// ... <return value = nil> <table> <arg#1> <arg#2> ... <arg#n>
-
-	int rv = lua_gettop(L) - nargs - 1;
-	int table = rv + 1;
-	int arg = table + 1;
-
-	luaL_checktype(L, table, LUA_TTABLE);
-
-	// Foreach
-	lua_pushnil(L);
-	bool first_loop = true;
-	while(lua_next(L, table) != 0){
-		// key at index -2 and value at index -1
-		luaL_checktype(L, -1, LUA_TFUNCTION);
-		// Call function
-		for(int i = 0; i < nargs; i++)
-			lua_pushvalue(L, arg+i);
-		if(lua_pcall(L, nargs, 1, 0))
-			script_error(L, "error: %s", lua_tostring(L, -1));
-
-		// Move return value to designated space in stack
-		// Or pop it
-		if(first_loop){
-			// Result of first callback is always moved
-			lua_replace(L, rv);
-			first_loop = false;
-		} else {
-			// Otherwise, what happens depends on the mode
-			if(mode == RUN_CALLBACKS_MODE_FIRST)
-				lua_pop(L, 1);
-			else if(mode == RUN_CALLBACKS_MODE_LAST)
-				lua_replace(L, rv);
-			else if(mode == RUN_CALLBACKS_MODE_AND ||
-					mode == RUN_CALLBACKS_MODE_AND_SC){
-				if((bool)lua_toboolean(L, rv) == true &&
-						(bool)lua_toboolean(L, -1) == false)
-					lua_replace(L, rv);
-				else
-					lua_pop(L, 1);
-			}
-			else if(mode == RUN_CALLBACKS_MODE_OR ||
-					mode == RUN_CALLBACKS_MODE_OR_SC){
-				if((bool)lua_toboolean(L, rv) == false &&
-						(bool)lua_toboolean(L, -1) == true)
-					lua_replace(L, rv);
-				else
-					lua_pop(L, 1);
-			}
-			else
-				assert(0);
-		}
-
-		// Handle short circuit modes
-		if(mode == RUN_CALLBACKS_MODE_AND_SC &&
-				(bool)lua_toboolean(L, rv) == false)
-			break;
-		else if(mode == RUN_CALLBACKS_MODE_OR_SC &&
-				(bool)lua_toboolean(L, rv) == true)
-			break;
-
-		// value removed, keep key for next iteration
-	}
-
-	// Remove stuff from stack, leaving only the return value
-	lua_settop(L, rv);
-
-	// Fix return value in case no callbacks were called
-	if(first_loop){
-		if(mode == RUN_CALLBACKS_MODE_AND ||
-				mode == RUN_CALLBACKS_MODE_AND_SC){
-			lua_pop(L, 1);
-			lua_pushboolean(L, true);
-		}
-		else if(mode == RUN_CALLBACKS_MODE_OR ||
-				mode == RUN_CALLBACKS_MODE_OR_SC){
-			lua_pop(L, 1);
-			lua_pushboolean(L, false);
-		}
-	}
-}
-
-bool scriptapi_on_chat_message(lua_State *L, const std::string &name,
-		const std::string &message)
-{
-	realitycheck(L);
-	assert(lua_checkstack(L, 20));
-	StackUnroller stack_unroller(L);
-
-	// Get minetest.registered_on_chat_messages
-	lua_getglobal(L, "minetest");
-	lua_getfield(L, -1, "registered_on_chat_messages");
-	// Call callbacks
-	lua_pushstring(L, name.c_str());
-	lua_pushstring(L, message.c_str());
-	scriptapi_run_callbacks(L, 2, RUN_CALLBACKS_MODE_OR_SC);
-	bool ate = lua_toboolean(L, -1);
-	return ate;
-}
-
-void scriptapi_on_shutdown(lua_State *L)
-{
-	realitycheck(L);
-	assert(lua_checkstack(L, 20));
-	StackUnroller stack_unroller(L);
-
-	// Get registered shutdown hooks
-	lua_getglobal(L, "minetest");
-	lua_getfield(L, -1, "registered_on_shutdown");
-	// Call callbacks
-	scriptapi_run_callbacks(L, 0, RUN_CALLBACKS_MODE_FIRST);
-}
-
-void scriptapi_on_newplayer(lua_State *L, ServerActiveObject *player)
-{
-	realitycheck(L);
-	assert(lua_checkstack(L, 20));
-	StackUnroller stack_unroller(L);
-
-	// Get minetest.registered_on_newplayers
-	lua_getglobal(L, "minetest");
-	lua_getfield(L, -1, "registered_on_newplayers");
-	// Call callbacks
-	objectref_get_or_create(L, player);
-	scriptapi_run_callbacks(L, 1, RUN_CALLBACKS_MODE_FIRST);
-}
-
-void scriptapi_on_dieplayer(lua_State *L, ServerActiveObject *player)
-{
-	realitycheck(L);
-	assert(lua_checkstack(L, 20));
-	StackUnroller stack_unroller(L);
-
-	// Get minetest.registered_on_dieplayers
-	lua_getglobal(L, "minetest");
-	lua_getfield(L, -1, "registered_on_dieplayers");
-	// Call callbacks
-	objectref_get_or_create(L, player);
-	scriptapi_run_callbacks(L, 1, RUN_CALLBACKS_MODE_FIRST);
-}
-
-bool scriptapi_on_respawnplayer(lua_State *L, ServerActiveObject *player)
-{
-	realitycheck(L);
-	assert(lua_checkstack(L, 20));
-	StackUnroller stack_unroller(L);
-
-	// Get minetest.registered_on_respawnplayers
-	lua_getglobal(L, "minetest");
-	lua_getfield(L, -1, "registered_on_respawnplayers");
-	// Call callbacks
-	objectref_get_or_create(L, player);
-	scriptapi_run_callbacks(L, 1, RUN_CALLBACKS_MODE_OR);
-	bool positioning_handled_by_some = lua_toboolean(L, -1);
-	return positioning_handled_by_some;
-}
-
-void scriptapi_on_joinplayer(lua_State *L, ServerActiveObject *player)
-{
-	realitycheck(L);
-	assert(lua_checkstack(L, 20));
-	StackUnroller stack_unroller(L);
-
-	// Get minetest.registered_on_joinplayers
-	lua_getglobal(L, "minetest");
-	lua_getfield(L, -1, "registered_on_joinplayers");
-	// Call callbacks
-	objectref_get_or_create(L, player);
-	scriptapi_run_callbacks(L, 1, RUN_CALLBACKS_MODE_FIRST);
-}
-
-void scriptapi_on_leaveplayer(lua_State *L, ServerActiveObject *player)
-{
-	realitycheck(L);
-	assert(lua_checkstack(L, 20));
-	StackUnroller stack_unroller(L);
-
-	// Get minetest.registered_on_leaveplayers
-	lua_getglobal(L, "minetest");
-	lua_getfield(L, -1, "registered_on_leaveplayers");
-	// Call callbacks
-	objectref_get_or_create(L, player);
-	scriptapi_run_callbacks(L, 1, RUN_CALLBACKS_MODE_FIRST);
-}
-
-static void get_auth_handler(lua_State *L)
-{
-	lua_getglobal(L, "minetest");
-	lua_getfield(L, -1, "registered_auth_handler");
-	if(lua_isnil(L, -1)){
-		lua_pop(L, 1);
-		lua_getfield(L, -1, "builtin_auth_handler");
-	}
-	if(lua_type(L, -1) != LUA_TTABLE)
-		throw LuaError(L, "Authentication handler table not valid");
-}
-
-bool scriptapi_get_auth(lua_State *L, const std::string &playername,
-		std::string *dst_password, std::set<std::string> *dst_privs)
-{
-	realitycheck(L);
-	assert(lua_checkstack(L, 20));
-	StackUnroller stack_unroller(L);
-
-	get_auth_handler(L);
-	lua_getfield(L, -1, "get_auth");
-	if(lua_type(L, -1) != LUA_TFUNCTION)
-		throw LuaError(L, "Authentication handler missing get_auth");
-	lua_pushstring(L, playername.c_str());
-	if(lua_pcall(L, 1, 1, 0))
-		script_error(L, "error: %s", lua_tostring(L, -1));
-
-	// nil = login not allowed
-	if(lua_isnil(L, -1))
-		return false;
-	luaL_checktype(L, -1, LUA_TTABLE);
-
-	std::string password;
-	bool found = getstringfield(L, -1, "password", password);
-	if(!found)
-		throw LuaError(L, "Authentication handler didn't return password");
-	if(dst_password)
-		*dst_password = password;
-
-	lua_getfield(L, -1, "privileges");
-	if(!lua_istable(L, -1))
-		throw LuaError(L,
-				"Authentication handler didn't return privilege table");
-	if(dst_privs)
-		read_privileges(L, -1, *dst_privs);
-	lua_pop(L, 1);
-
-	return true;
-}
-
-void scriptapi_create_auth(lua_State *L, const std::string &playername,
-		const std::string &password)
-{
-	realitycheck(L);
-	assert(lua_checkstack(L, 20));
-	StackUnroller stack_unroller(L);
-
-	get_auth_handler(L);
-	lua_getfield(L, -1, "create_auth");
-	if(lua_type(L, -1) != LUA_TFUNCTION)
-		throw LuaError(L, "Authentication handler missing create_auth");
-	lua_pushstring(L, playername.c_str());
-	lua_pushstring(L, password.c_str());
-	if(lua_pcall(L, 2, 0, 0))
-		script_error(L, "error: %s", lua_tostring(L, -1));
-}
-
-bool scriptapi_set_password(lua_State *L, const std::string &playername,
-		const std::string &password)
-{
-	realitycheck(L);
-	assert(lua_checkstack(L, 20));
-	StackUnroller stack_unroller(L);
-
-	get_auth_handler(L);
-	lua_getfield(L, -1, "set_password");
-	if(lua_type(L, -1) != LUA_TFUNCTION)
-		throw LuaError(L, "Authentication handler missing set_password");
-	lua_pushstring(L, playername.c_str());
-	lua_pushstring(L, password.c_str());
-	if(lua_pcall(L, 2, 1, 0))
-		script_error(L, "error: %s", lua_tostring(L, -1));
-	return lua_toboolean(L, -1);
-}
-
-/*
-	player
-*/
-
-void scriptapi_on_player_receive_fields(lua_State *L,
-		ServerActiveObject *player,
-		const std::string &formname,
-		const std::map<std::string, std::string> &fields)
+static void read_server_sound_params(lua_State *L, int index,
+		ServerSoundParams &params)
 {
-	realitycheck(L);
-	assert(lua_checkstack(L, 20));
-	StackUnroller stack_unroller(L);
-
-	// Get minetest.registered_on_chat_messages
-	lua_getglobal(L, "minetest");
-	lua_getfield(L, -1, "registered_on_player_receive_fields");
-	// Call callbacks
-	// param 1
-	objectref_get_or_create(L, player);
-	// param 2
-	lua_pushstring(L, formname.c_str());
-	// param 3
-	lua_newtable(L);
-	for(std::map<std::string, std::string>::const_iterator
-			i = fields.begin(); i != fields.end(); i++){
-		const std::string &name = i->first;
-		const std::string &value = i->second;
-		lua_pushstring(L, name.c_str());
-		lua_pushlstring(L, value.c_str(), value.size());
-		lua_settable(L, -3);
+	if(index < 0)
+		index = lua_gettop(L) + 1 + index;
+	// Clear
+	params = ServerSoundParams();
+	if(lua_istable(L, index)){
+		getfloatfield(L, index, "gain", params.gain);
+		getstringfield(L, index, "to_player", params.to_player);
+		lua_getfield(L, index, "pos");
+		if(!lua_isnil(L, -1)){
+			v3f p = read_v3f(L, -1)*BS;
+			params.pos = p;
+			params.type = ServerSoundParams::SSP_POSITIONAL;
+		}
+		lua_pop(L, 1);
+		lua_getfield(L, index, "object");
+		if(!lua_isnil(L, -1)){
+			ObjectRef *ref = ObjectRef::checkobject(L, -1);
+			ServerActiveObject *sao = ObjectRef::getobject(ref);
+			if(sao){
+				params.object = sao->getId();
+				params.type = ServerSoundParams::SSP_OBJECT;
+			}
+		}
+		lua_pop(L, 1);
+		params.max_hear_distance = BS*getfloatfield_default(L, index,
+				"max_hear_distance", params.max_hear_distance/BS);
+		getboolfield(L, index, "loop", params.loop);
 	}
-	scriptapi_run_callbacks(L, 3, RUN_CALLBACKS_MODE_OR_SC);
 }
 
-/*
-	item callbacks and node callbacks
-*/
+/*****************************************************************************/
+/* callbacks                                                                 */
+/*****************************************************************************/
 
-// Retrieves minetest.registered_items[name][callbackname]
-// If that is nil or on error, return false and stack is unchanged
-// If that is a function, returns true and pushes the
-// function onto the stack
-// If minetest.registered_items[name] doesn't exist, minetest.nodedef_default
-// is tried instead so unknown items can still be manipulated to some degree
-static bool get_item_callback(lua_State *L,
-		const char *name, const char *callbackname)
+// Push the list of callbacks (a lua table).
+// Then push nargs arguments.
+// Then call this function, which
+// - runs the callbacks
+// - removes the table and arguments from the lua stack
+// - pushes the return value, computed depending on mode
+void scriptapi_run_callbacks(lua_State *L, int nargs,
+		RunCallbacksMode mode)
 {
-	lua_getglobal(L, "minetest");
-	lua_getfield(L, -1, "registered_items");
-	lua_remove(L, -2);
-	luaL_checktype(L, -1, LUA_TTABLE);
-	lua_getfield(L, -1, name);
-	lua_remove(L, -2);
-	// Should be a table
-	if(lua_type(L, -1) != LUA_TTABLE)
-	{
-		// Report error and clean up
-		errorstream<<"Item \""<<name<<"\" not defined"<<std::endl;
-		lua_pop(L, 1);
+	// Insert the return value into the lua stack, below the table
+	assert(lua_gettop(L) >= nargs + 1);
+	lua_pushnil(L);
+	lua_insert(L, -(nargs + 1) - 1);
+	// Stack now looks like this:
+	// ... <return value = nil> <table> <arg#1> <arg#2> ... <arg#n>
 
-		// Try minetest.nodedef_default instead
-		lua_getglobal(L, "minetest");
-		lua_getfield(L, -1, "nodedef_default");
-		lua_remove(L, -2);
-		luaL_checktype(L, -1, LUA_TTABLE);
-	}
-	lua_getfield(L, -1, callbackname);
-	lua_remove(L, -2);
-	// Should be a function or nil
-	if(lua_type(L, -1) == LUA_TFUNCTION)
-	{
-		return true;
-	}
-	else if(lua_isnil(L, -1))
-	{
-		lua_pop(L, 1);
-		return false;
-	}
-	else
-	{
-		errorstream<<"Item \""<<name<<"\" callback \""
-			<<callbackname<<" is not a function"<<std::endl;
-		lua_pop(L, 1);
-		return false;
-	}
-}
+	int rv = lua_gettop(L) - nargs - 1;
+	int table = rv + 1;
+	int arg = table + 1;
 
-bool scriptapi_item_on_drop(lua_State *L, ItemStack &item,
-		ServerActiveObject *dropper, v3f pos)
-{
-	realitycheck(L);
-	assert(lua_checkstack(L, 20));
-	StackUnroller stack_unroller(L);
+	luaL_checktype(L, table, LUA_TTABLE);
 
-	// Push callback function on stack
-	if(!get_item_callback(L, item.name.c_str(), "on_drop"))
-		return false;
+	// Foreach
+	lua_pushnil(L);
+	bool first_loop = true;
+	while(lua_next(L, table) != 0){
+		// key at index -2 and value at index -1
+		luaL_checktype(L, -1, LUA_TFUNCTION);
+		// Call function
+		for(int i = 0; i < nargs; i++)
+			lua_pushvalue(L, arg+i);
+		if(lua_pcall(L, nargs, 1, 0))
+			script_error(L, "error: %s", lua_tostring(L, -1));
 
-	// Call function
-	LuaItemStack::create(L, item);
-	objectref_get_or_create(L, dropper);
-	pushFloatPos(L, pos);
-	if(lua_pcall(L, 3, 1, 0))
-		script_error(L, "error: %s", lua_tostring(L, -1));
-	if(!lua_isnil(L, -1))
-		item = read_item(L, -1);
-	return true;
-}
+		// Move return value to designated space in stack
+		// Or pop it
+		if(first_loop){
+			// Result of first callback is always moved
+			lua_replace(L, rv);
+			first_loop = false;
+		} else {
+			// Otherwise, what happens depends on the mode
+			if(mode == RUN_CALLBACKS_MODE_FIRST)
+				lua_pop(L, 1);
+			else if(mode == RUN_CALLBACKS_MODE_LAST)
+				lua_replace(L, rv);
+			else if(mode == RUN_CALLBACKS_MODE_AND ||
+					mode == RUN_CALLBACKS_MODE_AND_SC){
+				if((bool)lua_toboolean(L, rv) == true &&
+						(bool)lua_toboolean(L, -1) == false)
+					lua_replace(L, rv);
+				else
+					lua_pop(L, 1);
+			}
+			else if(mode == RUN_CALLBACKS_MODE_OR ||
+					mode == RUN_CALLBACKS_MODE_OR_SC){
+				if((bool)lua_toboolean(L, rv) == false &&
+						(bool)lua_toboolean(L, -1) == true)
+					lua_replace(L, rv);
+				else
+					lua_pop(L, 1);
+			}
+			else
+				assert(0);
+		}
 
-bool scriptapi_item_on_place(lua_State *L, ItemStack &item,
-		ServerActiveObject *placer, const PointedThing &pointed)
-{
-	realitycheck(L);
-	assert(lua_checkstack(L, 20));
-	StackUnroller stack_unroller(L);
+		// Handle short circuit modes
+		if(mode == RUN_CALLBACKS_MODE_AND_SC &&
+				(bool)lua_toboolean(L, rv) == false)
+			break;
+		else if(mode == RUN_CALLBACKS_MODE_OR_SC &&
+				(bool)lua_toboolean(L, rv) == true)
+			break;
 
-	// Push callback function on stack
-	if(!get_item_callback(L, item.name.c_str(), "on_place"))
-		return false;
+		// value removed, keep key for next iteration
+	}
 
-	// Call function
-	LuaItemStack::create(L, item);
-	objectref_get_or_create(L, placer);
-	push_pointed_thing(L, pointed);
-	if(lua_pcall(L, 3, 1, 0))
-		script_error(L, "error: %s", lua_tostring(L, -1));
-	if(!lua_isnil(L, -1))
-		item = read_item(L, -1);
-	return true;
+	// Remove stuff from stack, leaving only the return value
+	lua_settop(L, rv);
+
+	// Fix return value in case no callbacks were called
+	if(first_loop){
+		if(mode == RUN_CALLBACKS_MODE_AND ||
+				mode == RUN_CALLBACKS_MODE_AND_SC){
+			lua_pop(L, 1);
+			lua_pushboolean(L, true);
+		}
+		else if(mode == RUN_CALLBACKS_MODE_OR ||
+				mode == RUN_CALLBACKS_MODE_OR_SC){
+			lua_pop(L, 1);
+			lua_pushboolean(L, false);
+		}
+	}
 }
 
-bool scriptapi_item_on_use(lua_State *L, ItemStack &item,
-		ServerActiveObject *user, const PointedThing &pointed)
+bool scriptapi_on_chat_message(lua_State *L, const std::string &name,
+		const std::string &message)
 {
 	realitycheck(L);
 	assert(lua_checkstack(L, 20));
 	StackUnroller stack_unroller(L);
 
-	// Push callback function on stack
-	if(!get_item_callback(L, item.name.c_str(), "on_use"))
-		return false;
-
-	// Call function
-	LuaItemStack::create(L, item);
-	objectref_get_or_create(L, user);
-	push_pointed_thing(L, pointed);
-	if(lua_pcall(L, 3, 1, 0))
-		script_error(L, "error: %s", lua_tostring(L, -1));
-	if(!lua_isnil(L, -1))
-		item = read_item(L, -1);
-	return true;
+	// Get minetest.registered_on_chat_messages
+	lua_getglobal(L, "minetest");
+	lua_getfield(L, -1, "registered_on_chat_messages");
+	// Call callbacks
+	lua_pushstring(L, name.c_str());
+	lua_pushstring(L, message.c_str());
+	scriptapi_run_callbacks(L, 2, RUN_CALLBACKS_MODE_OR_SC);
+	bool ate = lua_toboolean(L, -1);
+	return ate;
 }
 
-bool scriptapi_node_on_punch(lua_State *L, v3s16 p, MapNode node,
-		ServerActiveObject *puncher)
+void scriptapi_on_shutdown(lua_State *L)
 {
 	realitycheck(L);
 	assert(lua_checkstack(L, 20));
 	StackUnroller stack_unroller(L);
 
-	INodeDefManager *ndef = get_server(L)->ndef();
-
-	// Push callback function on stack
-	if(!get_item_callback(L, ndef->get(node).name.c_str(), "on_punch"))
-		return false;
-
-	// Call function
-	push_v3s16(L, p);
-	pushnode(L, node, ndef);
-	objectref_get_or_create(L, puncher);
-	if(lua_pcall(L, 3, 0, 0))
-		script_error(L, "error: %s", lua_tostring(L, -1));
-	return true;
+	// Get registered shutdown hooks
+	lua_getglobal(L, "minetest");
+	lua_getfield(L, -1, "registered_on_shutdown");
+	// Call callbacks
+	scriptapi_run_callbacks(L, 0, RUN_CALLBACKS_MODE_FIRST);
 }
 
-bool scriptapi_node_on_dig(lua_State *L, v3s16 p, MapNode node,
-		ServerActiveObject *digger)
+void scriptapi_on_newplayer(lua_State *L, ServerActiveObject *player)
 {
 	realitycheck(L);
 	assert(lua_checkstack(L, 20));
 	StackUnroller stack_unroller(L);
 
-	INodeDefManager *ndef = get_server(L)->ndef();
-
-	// Push callback function on stack
-	if(!get_item_callback(L, ndef->get(node).name.c_str(), "on_dig"))
-		return false;
-
-	// Call function
-	push_v3s16(L, p);
-	pushnode(L, node, ndef);
-	objectref_get_or_create(L, digger);
-	if(lua_pcall(L, 3, 0, 0))
-		script_error(L, "error: %s", lua_tostring(L, -1));
-	return true;
+	// Get minetest.registered_on_newplayers
+	lua_getglobal(L, "minetest");
+	lua_getfield(L, -1, "registered_on_newplayers");
+	// Call callbacks
+	objectref_get_or_create(L, player);
+	scriptapi_run_callbacks(L, 1, RUN_CALLBACKS_MODE_FIRST);
 }
 
-void scriptapi_node_on_construct(lua_State *L, v3s16 p, MapNode node)
+void scriptapi_on_dieplayer(lua_State *L, ServerActiveObject *player)
 {
 	realitycheck(L);
 	assert(lua_checkstack(L, 20));
 	StackUnroller stack_unroller(L);
 
-	INodeDefManager *ndef = get_server(L)->ndef();
-
-	// Push callback function on stack
-	if(!get_item_callback(L, ndef->get(node).name.c_str(), "on_construct"))
-		return;
-
-	// Call function
-	push_v3s16(L, p);
-	if(lua_pcall(L, 1, 0, 0))
-		script_error(L, "error: %s", lua_tostring(L, -1));
+	// Get minetest.registered_on_dieplayers
+	lua_getglobal(L, "minetest");
+	lua_getfield(L, -1, "registered_on_dieplayers");
+	// Call callbacks
+	objectref_get_or_create(L, player);
+	scriptapi_run_callbacks(L, 1, RUN_CALLBACKS_MODE_FIRST);
 }
 
-void scriptapi_node_on_destruct(lua_State *L, v3s16 p, MapNode node)
+bool scriptapi_on_respawnplayer(lua_State *L, ServerActiveObject *player)
 {
 	realitycheck(L);
 	assert(lua_checkstack(L, 20));
 	StackUnroller stack_unroller(L);
 
-	INodeDefManager *ndef = get_server(L)->ndef();
-
-	// Push callback function on stack
-	if(!get_item_callback(L, ndef->get(node).name.c_str(), "on_destruct"))
-		return;
-
-	// Call function
-	push_v3s16(L, p);
-	if(lua_pcall(L, 1, 0, 0))
-		script_error(L, "error: %s", lua_tostring(L, -1));
+	// Get minetest.registered_on_respawnplayers
+	lua_getglobal(L, "minetest");
+	lua_getfield(L, -1, "registered_on_respawnplayers");
+	// Call callbacks
+	objectref_get_or_create(L, player);
+	scriptapi_run_callbacks(L, 1, RUN_CALLBACKS_MODE_OR);
+	bool positioning_handled_by_some = lua_toboolean(L, -1);
+	return positioning_handled_by_some;
 }
 
-void scriptapi_node_after_destruct(lua_State *L, v3s16 p, MapNode node)
+void scriptapi_on_joinplayer(lua_State *L, ServerActiveObject *player)
 {
 	realitycheck(L);
 	assert(lua_checkstack(L, 20));
 	StackUnroller stack_unroller(L);
 
-	INodeDefManager *ndef = get_server(L)->ndef();
-
-	// Push callback function on stack
-	if(!get_item_callback(L, ndef->get(node).name.c_str(), "after_destruct"))
-		return;
-
-	// Call function
-	push_v3s16(L, p);
-	pushnode(L, node, ndef);
-	if(lua_pcall(L, 2, 0, 0))
-		script_error(L, "error: %s", lua_tostring(L, -1));
+	// Get minetest.registered_on_joinplayers
+	lua_getglobal(L, "minetest");
+	lua_getfield(L, -1, "registered_on_joinplayers");
+	// Call callbacks
+	objectref_get_or_create(L, player);
+	scriptapi_run_callbacks(L, 1, RUN_CALLBACKS_MODE_FIRST);
 }
 
-bool scriptapi_node_on_timer(lua_State *L, v3s16 p, MapNode node, f32 dtime)
+void scriptapi_on_leaveplayer(lua_State *L, ServerActiveObject *player)
 {
 	realitycheck(L);
 	assert(lua_checkstack(L, 20));
 	StackUnroller stack_unroller(L);
 
-	INodeDefManager *ndef = get_server(L)->ndef();
-
-	// Push callback function on stack
-	if(!get_item_callback(L, ndef->get(node).name.c_str(), "on_timer"))
-		return false;
-
-	// Call function
-	push_v3s16(L, p);
-	lua_pushnumber(L,dtime);
-	if(lua_pcall(L, 2, 1, 0))
-		script_error(L, "error: %s", lua_tostring(L, -1));
-	if((bool)lua_isboolean(L,-1) && (bool)lua_toboolean(L,-1) == true)
-		return true;
-
-	return false;
+	// Get minetest.registered_on_leaveplayers
+	lua_getglobal(L, "minetest");
+	lua_getfield(L, -1, "registered_on_leaveplayers");
+	// Call callbacks
+	objectref_get_or_create(L, player);
+	scriptapi_run_callbacks(L, 1, RUN_CALLBACKS_MODE_FIRST);
 }
 
-void scriptapi_node_on_receive_fields(lua_State *L, v3s16 p,
+/*
+	player
+*/
+void scriptapi_on_player_receive_fields(lua_State *L,
+		ServerActiveObject *player,
 		const std::string &formname,
-		const std::map<std::string, std::string> &fields,
-		ServerActiveObject *sender)
+		const std::map<std::string, std::string> &fields)
 {
 	realitycheck(L);
 	assert(lua_checkstack(L, 20));
 	StackUnroller stack_unroller(L);
 
-	INodeDefManager *ndef = get_server(L)->ndef();
-
-	// If node doesn't exist, we don't know what callback to call
-	MapNode node = get_env(L)->getMap().getNodeNoEx(p);
-	if(node.getContent() == CONTENT_IGNORE)
-		return;
-
-	// Push callback function on stack
-	if(!get_item_callback(L, ndef->get(node).name.c_str(), "on_receive_fields"))
-		return;
-
-	// Call function
+	// Get minetest.registered_on_chat_messages
+	lua_getglobal(L, "minetest");
+	lua_getfield(L, -1, "registered_on_player_receive_fields");
+	// Call callbacks
 	// param 1
-	push_v3s16(L, p);
+	objectref_get_or_create(L, player);
 	// param 2
 	lua_pushstring(L, formname.c_str());
 	// param 3
@@ -6422,784 +560,634 @@ void scriptapi_node_on_receive_fields(lua_State *L, v3s16 p,
 		lua_pushlstring(L, value.c_str(), value.size());
 		lua_settable(L, -3);
 	}
-	// param 4
-	objectref_get_or_create(L, sender);
-	if(lua_pcall(L, 4, 0, 0))
-		script_error(L, "error: %s", lua_tostring(L, -1));
+	scriptapi_run_callbacks(L, 3, RUN_CALLBACKS_MODE_OR_SC);
 }
+/*****************************************************************************/
+/* Api functions                                                             */
+/*****************************************************************************/
 
-/*
-	Node metadata inventory callbacks
-*/
+// debug(text)
+// Writes a line to dstream
+static int l_debug(lua_State *L)
+{
+	std::string text = lua_tostring(L, 1);
+	dstream << text << std::endl;
+	return 0;
+}
 
-// Return number of accepted items to be moved
-int scriptapi_nodemeta_inventory_allow_move(lua_State *L, v3s16 p,
-		const std::string &from_list, int from_index,
-		const std::string &to_list, int to_index,
-		int count, ServerActiveObject *player)
+// log([level,] text)
+// Writes a line to the logger.
+// The one-argument version logs to infostream.
+// The two-argument version accept a log level: error, action, info, or verbose.
+static int l_log(lua_State *L)
 {
-	realitycheck(L);
-	assert(lua_checkstack(L, 20));
-	StackUnroller stack_unroller(L);
+	std::string text;
+	LogMessageLevel level = LMT_INFO;
+	if(lua_isnone(L, 2))
+	{
+		text = lua_tostring(L, 1);
+	}
+	else
+	{
+		std::string levelname = luaL_checkstring(L, 1);
+		text = luaL_checkstring(L, 2);
+		if(levelname == "error")
+			level = LMT_ERROR;
+		else if(levelname == "action")
+			level = LMT_ACTION;
+		else if(levelname == "verbose")
+			level = LMT_VERBOSE;
+	}
+	log_printline(level, text);
+	return 0;
+}
+
+// request_shutdown()
+static int l_request_shutdown(lua_State *L)
+{
+	get_server(L)->requestShutdown();
+	return 0;
+}
+
+// get_server_status()
+static int l_get_server_status(lua_State *L)
+{
+	lua_pushstring(L, wide_to_narrow(get_server(L)->getStatusString()).c_str());
+	return 1;
+}
+
+// register_biome_groups({frequencies})
+static int l_register_biome_groups(lua_State *L)
+{
+	luaL_checktype(L, 1, LUA_TTABLE);
+	int index = 1;
+
+	BiomeDefManager *bmgr = get_server(L)->getBiomeDef();
+	if (!bmgr) {
+		verbosestream << "register_biome_groups: BiomeDefManager not active" << std::endl;
+		return 0;
+	}
+
+	lua_pushnil(L);
+	for (int i = 1; lua_next(L, index) != 0; i++) {
+		bmgr->addBiomeGroup(lua_tonumber(L, -1));
+		lua_pop(L, 1);
+	}
+	lua_pop(L, 1);
+
+	return 0;
+}
 
-	INodeDefManager *ndef = get_server(L)->ndef();
+// register_biome({lots of stuff})
+static int l_register_biome(lua_State *L)
+{
+	luaL_checktype(L, 1, LUA_TTABLE);
+	int index = 1, groupid;
+	std::string nodename;
 
-	// If node doesn't exist, we don't know what callback to call
-	MapNode node = get_env(L)->getMap().getNodeNoEx(p);
-	if(node.getContent() == CONTENT_IGNORE)
+	IWritableNodeDefManager *ndef = get_server(L)->getWritableNodeDefManager();
+	BiomeDefManager *bmgr = get_server(L)->getBiomeDef();
+	if (!bmgr) {
+		verbosestream << "register_biome: BiomeDefManager not active" << std::endl;
 		return 0;
+	}
 
-	// Push callback function on stack
-	if(!get_item_callback(L, ndef->get(node).name.c_str(),
-			"allow_metadata_inventory_move"))
-		return count;
-
-	// function(pos, from_list, from_index, to_list, to_index, count, player)
-	// pos
-	push_v3s16(L, p);
-	// from_list
-	lua_pushstring(L, from_list.c_str());
-	// from_index
-	lua_pushinteger(L, from_index + 1);
-	// to_list
-	lua_pushstring(L, to_list.c_str());
-	// to_index
-	lua_pushinteger(L, to_index + 1);
-	// count
-	lua_pushinteger(L, count);
-	// player
-	objectref_get_or_create(L, player);
-	if(lua_pcall(L, 7, 1, 0))
-		script_error(L, "error: %s", lua_tostring(L, -1));
-	if(!lua_isnumber(L, -1))
-		throw LuaError(L, "allow_metadata_inventory_move should return a number");
-	return luaL_checkinteger(L, -1);
+	groupid = getintfield_default(L, index, "group_id", 0);
+
+	enum BiomeTerrainType terrain = (BiomeTerrainType)getenumfield(L, index,
+					"terrain_type", es_BiomeTerrainType, BIOME_TERRAIN_NORMAL);
+	Biome *b = bmgr->createBiome(terrain);
+
+	b->name = getstringfield_default(L, index, "name", "");
+
+	if (getstringfield(L, index, "node_top", nodename))
+		b->n_top = MapNode(ndef->getId(nodename));
+	else
+		b->n_top = MapNode(CONTENT_IGNORE);
+
+	if (getstringfield(L, index, "node_filler", nodename))
+		b->n_filler = MapNode(ndef->getId(nodename));
+	else
+		b->n_filler = b->n_top;
+
+	b->ntopnodes = getintfield_default(L, index, "num_top_nodes", 0);
+
+	b->height_min   = getintfield_default(L, index, "height_min", 0);
+	b->height_max   = getintfield_default(L, index, "height_max", 0);
+	b->heat_min     = getfloatfield_default(L, index, "heat_min", 0.);
+	b->heat_max     = getfloatfield_default(L, index, "heat_max", 0.);
+	b->humidity_min = getfloatfield_default(L, index, "humidity_min", 0.);
+	b->humidity_max = getfloatfield_default(L, index, "humidity_max", 0.);
+
+	b->np = new NoiseParams; // should read an entire NoiseParams later on...
+	getfloatfield(L, index, "scale", b->np->scale);
+	getfloatfield(L, index, "offset", b->np->offset);
+
+	b->groupid = (s8)groupid;
+	b->flags   = 0; //reserved
+
+	bmgr->addBiome(b);
+
+	verbosestream << "register_biome: " << b->name << std::endl;
+	return 0;
+}
+
+
+static int l_register_ore(lua_State *L)
+{
+	int index = 1;
+	luaL_checktype(L, index, LUA_TTABLE);
+	
+	IWritableNodeDefManager *ndef = get_server(L)->getWritableNodeDefManager();
+	EmergeManager *emerge = get_server(L)->getEmergeManager();
+	
+	enum OreType oretype = (OreType)getenumfield(L, index,
+				"ore_type", es_OreType, ORE_SCATTER);	
+	Ore *ore = createOre(oretype);
+	if (!ore) {
+		errorstream << "register_ore: ore_type "
+			<< oretype << " not implemented";
+		return 0;
+	}
+	
+	ore->ore_name       = getstringfield_default(L, index, "ore", "");
+	ore->wherein_name   = getstringfield_default(L, index, "wherein", "");
+	ore->clust_scarcity = getintfield_default(L, index, "clust_scarcity", 1);
+	ore->clust_num_ores = getintfield_default(L, index, "clust_num_ores", 1);
+	ore->clust_size     = getintfield_default(L, index, "clust_size", 0);
+	ore->height_min     = getintfield_default(L, index, "height_min", 0);
+	ore->height_max     = getintfield_default(L, index, "height_max", 0);
+	ore->nthresh        = getfloatfield_default(L, index, "noise_threshhold", 0.);
+
+	lua_getfield(L, index, "noise_params");
+	ore->np = read_noiseparams(L, -1);
+	lua_pop(L, 1);
+	
+	ore->noise = NULL;
+	
+	if (ore->clust_scarcity <= 0 || ore->clust_num_ores <= 0) {
+		errorstream << "register_ore: clust_scarcity and clust_num_ores"
+			"must be greater than 0";
+		delete ore;
+		return 0;
+	}
+	
+	emerge->ores.push_back(ore);
+	
+	verbosestream << "register_ore: ore '" << ore->ore_name
+		<< "' registered" << std::endl;
+	return 0;
+}
+
+
+// setting_set(name, value)
+static int l_setting_set(lua_State *L)
+{
+	const char *name = luaL_checkstring(L, 1);
+	const char *value = luaL_checkstring(L, 2);
+	g_settings->set(name, value);
+	return 0;
+}
+
+// setting_get(name)
+static int l_setting_get(lua_State *L)
+{
+	const char *name = luaL_checkstring(L, 1);
+	try{
+		std::string value = g_settings->get(name);
+		lua_pushstring(L, value.c_str());
+	} catch(SettingNotFoundException &e){
+		lua_pushnil(L);
+	}
+	return 1;
+}
+
+// setting_getbool(name)
+static int l_setting_getbool(lua_State *L)
+{
+	const char *name = luaL_checkstring(L, 1);
+	try{
+		bool value = g_settings->getBool(name);
+		lua_pushboolean(L, value);
+	} catch(SettingNotFoundException &e){
+		lua_pushnil(L);
+	}
+	return 1;
+}
+
+// setting_save()
+static int l_setting_save(lua_State *L)
+{
+	get_server(L)->saveConfig();
+	return 0;
 }
 
-// Return number of accepted items to be put
-int scriptapi_nodemeta_inventory_allow_put(lua_State *L, v3s16 p,
-		const std::string &listname, int index, ItemStack &stack,
-		ServerActiveObject *player)
+// chat_send_all(text)
+static int l_chat_send_all(lua_State *L)
 {
-	realitycheck(L);
-	assert(lua_checkstack(L, 20));
-	StackUnroller stack_unroller(L);
-
-	INodeDefManager *ndef = get_server(L)->ndef();
-
-	// If node doesn't exist, we don't know what callback to call
-	MapNode node = get_env(L)->getMap().getNodeNoEx(p);
-	if(node.getContent() == CONTENT_IGNORE)
-		return 0;
-
-	// Push callback function on stack
-	if(!get_item_callback(L, ndef->get(node).name.c_str(),
-			"allow_metadata_inventory_put"))
-		return stack.count;
-
-	// Call function(pos, listname, index, stack, player)
-	// pos
-	push_v3s16(L, p);
-	// listname
-	lua_pushstring(L, listname.c_str());
-	// index
-	lua_pushinteger(L, index + 1);
-	// stack
-	LuaItemStack::create(L, stack);
-	// player
-	objectref_get_or_create(L, player);
-	if(lua_pcall(L, 5, 1, 0))
-		script_error(L, "error: %s", lua_tostring(L, -1));
-	if(!lua_isnumber(L, -1))
-		throw LuaError(L, "allow_metadata_inventory_put should return a number");
-	return luaL_checkinteger(L, -1);
+	const char *text = luaL_checkstring(L, 1);
+	// Get server from registry
+	Server *server = get_server(L);
+	// Send
+	server->notifyPlayers(narrow_to_wide(text));
+	return 0;
 }
 
-// Return number of accepted items to be taken
-int scriptapi_nodemeta_inventory_allow_take(lua_State *L, v3s16 p,
-		const std::string &listname, int index, ItemStack &stack,
-		ServerActiveObject *player)
+// chat_send_player(name, text)
+static int l_chat_send_player(lua_State *L)
 {
-	realitycheck(L);
-	assert(lua_checkstack(L, 20));
-	StackUnroller stack_unroller(L);
-
-	INodeDefManager *ndef = get_server(L)->ndef();
-
-	// If node doesn't exist, we don't know what callback to call
-	MapNode node = get_env(L)->getMap().getNodeNoEx(p);
-	if(node.getContent() == CONTENT_IGNORE)
-		return 0;
-
-	// Push callback function on stack
-	if(!get_item_callback(L, ndef->get(node).name.c_str(),
-			"allow_metadata_inventory_take"))
-		return stack.count;
-
-	// Call function(pos, listname, index, count, player)
-	// pos
-	push_v3s16(L, p);
-	// listname
-	lua_pushstring(L, listname.c_str());
-	// index
-	lua_pushinteger(L, index + 1);
-	// stack
-	LuaItemStack::create(L, stack);
-	// player
-	objectref_get_or_create(L, player);
-	if(lua_pcall(L, 5, 1, 0))
-		script_error(L, "error: %s", lua_tostring(L, -1));
-	if(!lua_isnumber(L, -1))
-		throw LuaError(L, "allow_metadata_inventory_take should return a number");
-	return luaL_checkinteger(L, -1);
+	const char *name = luaL_checkstring(L, 1);
+	const char *text = luaL_checkstring(L, 2);
+	// Get server from registry
+	Server *server = get_server(L);
+	// Send
+	server->notifyPlayer(name, narrow_to_wide(text));
+	return 0;
 }
 
-// Report moved items
-void scriptapi_nodemeta_inventory_on_move(lua_State *L, v3s16 p,
-		const std::string &from_list, int from_index,
-		const std::string &to_list, int to_index,
-		int count, ServerActiveObject *player)
+// get_player_privs(name, text)
+static int l_get_player_privs(lua_State *L)
 {
-	realitycheck(L);
-	assert(lua_checkstack(L, 20));
-	StackUnroller stack_unroller(L);
-
-	INodeDefManager *ndef = get_server(L)->ndef();
-
-	// If node doesn't exist, we don't know what callback to call
-	MapNode node = get_env(L)->getMap().getNodeNoEx(p);
-	if(node.getContent() == CONTENT_IGNORE)
-		return;
-
-	// Push callback function on stack
-	if(!get_item_callback(L, ndef->get(node).name.c_str(),
-			"on_metadata_inventory_move"))
-		return;
-
-	// function(pos, from_list, from_index, to_list, to_index, count, player)
-	// pos
-	push_v3s16(L, p);
-	// from_list
-	lua_pushstring(L, from_list.c_str());
-	// from_index
-	lua_pushinteger(L, from_index + 1);
-	// to_list
-	lua_pushstring(L, to_list.c_str());
-	// to_index
-	lua_pushinteger(L, to_index + 1);
-	// count
-	lua_pushinteger(L, count);
-	// player
-	objectref_get_or_create(L, player);
-	if(lua_pcall(L, 7, 0, 0))
-		script_error(L, "error: %s", lua_tostring(L, -1));
+	const char *name = luaL_checkstring(L, 1);
+	// Get server from registry
+	Server *server = get_server(L);
+	// Do it
+	lua_newtable(L);
+	int table = lua_gettop(L);
+	std::set<std::string> privs_s = server->getPlayerEffectivePrivs(name);
+	for(std::set<std::string>::const_iterator
+			i = privs_s.begin(); i != privs_s.end(); i++){
+		lua_pushboolean(L, true);
+		lua_setfield(L, table, i->c_str());
+	}
+	lua_pushvalue(L, table);
+	return 1;
 }
 
-// Report put items
-void scriptapi_nodemeta_inventory_on_put(lua_State *L, v3s16 p,
-		const std::string &listname, int index, ItemStack &stack,
-		ServerActiveObject *player)
+// get_ban_list()
+static int l_get_ban_list(lua_State *L)
 {
-	realitycheck(L);
-	assert(lua_checkstack(L, 20));
-	StackUnroller stack_unroller(L);
-
-	INodeDefManager *ndef = get_server(L)->ndef();
-
-	// If node doesn't exist, we don't know what callback to call
-	MapNode node = get_env(L)->getMap().getNodeNoEx(p);
-	if(node.getContent() == CONTENT_IGNORE)
-		return;
-
-	// Push callback function on stack
-	if(!get_item_callback(L, ndef->get(node).name.c_str(),
-			"on_metadata_inventory_put"))
-		return;
-
-	// Call function(pos, listname, index, stack, player)
-	// pos
-	push_v3s16(L, p);
-	// listname
-	lua_pushstring(L, listname.c_str());
-	// index
-	lua_pushinteger(L, index + 1);
-	// stack
-	LuaItemStack::create(L, stack);
-	// player
-	objectref_get_or_create(L, player);
-	if(lua_pcall(L, 5, 0, 0))
-		script_error(L, "error: %s", lua_tostring(L, -1));
+	lua_pushstring(L, get_server(L)->getBanDescription("").c_str());
+	return 1;
 }
 
-// Report taken items
-void scriptapi_nodemeta_inventory_on_take(lua_State *L, v3s16 p,
-		const std::string &listname, int index, ItemStack &stack,
-		ServerActiveObject *player)
+// get_ban_description()
+static int l_get_ban_description(lua_State *L)
 {
-	realitycheck(L);
-	assert(lua_checkstack(L, 20));
-	StackUnroller stack_unroller(L);
-
-	INodeDefManager *ndef = get_server(L)->ndef();
-
-	// If node doesn't exist, we don't know what callback to call
-	MapNode node = get_env(L)->getMap().getNodeNoEx(p);
-	if(node.getContent() == CONTENT_IGNORE)
-		return;
-
-	// Push callback function on stack
-	if(!get_item_callback(L, ndef->get(node).name.c_str(),
-			"on_metadata_inventory_take"))
-		return;
-
-	// Call function(pos, listname, index, stack, player)
-	// pos
-	push_v3s16(L, p);
-	// listname
-	lua_pushstring(L, listname.c_str());
-	// index
-	lua_pushinteger(L, index + 1);
-	// stack
-	LuaItemStack::create(L, stack);
-	// player
-	objectref_get_or_create(L, player);
-	if(lua_pcall(L, 5, 0, 0))
-		script_error(L, "error: %s", lua_tostring(L, -1));
+	const char * ip_or_name = luaL_checkstring(L, 1);
+	lua_pushstring(L, get_server(L)->getBanDescription(std::string(ip_or_name)).c_str());
+	return 1;
 }
 
-/*
-	Detached inventory callbacks
-*/
-
-// Retrieves minetest.detached_inventories[name][callbackname]
-// If that is nil or on error, return false and stack is unchanged
-// If that is a function, returns true and pushes the
-// function onto the stack
-static bool get_detached_inventory_callback(lua_State *L,
-		const std::string &name, const char *callbackname)
+// ban_player()
+static int l_ban_player(lua_State *L)
 {
-	lua_getglobal(L, "minetest");
-	lua_getfield(L, -1, "detached_inventories");
-	lua_remove(L, -2);
-	luaL_checktype(L, -1, LUA_TTABLE);
-	lua_getfield(L, -1, name.c_str());
-	lua_remove(L, -2);
-	// Should be a table
-	if(lua_type(L, -1) != LUA_TTABLE)
-	{
-		errorstream<<"Item \""<<name<<"\" not defined"<<std::endl;
-		lua_pop(L, 1);
-		return false;
-	}
-	lua_getfield(L, -1, callbackname);
-	lua_remove(L, -2);
-	// Should be a function or nil
-	if(lua_type(L, -1) == LUA_TFUNCTION)
+	const char * name = luaL_checkstring(L, 1);
+	Player *player = get_env(L)->getPlayer(name);
+	if(player == NULL)
 	{
-		return true;
+		lua_pushboolean(L, false); // no such player
+		return 1;
 	}
-	else if(lua_isnil(L, -1))
+	try
 	{
-		lua_pop(L, 1);
-		return false;
+		Address addr = get_server(L)->getPeerAddress(get_env(L)->getPlayer(name)->peer_id);
+		std::string ip_str = addr.serializeString();
+		get_server(L)->setIpBanned(ip_str, name);
 	}
-	else
+	catch(con::PeerNotFoundException) // unlikely
 	{
-		errorstream<<"Detached inventory \""<<name<<"\" callback \""
-			<<callbackname<<"\" is not a function"<<std::endl;
-		lua_pop(L, 1);
-		return false;
+		dstream << __FUNCTION_NAME << ": peer was not found" << std::endl;
+		lua_pushboolean(L, false); // error
+		return 1;
 	}
+	lua_pushboolean(L, true);
+	return 1;
 }
 
-// Return number of accepted items to be moved
-int scriptapi_detached_inventory_allow_move(lua_State *L,
-		const std::string &name,
-		const std::string &from_list, int from_index,
-		const std::string &to_list, int to_index,
-		int count, ServerActiveObject *player)
+// unban_player_or_ip()
+static int l_unban_player_of_ip(lua_State *L)
 {
-	realitycheck(L);
-	assert(lua_checkstack(L, 20));
-	StackUnroller stack_unroller(L);
-
-	// Push callback function on stack
-	if(!get_detached_inventory_callback(L, name, "allow_move"))
-		return count;
-
-	// function(inv, from_list, from_index, to_list, to_index, count, player)
-	// inv
-	InventoryLocation loc;
-	loc.setDetached(name);
-	InvRef::create(L, loc);
-	// from_list
-	lua_pushstring(L, from_list.c_str());
-	// from_index
-	lua_pushinteger(L, from_index + 1);
-	// to_list
-	lua_pushstring(L, to_list.c_str());
-	// to_index
-	lua_pushinteger(L, to_index + 1);
-	// count
-	lua_pushinteger(L, count);
-	// player
-	objectref_get_or_create(L, player);
-	if(lua_pcall(L, 7, 1, 0))
-		script_error(L, "error: %s", lua_tostring(L, -1));
-	if(!lua_isnumber(L, -1))
-		throw LuaError(L, "allow_move should return a number");
-	return luaL_checkinteger(L, -1);
+	const char * ip_or_name = luaL_checkstring(L, 1);
+	get_server(L)->unsetIpBanned(ip_or_name);
+	lua_pushboolean(L, true);
+	return 1;
 }
 
-// Return number of accepted items to be put
-int scriptapi_detached_inventory_allow_put(lua_State *L,
-		const std::string &name,
-		const std::string &listname, int index, ItemStack &stack,
-		ServerActiveObject *player)
+// show_formspec(playername,formname,formspec)
+static int l_show_formspec(lua_State *L)
 {
-	realitycheck(L);
-	assert(lua_checkstack(L, 20));
-	StackUnroller stack_unroller(L);
+	const char *playername = luaL_checkstring(L, 1);
+	const char *formname = luaL_checkstring(L, 2);
+	const char *formspec = luaL_checkstring(L, 3);
 
-	// Push callback function on stack
-	if(!get_detached_inventory_callback(L, name, "allow_put"))
-		return stack.count; // All will be accepted
-
-	// Call function(inv, listname, index, stack, player)
-	// inv
-	InventoryLocation loc;
-	loc.setDetached(name);
-	InvRef::create(L, loc);
-	// listname
-	lua_pushstring(L, listname.c_str());
-	// index
-	lua_pushinteger(L, index + 1);
-	// stack
-	LuaItemStack::create(L, stack);
-	// player
-	objectref_get_or_create(L, player);
-	if(lua_pcall(L, 5, 1, 0))
-		script_error(L, "error: %s", lua_tostring(L, -1));
-	if(!lua_isnumber(L, -1))
-		throw LuaError(L, "allow_put should return a number");
-	return luaL_checkinteger(L, -1);
+	if(get_server(L)->showFormspec(playername,formspec,formname))
+	{
+		lua_pushboolean(L, true);
+	}else{
+		lua_pushboolean(L, false);
+	}
+	return 1;
 }
 
-// Return number of accepted items to be taken
-int scriptapi_detached_inventory_allow_take(lua_State *L,
-		const std::string &name,
-		const std::string &listname, int index, ItemStack &stack,
-		ServerActiveObject *player)
+// get_dig_params(groups, tool_capabilities[, time_from_last_punch])
+static int l_get_dig_params(lua_State *L)
 {
-	realitycheck(L);
-	assert(lua_checkstack(L, 20));
-	StackUnroller stack_unroller(L);
-
-	// Push callback function on stack
-	if(!get_detached_inventory_callback(L, name, "allow_take"))
-		return stack.count; // All will be accepted
-
-	// Call function(inv, listname, index, stack, player)
-	// inv
-	InventoryLocation loc;
-	loc.setDetached(name);
-	InvRef::create(L, loc);
-	// listname
-	lua_pushstring(L, listname.c_str());
-	// index
-	lua_pushinteger(L, index + 1);
-	// stack
-	LuaItemStack::create(L, stack);
-	// player
-	objectref_get_or_create(L, player);
-	if(lua_pcall(L, 5, 1, 0))
-		script_error(L, "error: %s", lua_tostring(L, -1));
-	if(!lua_isnumber(L, -1))
-		throw LuaError(L, "allow_take should return a number");
-	return luaL_checkinteger(L, -1);
+	std::map<std::string, int> groups;
+	read_groups(L, 1, groups);
+	ToolCapabilities tp = read_tool_capabilities(L, 2);
+	if(lua_isnoneornil(L, 3))
+		push_dig_params(L, getDigParams(groups, &tp));
+	else
+		push_dig_params(L, getDigParams(groups, &tp,
+					luaL_checknumber(L, 3)));
+	return 1;
 }
 
-// Report moved items
-void scriptapi_detached_inventory_on_move(lua_State *L,
-		const std::string &name,
-		const std::string &from_list, int from_index,
-		const std::string &to_list, int to_index,
-		int count, ServerActiveObject *player)
+// get_hit_params(groups, tool_capabilities[, time_from_last_punch])
+static int l_get_hit_params(lua_State *L)
 {
-	realitycheck(L);
-	assert(lua_checkstack(L, 20));
-	StackUnroller stack_unroller(L);
+	std::map<std::string, int> groups;
+	read_groups(L, 1, groups);
+	ToolCapabilities tp = read_tool_capabilities(L, 2);
+	if(lua_isnoneornil(L, 3))
+		push_hit_params(L, getHitParams(groups, &tp));
+	else
+		push_hit_params(L, getHitParams(groups, &tp,
+					luaL_checknumber(L, 3)));
+	return 1;
+}
 
-	// Push callback function on stack
-	if(!get_detached_inventory_callback(L, name, "on_move"))
-		return;
+// get_current_modname()
+static int l_get_current_modname(lua_State *L)
+{
+	lua_getfield(L, LUA_REGISTRYINDEX, "minetest_current_modname");
+	return 1;
+}
 
-	// function(inv, from_list, from_index, to_list, to_index, count, player)
-	// inv
-	InventoryLocation loc;
-	loc.setDetached(name);
-	InvRef::create(L, loc);
-	// from_list
-	lua_pushstring(L, from_list.c_str());
-	// from_index
-	lua_pushinteger(L, from_index + 1);
-	// to_list
-	lua_pushstring(L, to_list.c_str());
-	// to_index
-	lua_pushinteger(L, to_index + 1);
-	// count
-	lua_pushinteger(L, count);
-	// player
-	objectref_get_or_create(L, player);
-	if(lua_pcall(L, 7, 0, 0))
-		script_error(L, "error: %s", lua_tostring(L, -1));
+// get_modpath(modname)
+static int l_get_modpath(lua_State *L)
+{
+	std::string modname = luaL_checkstring(L, 1);
+	// Do it
+	if(modname == "__builtin"){
+		std::string path = get_server(L)->getBuiltinLuaPath();
+		lua_pushstring(L, path.c_str());
+		return 1;
+	}
+	const ModSpec *mod = get_server(L)->getModSpec(modname);
+	if(!mod){
+		lua_pushnil(L);
+		return 1;
+	}
+	lua_pushstring(L, mod->path.c_str());
+	return 1;
 }
 
-// Report put items
-void scriptapi_detached_inventory_on_put(lua_State *L,
-		const std::string &name,
-		const std::string &listname, int index, ItemStack &stack,
-		ServerActiveObject *player)
+// get_modnames()
+// the returned list is sorted alphabetically for you
+static int l_get_modnames(lua_State *L)
 {
-	realitycheck(L);
-	assert(lua_checkstack(L, 20));
-	StackUnroller stack_unroller(L);
+	// Get a list of mods
+	std::list<std::string> mods_unsorted, mods_sorted;
+	get_server(L)->getModNames(mods_unsorted);
 
-	// Push callback function on stack
-	if(!get_detached_inventory_callback(L, name, "on_put"))
-		return;
+	// Take unsorted items from mods_unsorted and sort them into
+	// mods_sorted; not great performance but the number of mods on a
+	// server will likely be small.
+	for(std::list<std::string>::iterator i = mods_unsorted.begin();
+	    i != mods_unsorted.end(); ++i)
+	{
+		bool added = false;
+		for(std::list<std::string>::iterator x = mods_sorted.begin();
+		    x != mods_sorted.end(); ++x)
+		{
+			// I doubt anybody using Minetest will be using
+			// anything not ASCII based :)
+			if((*i).compare(*x) <= 0)
+			{
+				mods_sorted.insert(x, *i);
+				added = true;
+				break;
+			}
+		}
+		if(!added)
+			mods_sorted.push_back(*i);
+	}
 
-	// Call function(inv, listname, index, stack, player)
-	// inv
-	InventoryLocation loc;
-	loc.setDetached(name);
-	InvRef::create(L, loc);
-	// listname
-	lua_pushstring(L, listname.c_str());
-	// index
-	lua_pushinteger(L, index + 1);
-	// stack
-	LuaItemStack::create(L, stack);
-	// player
-	objectref_get_or_create(L, player);
-	if(lua_pcall(L, 5, 0, 0))
-		script_error(L, "error: %s", lua_tostring(L, -1));
+	// Get the table insertion function from Lua.
+	lua_getglobal(L, "table");
+	lua_getfield(L, -1, "insert");
+	int insertion_func = lua_gettop(L);
+
+	// Package them up for Lua
+	lua_newtable(L);
+	int new_table = lua_gettop(L);
+	std::list<std::string>::iterator i = mods_sorted.begin();
+	while(i != mods_sorted.end())
+	{
+		lua_pushvalue(L, insertion_func);
+		lua_pushvalue(L, new_table);
+		lua_pushstring(L, (*i).c_str());
+		if(lua_pcall(L, 2, 0, 0) != 0)
+		{
+			script_error(L, "error: %s", lua_tostring(L, -1));
+		}
+		++i;
+	}
+	return 1;
 }
 
-// Report taken items
-void scriptapi_detached_inventory_on_take(lua_State *L,
-		const std::string &name,
-		const std::string &listname, int index, ItemStack &stack,
-		ServerActiveObject *player)
+// get_worldpath()
+static int l_get_worldpath(lua_State *L)
 {
-	realitycheck(L);
-	assert(lua_checkstack(L, 20));
-	StackUnroller stack_unroller(L);
-
-	// Push callback function on stack
-	if(!get_detached_inventory_callback(L, name, "on_take"))
-		return;
-
-	// Call function(inv, listname, index, stack, player)
-	// inv
-	InventoryLocation loc;
-	loc.setDetached(name);
-	InvRef::create(L, loc);
-	// listname
-	lua_pushstring(L, listname.c_str());
-	// index
-	lua_pushinteger(L, index + 1);
-	// stack
-	LuaItemStack::create(L, stack);
-	// player
-	objectref_get_or_create(L, player);
-	if(lua_pcall(L, 5, 0, 0))
-		script_error(L, "error: %s", lua_tostring(L, -1));
+	std::string worldpath = get_server(L)->getWorldPath();
+	lua_pushstring(L, worldpath.c_str());
+	return 1;
 }
 
-/*
-	environment
-*/
-
-void scriptapi_environment_step(lua_State *L, float dtime)
+// sound_play(spec, parameters)
+static int l_sound_play(lua_State *L)
 {
-	realitycheck(L);
-	assert(lua_checkstack(L, 20));
-	//infostream<<"scriptapi_environment_step"<<std::endl;
-	StackUnroller stack_unroller(L);
-
-	// Get minetest.registered_globalsteps
-	lua_getglobal(L, "minetest");
-	lua_getfield(L, -1, "registered_globalsteps");
-	// Call callbacks
-	lua_pushnumber(L, dtime);
-	scriptapi_run_callbacks(L, 1, RUN_CALLBACKS_MODE_FIRST);
+	SimpleSoundSpec spec;
+	read_soundspec(L, 1, spec);
+	ServerSoundParams params;
+	read_server_sound_params(L, 2, params);
+	s32 handle = get_server(L)->playSound(spec, params);
+	lua_pushinteger(L, handle);
+	return 1;
 }
 
-void scriptapi_environment_on_generated(lua_State *L, v3s16 minp, v3s16 maxp,
-		u32 blockseed)
+// sound_stop(handle)
+static int l_sound_stop(lua_State *L)
 {
-	realitycheck(L);
-	assert(lua_checkstack(L, 20));
-	//infostream<<"scriptapi_environment_on_generated"<<std::endl;
-	StackUnroller stack_unroller(L);
-
-	// Get minetest.registered_on_generateds
-	lua_getglobal(L, "minetest");
-	lua_getfield(L, -1, "registered_on_generateds");
-	// Call callbacks
-	push_v3s16(L, minp);
-	push_v3s16(L, maxp);
-	lua_pushnumber(L, blockseed);
-	scriptapi_run_callbacks(L, 3, RUN_CALLBACKS_MODE_FIRST);
+	int handle = luaL_checkinteger(L, 1);
+	get_server(L)->stopSound(handle);
+	return 0;
 }
 
-/*
-	luaentity
-*/
-
-bool scriptapi_luaentity_add(lua_State *L, u16 id, const char *name)
+// is_singleplayer()
+static int l_is_singleplayer(lua_State *L)
 {
-	realitycheck(L);
-	assert(lua_checkstack(L, 20));
-	verbosestream<<"scriptapi_luaentity_add: id="<<id<<" name=\""
-			<<name<<"\""<<std::endl;
-	StackUnroller stack_unroller(L);
-
-	// Get minetest.registered_entities[name]
-	lua_getglobal(L, "minetest");
-	lua_getfield(L, -1, "registered_entities");
-	luaL_checktype(L, -1, LUA_TTABLE);
-	lua_pushstring(L, name);
-	lua_gettable(L, -2);
-	// Should be a table, which we will use as a prototype
-	//luaL_checktype(L, -1, LUA_TTABLE);
-	if(lua_type(L, -1) != LUA_TTABLE){
-		errorstream<<"LuaEntity name \""<<name<<"\" not defined"<<std::endl;
-		return false;
-	}
-	int prototype_table = lua_gettop(L);
-	//dump2(L, "prototype_table");
-
-	// Create entity object
-	lua_newtable(L);
-	int object = lua_gettop(L);
-
-	// Set object metatable
-	lua_pushvalue(L, prototype_table);
-	lua_setmetatable(L, -2);
-
-	// Add object reference
-	// This should be userdata with metatable ObjectRef
-	objectref_get(L, id);
-	luaL_checktype(L, -1, LUA_TUSERDATA);
-	if(!luaL_checkudata(L, -1, "ObjectRef"))
-		luaL_typerror(L, -1, "ObjectRef");
-	lua_setfield(L, -2, "object");
-
-	// minetest.luaentities[id] = object
-	lua_getglobal(L, "minetest");
-	lua_getfield(L, -1, "luaentities");
-	luaL_checktype(L, -1, LUA_TTABLE);
-	lua_pushnumber(L, id); // Push id
-	lua_pushvalue(L, object); // Copy object to top of stack
-	lua_settable(L, -3);
-
-	return true;
+	lua_pushboolean(L, get_server(L)->isSingleplayer());
+	return 1;
 }
 
-void scriptapi_luaentity_activate(lua_State *L, u16 id,
-		const std::string &staticdata, u32 dtime_s)
+// get_password_hash(name, raw_password)
+static int l_get_password_hash(lua_State *L)
 {
-	realitycheck(L);
-	assert(lua_checkstack(L, 20));
-	verbosestream<<"scriptapi_luaentity_activate: id="<<id<<std::endl;
-	StackUnroller stack_unroller(L);
-
-	// Get minetest.luaentities[id]
-	luaentity_get(L, id);
-	int object = lua_gettop(L);
-
-	// Get on_activate function
-	lua_pushvalue(L, object);
-	lua_getfield(L, -1, "on_activate");
-	if(!lua_isnil(L, -1)){
-		luaL_checktype(L, -1, LUA_TFUNCTION);
-		lua_pushvalue(L, object); // self
-		lua_pushlstring(L, staticdata.c_str(), staticdata.size());
-		lua_pushinteger(L, dtime_s);
-		// Call with 3 arguments, 0 results
-		if(lua_pcall(L, 3, 0, 0))
-			script_error(L, "error running function on_activate: %s\n",
-					lua_tostring(L, -1));
-	}
+	std::string name = luaL_checkstring(L, 1);
+	std::string raw_password = luaL_checkstring(L, 2);
+	std::string hash = translatePassword(name,
+			narrow_to_wide(raw_password));
+	lua_pushstring(L, hash.c_str());
+	return 1;
 }
 
-void scriptapi_luaentity_rm(lua_State *L, u16 id)
+// notify_authentication_modified(name)
+static int l_notify_authentication_modified(lua_State *L)
 {
-	realitycheck(L);
-	assert(lua_checkstack(L, 20));
-	verbosestream<<"scriptapi_luaentity_rm: id="<<id<<std::endl;
-
-	// Get minetest.luaentities table
-	lua_getglobal(L, "minetest");
-	lua_getfield(L, -1, "luaentities");
-	luaL_checktype(L, -1, LUA_TTABLE);
-	int objectstable = lua_gettop(L);
-
-	// Set luaentities[id] = nil
-	lua_pushnumber(L, id); // Push id
-	lua_pushnil(L);
-	lua_settable(L, objectstable);
-
-	lua_pop(L, 2); // pop luaentities, minetest
+	std::string name = "";
+	if(lua_isstring(L, 1))
+		name = lua_tostring(L, 1);
+	get_server(L)->reportPrivsModified(name);
+	return 0;
 }
 
-std::string scriptapi_luaentity_get_staticdata(lua_State *L, u16 id)
+// rollback_get_last_node_actor(p, range, seconds) -> actor, p, seconds
+static int l_rollback_get_last_node_actor(lua_State *L)
 {
-	realitycheck(L);
-	assert(lua_checkstack(L, 20));
-	//infostream<<"scriptapi_luaentity_get_staticdata: id="<<id<<std::endl;
-	StackUnroller stack_unroller(L);
-
-	// Get minetest.luaentities[id]
-	luaentity_get(L, id);
-	int object = lua_gettop(L);
-
-	// Get get_staticdata function
-	lua_pushvalue(L, object);
-	lua_getfield(L, -1, "get_staticdata");
-	if(lua_isnil(L, -1))
-		return "";
-
-	luaL_checktype(L, -1, LUA_TFUNCTION);
-	lua_pushvalue(L, object); // self
-	// Call with 1 arguments, 1 results
-	if(lua_pcall(L, 1, 1, 0))
-		script_error(L, "error running function get_staticdata: %s\n",
-				lua_tostring(L, -1));
-
-	size_t len=0;
-	const char *s = lua_tolstring(L, -1, &len);
-	return std::string(s, len);
+	v3s16 p = read_v3s16(L, 1);
+	int range = luaL_checknumber(L, 2);
+	int seconds = luaL_checknumber(L, 3);
+	Server *server = get_server(L);
+	IRollbackManager *rollback = server->getRollbackManager();
+	v3s16 act_p;
+	int act_seconds = 0;
+	std::string actor = rollback->getLastNodeActor(p, range, seconds, &act_p, &act_seconds);
+	lua_pushstring(L, actor.c_str());
+	push_v3s16(L, act_p);
+	lua_pushnumber(L, act_seconds);
+	return 3;
 }
 
-void scriptapi_luaentity_get_properties(lua_State *L, u16 id,
-		ObjectProperties *prop)
+// rollback_revert_actions_by(actor, seconds) -> bool, log messages
+static int l_rollback_revert_actions_by(lua_State *L)
 {
-	realitycheck(L);
-	assert(lua_checkstack(L, 20));
-	//infostream<<"scriptapi_luaentity_get_properties: id="<<id<<std::endl;
-	StackUnroller stack_unroller(L);
-
-	// Get minetest.luaentities[id]
-	luaentity_get(L, id);
-	//int object = lua_gettop(L);
-
-	// Set default values that differ from ObjectProperties defaults
-	prop->hp_max = 10;
-
-	/* Read stuff */
-
-	prop->hp_max = getintfield_default(L, -1, "hp_max", 10);
-
-	getboolfield(L, -1, "physical", prop->physical);
-
-	getfloatfield(L, -1, "weight", prop->weight);
-
-	lua_getfield(L, -1, "collisionbox");
-	if(lua_istable(L, -1))
-		prop->collisionbox = read_aabb3f(L, -1, 1.0);
-	lua_pop(L, 1);
-
-	getstringfield(L, -1, "visual", prop->visual);
+	std::string actor = luaL_checkstring(L, 1);
+	int seconds = luaL_checknumber(L, 2);
+	Server *server = get_server(L);
+	IRollbackManager *rollback = server->getRollbackManager();
+	std::list<RollbackAction> actions = rollback->getRevertActions(actor, seconds);
+	std::list<std::string> log;
+	bool success = server->rollbackRevertActions(actions, &log);
+	// Push boolean result
+	lua_pushboolean(L, success);
+	// Get the table insert function and push the log table
+	lua_getglobal(L, "table");
+	lua_getfield(L, -1, "insert");
+	int table_insert = lua_gettop(L);
+	lua_newtable(L);
+	int table = lua_gettop(L);
+	for(std::list<std::string>::const_iterator i = log.begin();
+			i != log.end(); i++)
+	{
+		lua_pushvalue(L, table_insert);
+		lua_pushvalue(L, table);
+		lua_pushstring(L, i->c_str());
+		if(lua_pcall(L, 2, 0, 0))
+			script_error(L, "error: %s", lua_tostring(L, -1));
+	}
+	lua_remove(L, -2); // Remove table
+	lua_remove(L, -2); // Remove insert
+	return 2;
+}
 
-	getstringfield(L, -1, "mesh", prop->mesh);
+static const struct luaL_Reg minetest_f [] = {
+	{"debug", l_debug},
+	{"log", l_log},
+	{"request_shutdown", l_request_shutdown},
+	{"get_server_status", l_get_server_status},
+	{"register_item_raw", l_register_item_raw},
+	{"register_alias_raw", l_register_alias_raw},
+	{"register_craft", l_register_craft},
+	{"register_biome", l_register_biome},
+	{"register_biome_groups", l_register_biome_groups},
+	{"register_ore", l_register_ore},
+	{"setting_set", l_setting_set},
+	{"setting_get", l_setting_get},
+	{"setting_getbool", l_setting_getbool},
+	{"setting_save",l_setting_save},
+	{"chat_send_all", l_chat_send_all},
+	{"chat_send_player", l_chat_send_player},
+	{"get_player_privs", l_get_player_privs},
+	{"get_ban_list", l_get_ban_list},
+	{"get_ban_description", l_get_ban_description},
+	{"ban_player", l_ban_player},
+	{"unban_player_or_ip", l_unban_player_of_ip},
+	{"get_inventory", l_get_inventory},
+	{"create_detached_inventory_raw", l_create_detached_inventory_raw},
+	{"show_formspec", l_show_formspec},
+	{"get_dig_params", l_get_dig_params},
+	{"get_hit_params", l_get_hit_params},
+	{"get_current_modname", l_get_current_modname},
+	{"get_modpath", l_get_modpath},
+	{"get_modnames", l_get_modnames},
+	{"get_worldpath", l_get_worldpath},
+	{"sound_play", l_sound_play},
+	{"sound_stop", l_sound_stop},
+	{"is_singleplayer", l_is_singleplayer},
+	{"get_password_hash", l_get_password_hash},
+	{"notify_authentication_modified", l_notify_authentication_modified},
+	{"get_craft_result", l_get_craft_result},
+	{"get_craft_recipe", l_get_craft_recipe},
+	{"get_all_craft_recipes", l_get_all_craft_recipes},
+	{"rollback_get_last_node_actor", l_rollback_get_last_node_actor},
+	{"rollback_revert_actions_by", l_rollback_revert_actions_by},
+	{"add_particle", l_add_particle},
+	{"add_particlespawner", l_add_particlespawner},
+	{"delete_particlespawner", l_delete_particlespawner},
+	{NULL, NULL}
+};
 
-	// Deprecated: read object properties directly
-	read_object_properties(L, -1, prop);
 
-	// Read initial_properties
-	lua_getfield(L, -1, "initial_properties");
-	read_object_properties(L, -1, prop);
-	lua_pop(L, 1);
-}
+/*
+	Main export function
+*/
 
-void scriptapi_luaentity_step(lua_State *L, u16 id, float dtime)
+void scriptapi_export(lua_State *L, Server *server)
 {
 	realitycheck(L);
 	assert(lua_checkstack(L, 20));
-	//infostream<<"scriptapi_luaentity_step: id="<<id<<std::endl;
+	verbosestream<<"scriptapi_export()"<<std::endl;
 	StackUnroller stack_unroller(L);
 
-	// Get minetest.luaentities[id]
-	luaentity_get(L, id);
-	int object = lua_gettop(L);
-	// State: object is at top of stack
-	// Get step function
-	lua_getfield(L, -1, "on_step");
-	if(lua_isnil(L, -1))
-		return;
-	luaL_checktype(L, -1, LUA_TFUNCTION);
-	lua_pushvalue(L, object); // self
-	lua_pushnumber(L, dtime); // dtime
-	// Call with 2 arguments, 0 results
-	if(lua_pcall(L, 2, 0, 0))
-		script_error(L, "error running function 'on_step': %s\n", lua_tostring(L, -1));
-}
+	// Store server as light userdata in registry
+	lua_pushlightuserdata(L, server);
+	lua_setfield(L, LUA_REGISTRYINDEX, "minetest_server");
 
-// Calls entity:on_punch(ObjectRef puncher, time_from_last_punch,
-//                       tool_capabilities, direction)
-void scriptapi_luaentity_punch(lua_State *L, u16 id,
-		ServerActiveObject *puncher, float time_from_last_punch,
-		const ToolCapabilities *toolcap, v3f dir)
-{
-	realitycheck(L);
-	assert(lua_checkstack(L, 20));
-	//infostream<<"scriptapi_luaentity_step: id="<<id<<std::endl;
-	StackUnroller stack_unroller(L);
+	// Register global functions in table minetest
+	lua_newtable(L);
+	luaL_register(L, NULL, minetest_f);
+	lua_setglobal(L, "minetest");
 
-	// Get minetest.luaentities[id]
-	luaentity_get(L, id);
-	int object = lua_gettop(L);
-	// State: object is at top of stack
-	// Get function
-	lua_getfield(L, -1, "on_punch");
-	if(lua_isnil(L, -1))
-		return;
-	luaL_checktype(L, -1, LUA_TFUNCTION);
-	lua_pushvalue(L, object); // self
-	objectref_get_or_create(L, puncher); // Clicker reference
-	lua_pushnumber(L, time_from_last_punch);
-	push_tool_capabilities(L, *toolcap);
-	push_v3f(L, dir);
-	// Call with 5 arguments, 0 results
-	if(lua_pcall(L, 5, 0, 0))
-		script_error(L, "error running function 'on_punch': %s\n", lua_tostring(L, -1));
-}
+	// Get the main minetest table
+	lua_getglobal(L, "minetest");
 
-// Calls entity:on_rightclick(ObjectRef clicker)
-void scriptapi_luaentity_rightclick(lua_State *L, u16 id,
-		ServerActiveObject *clicker)
-{
-	realitycheck(L);
-	assert(lua_checkstack(L, 20));
-	//infostream<<"scriptapi_luaentity_step: id="<<id<<std::endl;
-	StackUnroller stack_unroller(L);
+	// Add tables to minetest
+	lua_newtable(L);
+	lua_setfield(L, -2, "object_refs");
+	lua_newtable(L);
+	lua_setfield(L, -2, "luaentities");
 
-	// Get minetest.luaentities[id]
-	luaentity_get(L, id);
-	int object = lua_gettop(L);
-	// State: object is at top of stack
-	// Get function
-	lua_getfield(L, -1, "on_rightclick");
-	if(lua_isnil(L, -1))
-		return;
-	luaL_checktype(L, -1, LUA_TFUNCTION);
-	lua_pushvalue(L, object); // self
-	objectref_get_or_create(L, clicker); // Clicker reference
-	// Call with 2 arguments, 0 results
-	if(lua_pcall(L, 2, 0, 0))
-		script_error(L, "error running function 'on_rightclick': %s\n", lua_tostring(L, -1));
+	// Register wrappers
+	LuaItemStack::Register(L);
+	InvRef::Register(L);
+	NodeMetaRef::Register(L);
+	NodeTimerRef::Register(L);
+	ObjectRef::Register(L);
+	EnvRef::Register(L);
+	LuaPseudoRandom::Register(L);
+	LuaPerlinNoise::Register(L);
+	LuaPerlinNoiseMap::Register(L);
 }
-
diff --git a/src/scriptapi.h b/src/scriptapi.h
index e94ca781fce68e8730865049718fc5b0f2919c4b..7f19bcef5c958ae0c09eb7ed1ea8079f39e49599 100644
--- a/src/scriptapi.h
+++ b/src/scriptapi.h
@@ -20,41 +20,35 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #ifndef SCRIPTAPI_HEADER
 #define SCRIPTAPI_HEADER
 
-#include "irrlichttypes_bloated.h"
 #include <string>
-#include "mapnode.h"
 #include <set>
 #include <map>
+#include "irr_v3d.h"
+#include "irr_v2d.h"
+
+extern "C" {
+#include <lua.h>
+}
+#include "scriptapi_inventory.h"
+#include "scriptapi_nodemeta.h"
+#include "scriptapi_entity.h"
+#include "scriptapi_object.h"
+#include "scriptapi_env.h"
+#include "scriptapi_item.h"
+#include "scriptapi_node.h"
+
+#define luamethod(class, name) {#name, class::l_##name}
 
 class Server;
-class ServerEnvironment;
-class ServerActiveObject;
-typedef struct lua_State lua_State;
-struct ObjectProperties;
-struct ItemStack;
-struct PointedThing;
-//class IGameDef;
-struct ToolCapabilities;
 
 void scriptapi_export(lua_State *L, Server *server);
 bool scriptapi_loadmod(lua_State *L, const std::string &scriptpath,
 		const std::string &modname);
-void scriptapi_add_environment(lua_State *L, ServerEnvironment *env);
-
-void scriptapi_add_object_reference(lua_State *L, ServerActiveObject *cobj);
-void scriptapi_rm_object_reference(lua_State *L, ServerActiveObject *cobj);
 
 // Returns true if script handled message
 bool scriptapi_on_chat_message(lua_State *L, const std::string &name,
 		const std::string &message);
 
-/* environment */
-// On environment step
-void scriptapi_environment_step(lua_State *L, float dtime);
-// After generating a piece of map
-void scriptapi_environment_on_generated(lua_State *L, v3s16 minp, v3s16 maxp,
-		u32 blockseed);
-
 /* server */
 void scriptapi_on_shutdown(lua_State *L);
 
@@ -77,110 +71,5 @@ void scriptapi_on_player_receive_fields(lua_State *L,
 		const std::string &formname,
 		const std::map<std::string, std::string> &fields);
 
-/* item callbacks */
-bool scriptapi_item_on_drop(lua_State *L, ItemStack &item,
-		ServerActiveObject *dropper, v3f pos);
-bool scriptapi_item_on_place(lua_State *L, ItemStack &item,
-		ServerActiveObject *placer, const PointedThing &pointed);
-bool scriptapi_item_on_use(lua_State *L, ItemStack &item,
-		ServerActiveObject *user, const PointedThing &pointed);
-
-/* node callbacks */
-bool scriptapi_node_on_punch(lua_State *L, v3s16 p, MapNode node,
-		ServerActiveObject *puncher);
-bool scriptapi_node_on_dig(lua_State *L, v3s16 p, MapNode node,
-		ServerActiveObject *digger);
-// Node constructor
-void scriptapi_node_on_construct(lua_State *L, v3s16 p, MapNode node);
-// Node destructor
-void scriptapi_node_on_destruct(lua_State *L, v3s16 p, MapNode node);
-// Node post-destructor
-void scriptapi_node_after_destruct(lua_State *L, v3s16 p, MapNode node);
-// Node Timer event
-bool scriptapi_node_on_timer(lua_State *L, v3s16 p, MapNode node, f32 dtime);
-// Called when a metadata form returns values
-void scriptapi_node_on_receive_fields(lua_State *L, v3s16 p,
-		const std::string &formname,
-		const std::map<std::string, std::string> &fields,
-		ServerActiveObject *sender);
-
-/* Node metadata inventory callbacks */
-// Return number of accepted items to be moved
-int scriptapi_nodemeta_inventory_allow_move(lua_State *L, v3s16 p,
-		const std::string &from_list, int from_index,
-		const std::string &to_list, int to_index,
-		int count, ServerActiveObject *player);
-// Return number of accepted items to be put
-int scriptapi_nodemeta_inventory_allow_put(lua_State *L, v3s16 p,
-		const std::string &listname, int index, ItemStack &stack,
-		ServerActiveObject *player);
-// Return number of accepted items to be taken
-int scriptapi_nodemeta_inventory_allow_take(lua_State *L, v3s16 p,
-		const std::string &listname, int index, ItemStack &stack,
-		ServerActiveObject *player);
-// Report moved items
-void scriptapi_nodemeta_inventory_on_move(lua_State *L, v3s16 p,
-		const std::string &from_list, int from_index,
-		const std::string &to_list, int to_index,
-		int count, ServerActiveObject *player);
-// Report put items
-void scriptapi_nodemeta_inventory_on_put(lua_State *L, v3s16 p,
-		const std::string &listname, int index, ItemStack &stack,
-		ServerActiveObject *player);
-// Report taken items
-void scriptapi_nodemeta_inventory_on_take(lua_State *L, v3s16 p,
-		const std::string &listname, int index, ItemStack &stack,
-		ServerActiveObject *player);
-
-/* Detached inventory callbacks */
-// Return number of accepted items to be moved
-int scriptapi_detached_inventory_allow_move(lua_State *L,
-		const std::string &name,
-		const std::string &from_list, int from_index,
-		const std::string &to_list, int to_index,
-		int count, ServerActiveObject *player);
-// Return number of accepted items to be put
-int scriptapi_detached_inventory_allow_put(lua_State *L,
-		const std::string &name,
-		const std::string &listname, int index, ItemStack &stack,
-		ServerActiveObject *player);
-// Return number of accepted items to be taken
-int scriptapi_detached_inventory_allow_take(lua_State *L,
-		const std::string &name,
-		const std::string &listname, int index, ItemStack &stack,
-		ServerActiveObject *player);
-// Report moved items
-void scriptapi_detached_inventory_on_move(lua_State *L,
-		const std::string &name,
-		const std::string &from_list, int from_index,
-		const std::string &to_list, int to_index,
-		int count, ServerActiveObject *player);
-// Report put items
-void scriptapi_detached_inventory_on_put(lua_State *L,
-		const std::string &name,
-		const std::string &listname, int index, ItemStack &stack,
-		ServerActiveObject *player);
-// Report taken items
-void scriptapi_detached_inventory_on_take(lua_State *L,
-		const std::string &name,
-		const std::string &listname, int index, ItemStack &stack,
-		ServerActiveObject *player);
-
-/* luaentity */
-// Returns true if succesfully added into Lua; false otherwise.
-bool scriptapi_luaentity_add(lua_State *L, u16 id, const char *name);
-void scriptapi_luaentity_activate(lua_State *L, u16 id,
-		const std::string &staticdata, u32 dtime_s);
-void scriptapi_luaentity_rm(lua_State *L, u16 id);
-std::string scriptapi_luaentity_get_staticdata(lua_State *L, u16 id);
-void scriptapi_luaentity_get_properties(lua_State *L, u16 id,
-		ObjectProperties *prop);
-void scriptapi_luaentity_step(lua_State *L, u16 id, float dtime);
-void scriptapi_luaentity_punch(lua_State *L, u16 id,
-		ServerActiveObject *puncher, float time_from_last_punch,
-		const ToolCapabilities *toolcap, v3f dir);
-void scriptapi_luaentity_rightclick(lua_State *L, u16 id,
-		ServerActiveObject *clicker);
-
 #endif
 
diff --git a/src/scriptapi_common.cpp b/src/scriptapi_common.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..2d6f6c72b8c27b39694ae4797954a2d1ec4a04d8
--- /dev/null
+++ b/src/scriptapi_common.cpp
@@ -0,0 +1,311 @@
+/*
+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 "scriptapi.h"
+#include "scriptapi_common.h"
+
+extern "C" {
+#include "lauxlib.h"
+}
+
+#include "script.h"
+#include "scriptapi_types.h"
+#include "scriptapi_object.h"
+
+
+Server* get_server(lua_State *L)
+{
+	// Get server from registry
+	lua_getfield(L, LUA_REGISTRYINDEX, "minetest_server");
+	Server *server = (Server*)lua_touserdata(L, -1);
+	lua_pop(L, 1);
+	return server;
+}
+
+ServerEnvironment* get_env(lua_State *L)
+{
+	// Get environment from registry
+	lua_getfield(L, LUA_REGISTRYINDEX, "minetest_env");
+	ServerEnvironment *env = (ServerEnvironment*)lua_touserdata(L, -1);
+	lua_pop(L, 1);
+	return env;
+}
+
+void warn_if_field_exists(lua_State *L, int table,
+		const char *fieldname, const std::string &message)
+{
+	lua_getfield(L, table, fieldname);
+	if(!lua_isnil(L, -1)){
+		infostream<<script_get_backtrace(L)<<std::endl;
+		infostream<<"WARNING: field \""<<fieldname<<"\": "
+				<<message<<std::endl;
+	}
+	lua_pop(L, 1);
+}
+
+/*
+	ToolCapabilities
+*/
+
+ToolCapabilities read_tool_capabilities(
+		lua_State *L, int table)
+{
+	ToolCapabilities toolcap;
+	getfloatfield(L, table, "full_punch_interval", toolcap.full_punch_interval);
+	getintfield(L, table, "max_drop_level", toolcap.max_drop_level);
+	lua_getfield(L, table, "groupcaps");
+	if(lua_istable(L, -1)){
+		int table_groupcaps = lua_gettop(L);
+		lua_pushnil(L);
+		while(lua_next(L, table_groupcaps) != 0){
+			// key at index -2 and value at index -1
+			std::string groupname = luaL_checkstring(L, -2);
+			if(lua_istable(L, -1)){
+				int table_groupcap = lua_gettop(L);
+				// This will be created
+				ToolGroupCap groupcap;
+				// Read simple parameters
+				getintfield(L, table_groupcap, "maxlevel", groupcap.maxlevel);
+				getintfield(L, table_groupcap, "uses", groupcap.uses);
+				// DEPRECATED: maxwear
+				float maxwear = 0;
+				if(getfloatfield(L, table_groupcap, "maxwear", maxwear)){
+					if(maxwear != 0)
+						groupcap.uses = 1.0/maxwear;
+					else
+						groupcap.uses = 0;
+					infostream<<script_get_backtrace(L)<<std::endl;
+					infostream<<"WARNING: field \"maxwear\" is deprecated; "
+							<<"should replace with uses=1/maxwear"<<std::endl;
+				}
+				// Read "times" table
+				lua_getfield(L, table_groupcap, "times");
+				if(lua_istable(L, -1)){
+					int table_times = lua_gettop(L);
+					lua_pushnil(L);
+					while(lua_next(L, table_times) != 0){
+						// key at index -2 and value at index -1
+						int rating = luaL_checkinteger(L, -2);
+						float time = luaL_checknumber(L, -1);
+						groupcap.times[rating] = time;
+						// removes value, keeps key for next iteration
+						lua_pop(L, 1);
+					}
+				}
+				lua_pop(L, 1);
+				// Insert groupcap into toolcap
+				toolcap.groupcaps[groupname] = groupcap;
+			}
+			// removes value, keeps key for next iteration
+			lua_pop(L, 1);
+		}
+	}
+	lua_pop(L, 1);
+	lua_getfield(L, table, "damage_groups");
+	if(lua_istable(L, -1)){
+		int table_damage_groups = lua_gettop(L);
+		lua_pushnil(L);
+		while(lua_next(L, table_damage_groups) != 0){
+			// key at index -2 and value at index -1
+			std::string groupname = luaL_checkstring(L, -2);
+			u16 value = luaL_checkinteger(L, -1);
+			toolcap.damageGroups[groupname] = value;
+			// removes value, keeps key for next iteration
+			lua_pop(L, 1);
+		}
+	}
+	lua_pop(L, 1);
+	return toolcap;
+}
+
+void set_tool_capabilities(lua_State *L, int table,
+		const ToolCapabilities &toolcap)
+{
+	setfloatfield(L, table, "full_punch_interval", toolcap.full_punch_interval);
+	setintfield(L, table, "max_drop_level", toolcap.max_drop_level);
+	// Create groupcaps table
+	lua_newtable(L);
+	// For each groupcap
+	for(std::map<std::string, ToolGroupCap>::const_iterator
+			i = toolcap.groupcaps.begin(); i != toolcap.groupcaps.end(); i++){
+		// Create groupcap table
+		lua_newtable(L);
+		const std::string &name = i->first;
+		const ToolGroupCap &groupcap = i->second;
+		// Create subtable "times"
+		lua_newtable(L);
+		for(std::map<int, float>::const_iterator
+				i = groupcap.times.begin(); i != groupcap.times.end(); i++){
+			int rating = i->first;
+			float time = i->second;
+			lua_pushinteger(L, rating);
+			lua_pushnumber(L, time);
+			lua_settable(L, -3);
+		}
+		// Set subtable "times"
+		lua_setfield(L, -2, "times");
+		// Set simple parameters
+		setintfield(L, -1, "maxlevel", groupcap.maxlevel);
+		setintfield(L, -1, "uses", groupcap.uses);
+		// Insert groupcap table into groupcaps table
+		lua_setfield(L, -2, name.c_str());
+	}
+	// Set groupcaps table
+	lua_setfield(L, -2, "groupcaps");
+	//Create damage_groups table
+	lua_newtable(L);
+	// For each damage group
+	for(std::map<std::string, s16>::const_iterator
+			i = toolcap.damageGroups.begin(); i != toolcap.damageGroups.end(); i++){
+		// Create damage group table
+		lua_pushinteger(L, i->second);
+		lua_setfield(L, -2, i->first.c_str());
+	}
+	lua_setfield(L, -2, "damage_groups");
+}
+
+void push_tool_capabilities(lua_State *L,
+		const ToolCapabilities &prop)
+{
+	lua_newtable(L);
+	set_tool_capabilities(L, -1, prop);
+}
+
+void realitycheck(lua_State *L)
+{
+	int top = lua_gettop(L);
+	if(top >= 30){
+		dstream<<"Stack is over 30:"<<std::endl;
+		stackDump(L, dstream);
+		script_error(L, "Stack is over 30 (reality check)");
+	}
+}
+
+/*
+	PointedThing
+*/
+
+void push_pointed_thing(lua_State *L, const PointedThing& pointed)
+{
+	lua_newtable(L);
+	if(pointed.type == POINTEDTHING_NODE)
+	{
+		lua_pushstring(L, "node");
+		lua_setfield(L, -2, "type");
+		push_v3s16(L, pointed.node_undersurface);
+		lua_setfield(L, -2, "under");
+		push_v3s16(L, pointed.node_abovesurface);
+		lua_setfield(L, -2, "above");
+	}
+	else if(pointed.type == POINTEDTHING_OBJECT)
+	{
+		lua_pushstring(L, "object");
+		lua_setfield(L, -2, "type");
+		objectref_get(L, pointed.object_id);
+		lua_setfield(L, -2, "ref");
+	}
+	else
+	{
+		lua_pushstring(L, "nothing");
+		lua_setfield(L, -2, "type");
+	}
+}
+
+void stackDump(lua_State *L, std::ostream &o)
+{
+  int i;
+  int top = lua_gettop(L);
+  for (i = 1; i <= top; i++) {  /* repeat for each level */
+	int t = lua_type(L, i);
+	switch (t) {
+
+	  case LUA_TSTRING:  /* strings */
+	  	o<<"\""<<lua_tostring(L, i)<<"\"";
+		break;
+
+	  case LUA_TBOOLEAN:  /* booleans */
+		o<<(lua_toboolean(L, i) ? "true" : "false");
+		break;
+
+	  case LUA_TNUMBER:  /* numbers */ {
+	  	char buf[10];
+		snprintf(buf, 10, "%g", lua_tonumber(L, i));
+		o<<buf;
+		break; }
+
+	  default:  /* other values */
+		o<<lua_typename(L, t);
+		break;
+
+	}
+	o<<" ";
+  }
+  o<<std::endl;
+}
+
+#if 0
+// Dump stack top with the dump2 function
+static void dump2(lua_State *L, const char *name)
+{
+	// Dump object (debug)
+	lua_getglobal(L, "dump2");
+	luaL_checktype(L, -1, LUA_TFUNCTION);
+	lua_pushvalue(L, -2); // Get previous stack top as first parameter
+	lua_pushstring(L, name);
+	if(lua_pcall(L, 2, 0, 0))
+		script_error(L, "error: %s", lua_tostring(L, -1));
+}
+#endif
+
+bool string_to_enum(const EnumString *spec, int &result,
+		const std::string &str)
+{
+	const EnumString *esp = spec;
+	while(esp->str){
+		if(str == std::string(esp->str)){
+			result = esp->num;
+			return true;
+		}
+		esp++;
+	}
+	return false;
+}
+
+/*bool enum_to_string(const EnumString *spec, std::string &result,
+		int num)
+{
+	const EnumString *esp = spec;
+	while(esp){
+		if(num == esp->num){
+			result = esp->str;
+			return true;
+		}
+		esp++;
+	}
+	return false;
+}*/
+
+int getenumfield(lua_State *L, int table,
+		const char *fieldname, const EnumString *spec, int default_)
+{
+	int result = default_;
+	string_to_enum(spec, result,
+			getstringfield_default(L, table, fieldname, ""));
+	return result;
+}
diff --git a/src/scriptapi_common.h b/src/scriptapi_common.h
new file mode 100644
index 0000000000000000000000000000000000000000..d029b48d331450dbb2974ba988eff2b5583b2255
--- /dev/null
+++ b/src/scriptapi_common.h
@@ -0,0 +1,112 @@
+/*
+Minetest-c55
+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 LUA_COMMON_H_
+#define LUA_COMMON_H_
+
+extern "C" {
+#include <lua.h>
+}
+
+#include "server.h"
+#include "environment.h"
+#include "nodedef.h"
+#include "util/pointedthing.h"
+#include "tool.h"
+
+Server* get_server(lua_State *L);
+ServerEnvironment* get_env(lua_State *L);
+
+void warn_if_field_exists(lua_State *L, int table,
+        const char *fieldname, const std::string &message);
+
+ToolCapabilities   read_tool_capabilities        (lua_State *L, int table);
+void               push_tool_capabilities        (lua_State *L,
+        const ToolCapabilities &prop);
+void               set_tool_capabilities         (lua_State *L, int table,
+        const ToolCapabilities &toolcap);
+
+void               realitycheck                  (lua_State *L);
+
+void               push_pointed_thing            (lua_State *L,
+        const PointedThing& pointed);
+
+void               stackDump                     (lua_State *L, std::ostream &o);
+
+class StackUnroller
+{
+private:
+	lua_State *m_lua;
+	int m_original_top;
+public:
+	StackUnroller(lua_State *L):
+		m_lua(L),
+		m_original_top(-1)
+	{
+		m_original_top = lua_gettop(m_lua); // store stack height
+	}
+	~StackUnroller()
+	{
+		lua_settop(m_lua, m_original_top); // restore stack height
+	}
+};
+
+/* definitions */
+// What scriptapi_run_callbacks does with the return values of callbacks.
+// Regardless of the mode, if only one callback is defined,
+// its return value is the total return value.
+// Modes only affect the case where 0 or >= 2 callbacks are defined.
+enum RunCallbacksMode
+{
+	// Returns the return value of the first callback
+	// Returns nil if list of callbacks is empty
+	RUN_CALLBACKS_MODE_FIRST,
+	// Returns the return value of the last callback
+	// Returns nil if list of callbacks is empty
+	RUN_CALLBACKS_MODE_LAST,
+	// If any callback returns a false value, the first such is returned
+	// Otherwise, the first callback's return value (trueish) is returned
+	// Returns true if list of callbacks is empty
+	RUN_CALLBACKS_MODE_AND,
+	// Like above, but stops calling callbacks (short circuit)
+	// after seeing the first false value
+	RUN_CALLBACKS_MODE_AND_SC,
+	// If any callback returns a true value, the first such is returned
+	// Otherwise, the first callback's return value (falseish) is returned
+	// Returns false if list of callbacks is empty
+	RUN_CALLBACKS_MODE_OR,
+	// Like above, but stops calling callbacks (short circuit)
+	// after seeing the first true value
+	RUN_CALLBACKS_MODE_OR_SC,
+	// Note: "a true value" and "a false value" refer to values that
+	// are converted by lua_toboolean to true or false, respectively.
+};
+
+struct EnumString
+{
+	int num;
+	const char *str;
+};
+
+bool string_to_enum(const EnumString *spec, int &result,
+		const std::string &str);
+
+int getenumfield(lua_State *L, int table,
+		const char *fieldname, const EnumString *spec, int default_);
+#endif /* LUA_COMMON_H_ */
diff --git a/src/scriptapi_content.cpp b/src/scriptapi_content.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..3b7ed517962936994707ea60b793bb6811ce70a7
--- /dev/null
+++ b/src/scriptapi_content.cpp
@@ -0,0 +1,322 @@
+/*
+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 "scriptapi.h"
+#include "scriptapi_content.h"
+#include "scriptapi_types.h"
+#include "scriptapi_common.h"
+#include "scriptapi_node.h"
+
+
+NodeBox read_nodebox(lua_State *L, int index)
+{
+	NodeBox nodebox;
+	if(lua_istable(L, -1)){
+		nodebox.type = (NodeBoxType)getenumfield(L, index, "type",
+				es_NodeBoxType, NODEBOX_REGULAR);
+
+		lua_getfield(L, index, "fixed");
+		if(lua_istable(L, -1))
+			nodebox.fixed = read_aabb3f_vector(L, -1, BS);
+		lua_pop(L, 1);
+
+		lua_getfield(L, index, "wall_top");
+		if(lua_istable(L, -1))
+			nodebox.wall_top = read_aabb3f(L, -1, BS);
+		lua_pop(L, 1);
+
+		lua_getfield(L, index, "wall_bottom");
+		if(lua_istable(L, -1))
+			nodebox.wall_bottom = read_aabb3f(L, -1, BS);
+		lua_pop(L, 1);
+
+		lua_getfield(L, index, "wall_side");
+		if(lua_istable(L, -1))
+			nodebox.wall_side = read_aabb3f(L, -1, BS);
+		lua_pop(L, 1);
+	}
+	return nodebox;
+}
+
+/*
+	SimpleSoundSpec
+*/
+
+void read_soundspec(lua_State *L, int index, SimpleSoundSpec &spec)
+{
+	if(index < 0)
+		index = lua_gettop(L) + 1 + index;
+	if(lua_isnil(L, index)){
+	} else if(lua_istable(L, index)){
+		getstringfield(L, index, "name", spec.name);
+		getfloatfield(L, index, "gain", spec.gain);
+	} else if(lua_isstring(L, index)){
+		spec.name = lua_tostring(L, index);
+	}
+}
+
+struct EnumString es_TileAnimationType[] =
+{
+	{TAT_NONE, "none"},
+	{TAT_VERTICAL_FRAMES, "vertical_frames"},
+	{0, NULL},
+};
+
+/*
+	TileDef
+*/
+
+TileDef read_tiledef(lua_State *L, int index)
+{
+	if(index < 0)
+		index = lua_gettop(L) + 1 + index;
+
+	TileDef tiledef;
+
+	// key at index -2 and value at index
+	if(lua_isstring(L, index)){
+		// "default_lava.png"
+		tiledef.name = lua_tostring(L, index);
+	}
+	else if(lua_istable(L, index))
+	{
+		// {name="default_lava.png", animation={}}
+		tiledef.name = "";
+		getstringfield(L, index, "name", tiledef.name);
+		getstringfield(L, index, "image", tiledef.name); // MaterialSpec compat.
+		tiledef.backface_culling = getboolfield_default(
+					L, index, "backface_culling", true);
+		// animation = {}
+		lua_getfield(L, index, "animation");
+		if(lua_istable(L, -1)){
+			// {type="vertical_frames", aspect_w=16, aspect_h=16, length=2.0}
+			tiledef.animation.type = (TileAnimationType)
+					getenumfield(L, -1, "type", es_TileAnimationType,
+					TAT_NONE);
+			tiledef.animation.aspect_w =
+					getintfield_default(L, -1, "aspect_w", 16);
+			tiledef.animation.aspect_h =
+					getintfield_default(L, -1, "aspect_h", 16);
+			tiledef.animation.length =
+					getfloatfield_default(L, -1, "length", 1.0);
+		}
+		lua_pop(L, 1);
+	}
+
+	return tiledef;
+}
+
+/*
+	ContentFeatures
+*/
+
+ContentFeatures read_content_features(lua_State *L, int index)
+{
+	if(index < 0)
+		index = lua_gettop(L) + 1 + index;
+
+	ContentFeatures f;
+
+	/* Cache existence of some callbacks */
+	lua_getfield(L, index, "on_construct");
+	if(!lua_isnil(L, -1)) f.has_on_construct = true;
+	lua_pop(L, 1);
+	lua_getfield(L, index, "on_destruct");
+	if(!lua_isnil(L, -1)) f.has_on_destruct = true;
+	lua_pop(L, 1);
+	lua_getfield(L, index, "after_destruct");
+	if(!lua_isnil(L, -1)) f.has_after_destruct = true;
+	lua_pop(L, 1);
+
+	lua_getfield(L, index, "on_rightclick");
+	f.rightclickable = lua_isfunction(L, -1);
+	lua_pop(L, 1);
+	
+	/* Name */
+	getstringfield(L, index, "name", f.name);
+
+	/* Groups */
+	lua_getfield(L, index, "groups");
+	read_groups(L, -1, f.groups);
+	lua_pop(L, 1);
+
+	/* Visual definition */
+
+	f.drawtype = (NodeDrawType)getenumfield(L, index, "drawtype", es_DrawType,
+			NDT_NORMAL);
+	getfloatfield(L, index, "visual_scale", f.visual_scale);
+
+	// tiles = {}
+	lua_getfield(L, index, "tiles");
+	// If nil, try the deprecated name "tile_images" instead
+	if(lua_isnil(L, -1)){
+		lua_pop(L, 1);
+		warn_if_field_exists(L, index, "tile_images",
+				"Deprecated; new name is \"tiles\".");
+		lua_getfield(L, index, "tile_images");
+	}
+	if(lua_istable(L, -1)){
+		int table = lua_gettop(L);
+		lua_pushnil(L);
+		int i = 0;
+		while(lua_next(L, table) != 0){
+			// Read tiledef from value
+			f.tiledef[i] = read_tiledef(L, -1);
+			// removes value, keeps key for next iteration
+			lua_pop(L, 1);
+			i++;
+			if(i==6){
+				lua_pop(L, 1);
+				break;
+			}
+		}
+		// Copy last value to all remaining textures
+		if(i >= 1){
+			TileDef lasttile = f.tiledef[i-1];
+			while(i < 6){
+				f.tiledef[i] = lasttile;
+				i++;
+			}
+		}
+	}
+	lua_pop(L, 1);
+
+	// special_tiles = {}
+	lua_getfield(L, index, "special_tiles");
+	// If nil, try the deprecated name "special_materials" instead
+	if(lua_isnil(L, -1)){
+		lua_pop(L, 1);
+		warn_if_field_exists(L, index, "special_materials",
+				"Deprecated; new name is \"special_tiles\".");
+		lua_getfield(L, index, "special_materials");
+	}
+	if(lua_istable(L, -1)){
+		int table = lua_gettop(L);
+		lua_pushnil(L);
+		int i = 0;
+		while(lua_next(L, table) != 0){
+			// Read tiledef from value
+			f.tiledef_special[i] = read_tiledef(L, -1);
+			// removes value, keeps key for next iteration
+			lua_pop(L, 1);
+			i++;
+			if(i==6){
+				lua_pop(L, 1);
+				break;
+			}
+		}
+	}
+	lua_pop(L, 1);
+
+	f.alpha = getintfield_default(L, index, "alpha", 255);
+
+	/* Other stuff */
+
+	lua_getfield(L, index, "post_effect_color");
+	if(!lua_isnil(L, -1))
+		f.post_effect_color = readARGB8(L, -1);
+	lua_pop(L, 1);
+
+	f.param_type = (ContentParamType)getenumfield(L, index, "paramtype",
+			es_ContentParamType, CPT_NONE);
+	f.param_type_2 = (ContentParamType2)getenumfield(L, index, "paramtype2",
+			es_ContentParamType2, CPT2_NONE);
+
+	// Warn about some deprecated fields
+	warn_if_field_exists(L, index, "wall_mounted",
+			"deprecated: use paramtype2 = 'wallmounted'");
+	warn_if_field_exists(L, index, "light_propagates",
+			"deprecated: determined from paramtype");
+	warn_if_field_exists(L, index, "dug_item",
+			"deprecated: use 'drop' field");
+	warn_if_field_exists(L, index, "extra_dug_item",
+			"deprecated: use 'drop' field");
+	warn_if_field_exists(L, index, "extra_dug_item_rarity",
+			"deprecated: use 'drop' field");
+	warn_if_field_exists(L, index, "metadata_name",
+			"deprecated: use on_add and metadata callbacks");
+
+	// True for all ground-like things like stone and mud, false for eg. trees
+	getboolfield(L, index, "is_ground_content", f.is_ground_content);
+	f.light_propagates = (f.param_type == CPT_LIGHT);
+	getboolfield(L, index, "sunlight_propagates", f.sunlight_propagates);
+	// This is used for collision detection.
+	// Also for general solidness queries.
+	getboolfield(L, index, "walkable", f.walkable);
+	// Player can point to these
+	getboolfield(L, index, "pointable", f.pointable);
+	// Player can dig these
+	getboolfield(L, index, "diggable", f.diggable);
+	// Player can climb these
+	getboolfield(L, index, "climbable", f.climbable);
+	// Player can build on these
+	getboolfield(L, index, "buildable_to", f.buildable_to);
+	// Whether the node is non-liquid, source liquid or flowing liquid
+	f.liquid_type = (LiquidType)getenumfield(L, index, "liquidtype",
+			es_LiquidType, LIQUID_NONE);
+	// If the content is liquid, this is the flowing version of the liquid.
+	getstringfield(L, index, "liquid_alternative_flowing",
+			f.liquid_alternative_flowing);
+	// If the content is liquid, this is the source version of the liquid.
+	getstringfield(L, index, "liquid_alternative_source",
+			f.liquid_alternative_source);
+	// Viscosity for fluid flow, ranging from 1 to 7, with
+	// 1 giving almost instantaneous propagation and 7 being
+	// the slowest possible
+	f.liquid_viscosity = getintfield_default(L, index,
+			"liquid_viscosity", f.liquid_viscosity);
+	getboolfield(L, index, "liquid_renewable", f.liquid_renewable);
+	// Amount of light the node emits
+	f.light_source = getintfield_default(L, index,
+			"light_source", f.light_source);
+	f.damage_per_second = getintfield_default(L, index,
+			"damage_per_second", f.damage_per_second);
+
+	lua_getfield(L, index, "node_box");
+	if(lua_istable(L, -1))
+		f.node_box = read_nodebox(L, -1);
+	lua_pop(L, 1);
+
+	lua_getfield(L, index, "selection_box");
+	if(lua_istable(L, -1))
+		f.selection_box = read_nodebox(L, -1);
+ 	lua_pop(L, 1);
+
+	// Set to true if paramtype used to be 'facedir_simple'
+	getboolfield(L, index, "legacy_facedir_simple", f.legacy_facedir_simple);
+	// Set to true if wall_mounted used to be set to true
+	getboolfield(L, index, "legacy_wallmounted", f.legacy_wallmounted);
+
+	// Sound table
+	lua_getfield(L, index, "sounds");
+	if(lua_istable(L, -1)){
+		lua_getfield(L, -1, "footstep");
+		read_soundspec(L, -1, f.sound_footstep);
+		lua_pop(L, 1);
+		lua_getfield(L, -1, "dig");
+		read_soundspec(L, -1, f.sound_dig);
+		lua_pop(L, 1);
+		lua_getfield(L, -1, "dug");
+		read_soundspec(L, -1, f.sound_dug);
+		lua_pop(L, 1);
+	}
+	lua_pop(L, 1);
+
+	return f;
+}
diff --git a/src/scriptapi_content.h b/src/scriptapi_content.h
new file mode 100644
index 0000000000000000000000000000000000000000..801f3856e4461415d2c6d5fdf63dfed58cc90e1c
--- /dev/null
+++ b/src/scriptapi_content.h
@@ -0,0 +1,37 @@
+/*
+Minetest-c55
+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 LUA_CONTENT_H_
+#define LUA_CONTENT_H_
+
+extern "C" {
+#include <lua.h>
+}
+
+#include "nodedef.h"
+
+ContentFeatures    read_content_features         (lua_State *L, int index);
+TileDef            read_tiledef                  (lua_State *L, int index);
+void               read_soundspec                (lua_State *L, int index,
+		SimpleSoundSpec &spec);
+NodeBox            read_nodebox                  (lua_State *L, int index);
+
+extern struct EnumString es_TileAnimationType[];
+
+#endif /* LUA_CONTENT_H_ */
diff --git a/src/scriptapi_craft.cpp b/src/scriptapi_craft.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..183eeb84055759ed1f4c534728632df688088c8d
--- /dev/null
+++ b/src/scriptapi_craft.cpp
@@ -0,0 +1,454 @@
+/*
+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 "scriptapi.h"
+#include "scriptapi_craft.h"
+
+extern "C" {
+#include <lauxlib.h>
+}
+
+#include "script.h"
+#include "scriptapi_types.h"
+#include "scriptapi_common.h"
+#include "scriptapi_item.h"
+
+
+struct EnumString es_CraftMethod[] =
+{
+	{CRAFT_METHOD_NORMAL, "normal"},
+	{CRAFT_METHOD_COOKING, "cooking"},
+	{CRAFT_METHOD_FUEL, "fuel"},
+	{0, NULL},
+};
+
+
+// helper for register_craft
+bool read_craft_recipe_shaped(lua_State *L, int index,
+		int &width, std::vector<std::string> &recipe)
+{
+	if(index < 0)
+		index = lua_gettop(L) + 1 + index;
+
+	if(!lua_istable(L, index))
+		return false;
+
+	lua_pushnil(L);
+	int rowcount = 0;
+	while(lua_next(L, index) != 0){
+		int colcount = 0;
+		// key at index -2 and value at index -1
+		if(!lua_istable(L, -1))
+			return false;
+		int table2 = lua_gettop(L);
+		lua_pushnil(L);
+		while(lua_next(L, table2) != 0){
+			// key at index -2 and value at index -1
+			if(!lua_isstring(L, -1))
+				return false;
+			recipe.push_back(lua_tostring(L, -1));
+			// removes value, keeps key for next iteration
+			lua_pop(L, 1);
+			colcount++;
+		}
+		if(rowcount == 0){
+			width = colcount;
+		} else {
+			if(colcount != width)
+				return false;
+		}
+		// removes value, keeps key for next iteration
+		lua_pop(L, 1);
+		rowcount++;
+	}
+	return width != 0;
+}
+
+// helper for register_craft
+bool read_craft_recipe_shapeless(lua_State *L, int index,
+		std::vector<std::string> &recipe)
+{
+	if(index < 0)
+		index = lua_gettop(L) + 1 + index;
+
+	if(!lua_istable(L, index))
+		return false;
+
+	lua_pushnil(L);
+	while(lua_next(L, index) != 0){
+		// key at index -2 and value at index -1
+		if(!lua_isstring(L, -1))
+			return false;
+		recipe.push_back(lua_tostring(L, -1));
+		// removes value, keeps key for next iteration
+		lua_pop(L, 1);
+	}
+	return true;
+}
+
+// helper for register_craft
+bool read_craft_replacements(lua_State *L, int index,
+		CraftReplacements &replacements)
+{
+	if(index < 0)
+		index = lua_gettop(L) + 1 + index;
+
+	if(!lua_istable(L, index))
+		return false;
+
+	lua_pushnil(L);
+	while(lua_next(L, index) != 0){
+		// key at index -2 and value at index -1
+		if(!lua_istable(L, -1))
+			return false;
+		lua_rawgeti(L, -1, 1);
+		if(!lua_isstring(L, -1))
+			return false;
+		std::string replace_from = lua_tostring(L, -1);
+		lua_pop(L, 1);
+		lua_rawgeti(L, -1, 2);
+		if(!lua_isstring(L, -1))
+			return false;
+		std::string replace_to = lua_tostring(L, -1);
+		lua_pop(L, 1);
+		replacements.pairs.push_back(
+				std::make_pair(replace_from, replace_to));
+		// removes value, keeps key for next iteration
+		lua_pop(L, 1);
+	}
+	return true;
+}
+// register_craft({output=item, recipe={{item00,item10},{item01,item11}})
+int l_register_craft(lua_State *L)
+{
+	//infostream<<"register_craft"<<std::endl;
+	luaL_checktype(L, 1, LUA_TTABLE);
+	int table = 1;
+
+	// Get the writable craft definition manager from the server
+	IWritableCraftDefManager *craftdef =
+			get_server(L)->getWritableCraftDefManager();
+
+	std::string type = getstringfield_default(L, table, "type", "shaped");
+
+	/*
+		CraftDefinitionShaped
+	*/
+	if(type == "shaped"){
+		std::string output = getstringfield_default(L, table, "output", "");
+		if(output == "")
+			throw LuaError(L, "Crafting definition is missing an output");
+
+		int width = 0;
+		std::vector<std::string> recipe;
+		lua_getfield(L, table, "recipe");
+		if(lua_isnil(L, -1))
+			throw LuaError(L, "Crafting definition is missing a recipe"
+					" (output=\"" + output + "\")");
+		if(!read_craft_recipe_shaped(L, -1, width, recipe))
+			throw LuaError(L, "Invalid crafting recipe"
+					" (output=\"" + output + "\")");
+
+		CraftReplacements replacements;
+		lua_getfield(L, table, "replacements");
+		if(!lua_isnil(L, -1))
+		{
+			if(!read_craft_replacements(L, -1, replacements))
+				throw LuaError(L, "Invalid replacements"
+						" (output=\"" + output + "\")");
+		}
+
+		CraftDefinition *def = new CraftDefinitionShaped(
+				output, width, recipe, replacements);
+		craftdef->registerCraft(def);
+	}
+	/*
+		CraftDefinitionShapeless
+	*/
+	else if(type == "shapeless"){
+		std::string output = getstringfield_default(L, table, "output", "");
+		if(output == "")
+			throw LuaError(L, "Crafting definition (shapeless)"
+					" is missing an output");
+
+		std::vector<std::string> recipe;
+		lua_getfield(L, table, "recipe");
+		if(lua_isnil(L, -1))
+			throw LuaError(L, "Crafting definition (shapeless)"
+					" is missing a recipe"
+					" (output=\"" + output + "\")");
+		if(!read_craft_recipe_shapeless(L, -1, recipe))
+			throw LuaError(L, "Invalid crafting recipe"
+					" (output=\"" + output + "\")");
+
+		CraftReplacements replacements;
+		lua_getfield(L, table, "replacements");
+		if(!lua_isnil(L, -1))
+		{
+			if(!read_craft_replacements(L, -1, replacements))
+				throw LuaError(L, "Invalid replacements"
+						" (output=\"" + output + "\")");
+		}
+
+		CraftDefinition *def = new CraftDefinitionShapeless(
+				output, recipe, replacements);
+		craftdef->registerCraft(def);
+	}
+	/*
+		CraftDefinitionToolRepair
+	*/
+	else if(type == "toolrepair"){
+		float additional_wear = getfloatfield_default(L, table,
+				"additional_wear", 0.0);
+
+		CraftDefinition *def = new CraftDefinitionToolRepair(
+				additional_wear);
+		craftdef->registerCraft(def);
+	}
+	/*
+		CraftDefinitionCooking
+	*/
+	else if(type == "cooking"){
+		std::string output = getstringfield_default(L, table, "output", "");
+		if(output == "")
+			throw LuaError(L, "Crafting definition (cooking)"
+					" is missing an output");
+
+		std::string recipe = getstringfield_default(L, table, "recipe", "");
+		if(recipe == "")
+			throw LuaError(L, "Crafting definition (cooking)"
+					" is missing a recipe"
+					" (output=\"" + output + "\")");
+
+		float cooktime = getfloatfield_default(L, table, "cooktime", 3.0);
+
+		CraftReplacements replacements;
+		lua_getfield(L, table, "replacements");
+		if(!lua_isnil(L, -1))
+		{
+			if(!read_craft_replacements(L, -1, replacements))
+				throw LuaError(L, "Invalid replacements"
+						" (cooking output=\"" + output + "\")");
+		}
+
+		CraftDefinition *def = new CraftDefinitionCooking(
+				output, recipe, cooktime, replacements);
+		craftdef->registerCraft(def);
+	}
+	/*
+		CraftDefinitionFuel
+	*/
+	else if(type == "fuel"){
+		std::string recipe = getstringfield_default(L, table, "recipe", "");
+		if(recipe == "")
+			throw LuaError(L, "Crafting definition (fuel)"
+					" is missing a recipe");
+
+		float burntime = getfloatfield_default(L, table, "burntime", 1.0);
+
+		CraftReplacements replacements;
+		lua_getfield(L, table, "replacements");
+		if(!lua_isnil(L, -1))
+		{
+			if(!read_craft_replacements(L, -1, replacements))
+				throw LuaError(L, "Invalid replacements"
+						" (fuel recipe=\"" + recipe + "\")");
+		}
+
+		CraftDefinition *def = new CraftDefinitionFuel(
+				recipe, burntime, replacements);
+		craftdef->registerCraft(def);
+	}
+	else
+	{
+		throw LuaError(L, "Unknown crafting definition type: \"" + type + "\"");
+	}
+
+	lua_pop(L, 1);
+	return 0; /* number of results */
+}
+
+// get_craft_result(input)
+int l_get_craft_result(lua_State *L)
+{
+	int input_i = 1;
+	std::string method_s = getstringfield_default(L, input_i, "method", "normal");
+	enum CraftMethod method = (CraftMethod)getenumfield(L, input_i, "method",
+				es_CraftMethod, CRAFT_METHOD_NORMAL);
+	int width = 1;
+	lua_getfield(L, input_i, "width");
+	if(lua_isnumber(L, -1))
+		width = luaL_checkinteger(L, -1);
+	lua_pop(L, 1);
+	lua_getfield(L, input_i, "items");
+	std::vector<ItemStack> items = read_items(L, -1);
+	lua_pop(L, 1); // items
+
+	IGameDef *gdef = get_server(L);
+	ICraftDefManager *cdef = gdef->cdef();
+	CraftInput input(method, width, items);
+	CraftOutput output;
+	bool got = cdef->getCraftResult(input, output, true, gdef);
+	lua_newtable(L); // output table
+	if(got){
+		ItemStack item;
+		item.deSerialize(output.item, gdef->idef());
+		LuaItemStack::create(L, item);
+		lua_setfield(L, -2, "item");
+		setintfield(L, -1, "time", output.time);
+	} else {
+		LuaItemStack::create(L, ItemStack());
+		lua_setfield(L, -2, "item");
+		setintfield(L, -1, "time", 0);
+	}
+	lua_newtable(L); // decremented input table
+	lua_pushstring(L, method_s.c_str());
+	lua_setfield(L, -2, "method");
+	lua_pushinteger(L, width);
+	lua_setfield(L, -2, "width");
+	push_items(L, input.items);
+	lua_setfield(L, -2, "items");
+	return 2;
+}
+
+// get_craft_recipe(result item)
+int l_get_craft_recipe(lua_State *L)
+{
+	int k = 0;
+	char tmp[20];
+	int input_i = 1;
+	std::string o_item = luaL_checkstring(L,input_i);
+
+	IGameDef *gdef = get_server(L);
+	ICraftDefManager *cdef = gdef->cdef();
+	CraftInput input;
+	CraftOutput output(o_item,0);
+	bool got = cdef->getCraftRecipe(input, output, gdef);
+	lua_newtable(L); // output table
+	if(got){
+		lua_newtable(L);
+		for(std::vector<ItemStack>::const_iterator
+			i = input.items.begin();
+			i != input.items.end(); i++, k++)
+		{
+			if (i->empty())
+			{
+				continue;
+			}
+			sprintf(tmp,"%d",k);
+			lua_pushstring(L,tmp);
+			lua_pushstring(L,i->name.c_str());
+			lua_settable(L, -3);
+		}
+		lua_setfield(L, -2, "items");
+		setintfield(L, -1, "width", input.width);
+		switch (input.method) {
+		case CRAFT_METHOD_NORMAL:
+			lua_pushstring(L,"normal");
+			break;
+		case CRAFT_METHOD_COOKING:
+			lua_pushstring(L,"cooking");
+			break;
+		case CRAFT_METHOD_FUEL:
+			lua_pushstring(L,"fuel");
+			break;
+		default:
+			lua_pushstring(L,"unknown");
+		}
+		lua_setfield(L, -2, "type");
+	} else {
+		lua_pushnil(L);
+		lua_setfield(L, -2, "items");
+		setintfield(L, -1, "width", 0);
+	}
+	return 1;
+}
+
+// get_all_craft_recipes(result item)
+int l_get_all_craft_recipes(lua_State *L)
+{
+	char tmp[20];
+	int input_i = 1;
+	std::string o_item = luaL_checkstring(L,input_i);
+	IGameDef *gdef = get_server(L);
+	ICraftDefManager *cdef = gdef->cdef();
+	CraftInput input;
+	CraftOutput output(o_item,0);
+	std::vector<CraftDefinition*> recipes_list = cdef->getCraftRecipes(output, gdef);
+	if (recipes_list.empty())
+	{
+		lua_pushnil(L);
+		return 1;
+	}
+	// Get the table insert function
+	lua_getglobal(L, "table");
+	lua_getfield(L, -1, "insert");
+	int table_insert = lua_gettop(L);
+	lua_newtable(L);
+	int table = lua_gettop(L);
+	for(std::vector<CraftDefinition*>::const_iterator
+		i = recipes_list.begin();
+		i != recipes_list.end(); i++)
+	{
+		CraftOutput tmpout;
+		tmpout.item = "";
+		tmpout.time = 0;
+		CraftDefinition *def = *i;
+		tmpout = def->getOutput(input, gdef);
+		if(tmpout.item.substr(0,output.item.length()) == output.item)
+		{
+			input = def->getInput(output, gdef);
+			lua_pushvalue(L, table_insert);
+			lua_pushvalue(L, table);
+			lua_newtable(L);
+			int k = 0;
+			lua_newtable(L);
+			for(std::vector<ItemStack>::const_iterator
+				i = input.items.begin();
+				i != input.items.end(); i++, k++)
+			{
+				if (i->empty()) continue;
+				sprintf(tmp,"%d",k);
+				lua_pushstring(L,tmp);
+				lua_pushstring(L,i->name.c_str());
+				lua_settable(L, -3);
+			}
+			lua_setfield(L, -2, "items");
+			setintfield(L, -1, "width", input.width);
+			switch (input.method)
+				{
+				case CRAFT_METHOD_NORMAL:
+					lua_pushstring(L,"normal");
+					break;
+				case CRAFT_METHOD_COOKING:
+					lua_pushstring(L,"cooking");
+					break;
+				case CRAFT_METHOD_FUEL:
+					lua_pushstring(L,"fuel");
+					break;
+				default:
+					lua_pushstring(L,"unknown");
+				}
+			lua_setfield(L, -2, "type");
+			if(lua_pcall(L, 2, 0, 0))
+			script_error(L, "error: %s", lua_tostring(L, -1));
+		}
+	}
+	return 1;
+}
diff --git a/src/scriptapi_craft.h b/src/scriptapi_craft.h
new file mode 100644
index 0000000000000000000000000000000000000000..f28989fd96b501e67500495e18ed5ca91f027ebc
--- /dev/null
+++ b/src/scriptapi_craft.h
@@ -0,0 +1,51 @@
+/*
+Minetest-c55
+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 LUA_CRAFT_H_
+#define LUA_CRAFT_H_
+
+#include <vector>
+
+extern "C" {
+#include <lua.h>
+}
+
+#include "craftdef.h"
+
+/*****************************************************************************/
+/* Mod API                                                                   */
+/*****************************************************************************/
+int l_register_craft(lua_State *L);
+int l_get_craft_recipe(lua_State *L);
+int l_get_all_craft_recipes(lua_State *L);
+int l_get_craft_result(lua_State *L);
+
+/*****************************************************************************/
+/* scriptapi internal                                                        */
+/*****************************************************************************/
+bool read_craft_replacements(lua_State *L, int index,
+		CraftReplacements &replacements);
+bool read_craft_recipe_shapeless(lua_State *L, int index,
+		std::vector<std::string> &recipe);
+bool read_craft_recipe_shaped(lua_State *L, int index,
+		int &width, std::vector<std::string> &recipe);
+
+extern struct EnumString es_CraftMethod[];
+
+#endif /* LUA_CRAFT_H_ */
diff --git a/src/scriptapi_entity.cpp b/src/scriptapi_entity.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..c15f801a633a42eb68e06bdeb00752c419c8749d
--- /dev/null
+++ b/src/scriptapi_entity.cpp
@@ -0,0 +1,293 @@
+/*
+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 "scriptapi.h"
+#include "scriptapi_entity.h"
+
+extern "C" {
+#include <lauxlib.h>
+}
+
+#include "log.h"
+#include "script.h"
+#include "scriptapi_types.h"
+#include "scriptapi_object.h"
+#include "scriptapi_common.h"
+
+
+void luaentity_get(lua_State *L, u16 id)
+{
+	// Get minetest.luaentities[i]
+	lua_getglobal(L, "minetest");
+	lua_getfield(L, -1, "luaentities");
+	luaL_checktype(L, -1, LUA_TTABLE);
+	lua_pushnumber(L, id);
+	lua_gettable(L, -2);
+	lua_remove(L, -2); // luaentities
+	lua_remove(L, -2); // minetest
+}
+
+/*
+	luaentity
+*/
+
+bool scriptapi_luaentity_add(lua_State *L, u16 id, const char *name)
+{
+	realitycheck(L);
+	assert(lua_checkstack(L, 20));
+	verbosestream<<"scriptapi_luaentity_add: id="<<id<<" name=\""
+			<<name<<"\""<<std::endl;
+	StackUnroller stack_unroller(L);
+
+	// Get minetest.registered_entities[name]
+	lua_getglobal(L, "minetest");
+	lua_getfield(L, -1, "registered_entities");
+	luaL_checktype(L, -1, LUA_TTABLE);
+	lua_pushstring(L, name);
+	lua_gettable(L, -2);
+	// Should be a table, which we will use as a prototype
+	//luaL_checktype(L, -1, LUA_TTABLE);
+	if(lua_type(L, -1) != LUA_TTABLE){
+		errorstream<<"LuaEntity name \""<<name<<"\" not defined"<<std::endl;
+		return false;
+	}
+	int prototype_table = lua_gettop(L);
+	//dump2(L, "prototype_table");
+
+	// Create entity object
+	lua_newtable(L);
+	int object = lua_gettop(L);
+
+	// Set object metatable
+	lua_pushvalue(L, prototype_table);
+	lua_setmetatable(L, -2);
+
+	// Add object reference
+	// This should be userdata with metatable ObjectRef
+	objectref_get(L, id);
+	luaL_checktype(L, -1, LUA_TUSERDATA);
+	if(!luaL_checkudata(L, -1, "ObjectRef"))
+		luaL_typerror(L, -1, "ObjectRef");
+	lua_setfield(L, -2, "object");
+
+	// minetest.luaentities[id] = object
+	lua_getglobal(L, "minetest");
+	lua_getfield(L, -1, "luaentities");
+	luaL_checktype(L, -1, LUA_TTABLE);
+	lua_pushnumber(L, id); // Push id
+	lua_pushvalue(L, object); // Copy object to top of stack
+	lua_settable(L, -3);
+
+	return true;
+}
+
+void scriptapi_luaentity_activate(lua_State *L, u16 id,
+		const std::string &staticdata, u32 dtime_s)
+{
+	realitycheck(L);
+	assert(lua_checkstack(L, 20));
+	verbosestream<<"scriptapi_luaentity_activate: id="<<id<<std::endl;
+	StackUnroller stack_unroller(L);
+
+	// Get minetest.luaentities[id]
+	luaentity_get(L, id);
+	int object = lua_gettop(L);
+
+	// Get on_activate function
+	lua_pushvalue(L, object);
+	lua_getfield(L, -1, "on_activate");
+	if(!lua_isnil(L, -1)){
+		luaL_checktype(L, -1, LUA_TFUNCTION);
+		lua_pushvalue(L, object); // self
+		lua_pushlstring(L, staticdata.c_str(), staticdata.size());
+		lua_pushinteger(L, dtime_s);
+		// Call with 3 arguments, 0 results
+		if(lua_pcall(L, 3, 0, 0))
+			script_error(L, "error running function on_activate: %s\n",
+					lua_tostring(L, -1));
+	}
+}
+
+void scriptapi_luaentity_rm(lua_State *L, u16 id)
+{
+	realitycheck(L);
+	assert(lua_checkstack(L, 20));
+	verbosestream<<"scriptapi_luaentity_rm: id="<<id<<std::endl;
+
+	// Get minetest.luaentities table
+	lua_getglobal(L, "minetest");
+	lua_getfield(L, -1, "luaentities");
+	luaL_checktype(L, -1, LUA_TTABLE);
+	int objectstable = lua_gettop(L);
+
+	// Set luaentities[id] = nil
+	lua_pushnumber(L, id); // Push id
+	lua_pushnil(L);
+	lua_settable(L, objectstable);
+
+	lua_pop(L, 2); // pop luaentities, minetest
+}
+
+std::string scriptapi_luaentity_get_staticdata(lua_State *L, u16 id)
+{
+	realitycheck(L);
+	assert(lua_checkstack(L, 20));
+	//infostream<<"scriptapi_luaentity_get_staticdata: id="<<id<<std::endl;
+	StackUnroller stack_unroller(L);
+
+	// Get minetest.luaentities[id]
+	luaentity_get(L, id);
+	int object = lua_gettop(L);
+
+	// Get get_staticdata function
+	lua_pushvalue(L, object);
+	lua_getfield(L, -1, "get_staticdata");
+	if(lua_isnil(L, -1))
+		return "";
+
+	luaL_checktype(L, -1, LUA_TFUNCTION);
+	lua_pushvalue(L, object); // self
+	// Call with 1 arguments, 1 results
+	if(lua_pcall(L, 1, 1, 0))
+		script_error(L, "error running function get_staticdata: %s\n",
+				lua_tostring(L, -1));
+
+	size_t len=0;
+	const char *s = lua_tolstring(L, -1, &len);
+	return std::string(s, len);
+}
+
+void scriptapi_luaentity_get_properties(lua_State *L, u16 id,
+		ObjectProperties *prop)
+{
+	realitycheck(L);
+	assert(lua_checkstack(L, 20));
+	//infostream<<"scriptapi_luaentity_get_properties: id="<<id<<std::endl;
+	StackUnroller stack_unroller(L);
+
+	// Get minetest.luaentities[id]
+	luaentity_get(L, id);
+	//int object = lua_gettop(L);
+
+	// Set default values that differ from ObjectProperties defaults
+	prop->hp_max = 10;
+
+	/* Read stuff */
+
+	prop->hp_max = getintfield_default(L, -1, "hp_max", 10);
+
+	getboolfield(L, -1, "physical", prop->physical);
+
+	getfloatfield(L, -1, "weight", prop->weight);
+
+	lua_getfield(L, -1, "collisionbox");
+	if(lua_istable(L, -1))
+		prop->collisionbox = read_aabb3f(L, -1, 1.0);
+	lua_pop(L, 1);
+
+	getstringfield(L, -1, "visual", prop->visual);
+
+	getstringfield(L, -1, "mesh", prop->mesh);
+
+	// Deprecated: read object properties directly
+	read_object_properties(L, -1, prop);
+
+	// Read initial_properties
+	lua_getfield(L, -1, "initial_properties");
+	read_object_properties(L, -1, prop);
+	lua_pop(L, 1);
+}
+
+void scriptapi_luaentity_step(lua_State *L, u16 id, float dtime)
+{
+	realitycheck(L);
+	assert(lua_checkstack(L, 20));
+	//infostream<<"scriptapi_luaentity_step: id="<<id<<std::endl;
+	StackUnroller stack_unroller(L);
+
+	// Get minetest.luaentities[id]
+	luaentity_get(L, id);
+	int object = lua_gettop(L);
+	// State: object is at top of stack
+	// Get step function
+	lua_getfield(L, -1, "on_step");
+	if(lua_isnil(L, -1))
+		return;
+	luaL_checktype(L, -1, LUA_TFUNCTION);
+	lua_pushvalue(L, object); // self
+	lua_pushnumber(L, dtime); // dtime
+	// Call with 2 arguments, 0 results
+	if(lua_pcall(L, 2, 0, 0))
+		script_error(L, "error running function 'on_step': %s\n", lua_tostring(L, -1));
+}
+
+// Calls entity:on_punch(ObjectRef puncher, time_from_last_punch,
+//                       tool_capabilities, direction)
+void scriptapi_luaentity_punch(lua_State *L, u16 id,
+		ServerActiveObject *puncher, float time_from_last_punch,
+		const ToolCapabilities *toolcap, v3f dir)
+{
+	realitycheck(L);
+	assert(lua_checkstack(L, 20));
+	//infostream<<"scriptapi_luaentity_step: id="<<id<<std::endl;
+	StackUnroller stack_unroller(L);
+
+	// Get minetest.luaentities[id]
+	luaentity_get(L, id);
+	int object = lua_gettop(L);
+	// State: object is at top of stack
+	// Get function
+	lua_getfield(L, -1, "on_punch");
+	if(lua_isnil(L, -1))
+		return;
+	luaL_checktype(L, -1, LUA_TFUNCTION);
+	lua_pushvalue(L, object); // self
+	objectref_get_or_create(L, puncher); // Clicker reference
+	lua_pushnumber(L, time_from_last_punch);
+	push_tool_capabilities(L, *toolcap);
+	push_v3f(L, dir);
+	// Call with 5 arguments, 0 results
+	if(lua_pcall(L, 5, 0, 0))
+		script_error(L, "error running function 'on_punch': %s\n", lua_tostring(L, -1));
+}
+
+// Calls entity:on_rightclick(ObjectRef clicker)
+void scriptapi_luaentity_rightclick(lua_State *L, u16 id,
+		ServerActiveObject *clicker)
+{
+	realitycheck(L);
+	assert(lua_checkstack(L, 20));
+	//infostream<<"scriptapi_luaentity_step: id="<<id<<std::endl;
+	StackUnroller stack_unroller(L);
+
+	// Get minetest.luaentities[id]
+	luaentity_get(L, id);
+	int object = lua_gettop(L);
+	// State: object is at top of stack
+	// Get function
+	lua_getfield(L, -1, "on_rightclick");
+	if(lua_isnil(L, -1))
+		return;
+	luaL_checktype(L, -1, LUA_TFUNCTION);
+	lua_pushvalue(L, object); // self
+	objectref_get_or_create(L, clicker); // Clicker reference
+	// Call with 2 arguments, 0 results
+	if(lua_pcall(L, 2, 0, 0))
+		script_error(L, "error running function 'on_rightclick': %s\n", lua_tostring(L, -1));
+}
diff --git a/src/scriptapi_entity.h b/src/scriptapi_entity.h
new file mode 100644
index 0000000000000000000000000000000000000000..e0874325899dc846920c76f119df6715b4bf7e5a
--- /dev/null
+++ b/src/scriptapi_entity.h
@@ -0,0 +1,54 @@
+/*
+Minetest-c55
+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 LUA_ENTITY_H_
+#define LUA_ENTITY_H_
+
+extern "C" {
+#include <lua.h>
+}
+
+#include "object_properties.h"
+#include "content_sao.h"
+#include "tool.h"
+
+/*****************************************************************************/
+/* scriptapi internal                                                        */
+/*****************************************************************************/
+void luaentity_get(lua_State *L, u16 id);
+
+/*****************************************************************************/
+/* Minetest interface                                                        */
+/*****************************************************************************/
+// Returns true if succesfully added into Lua; false otherwise.
+bool scriptapi_luaentity_add(lua_State *L, u16 id, const char *name);
+void scriptapi_luaentity_activate(lua_State *L, u16 id,
+		const std::string &staticdata, u32 dtime_s);
+void scriptapi_luaentity_rm(lua_State *L, u16 id);
+std::string scriptapi_luaentity_get_staticdata(lua_State *L, u16 id);
+void scriptapi_luaentity_get_properties(lua_State *L, u16 id,
+		ObjectProperties *prop);
+void scriptapi_luaentity_step(lua_State *L, u16 id, float dtime);
+void scriptapi_luaentity_punch(lua_State *L, u16 id,
+		ServerActiveObject *puncher, float time_from_last_punch,
+		const ToolCapabilities *toolcap, v3f dir);
+void scriptapi_luaentity_rightclick(lua_State *L, u16 id,
+		ServerActiveObject *clicker);
+
+#endif /* LUA_ENTITY_H_ */
diff --git a/src/scriptapi_env.cpp b/src/scriptapi_env.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..4e068e37754f35856690244391530efdd707625b
--- /dev/null
+++ b/src/scriptapi_env.cpp
@@ -0,0 +1,908 @@
+/*
+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 "scriptapi.h"
+#include "scriptapi_env.h"
+#include "nodedef.h"
+#include "gamedef.h"
+#include "map.h"
+#include "daynightratio.h"
+#include "content_sao.h"
+#include "script.h"
+#include "treegen.h"
+#include "util/pointedthing.h"
+#include "scriptapi_types.h"
+#include "scriptapi_noise.h"
+#include "scriptapi_nodemeta.h"
+#include "scriptapi_nodetimer.h"
+#include "scriptapi_object.h"
+#include "scriptapi_common.h"
+#include "scriptapi_item.h"
+#include "scriptapi_node.h"
+
+
+//TODO
+extern void scriptapi_run_callbacks(lua_State *L, int nargs,
+		RunCallbacksMode mode);
+
+
+class LuaABM : public ActiveBlockModifier
+{
+private:
+	lua_State *m_lua;
+	int m_id;
+
+	std::set<std::string> m_trigger_contents;
+	std::set<std::string> m_required_neighbors;
+	float m_trigger_interval;
+	u32 m_trigger_chance;
+public:
+	LuaABM(lua_State *L, int id,
+			const std::set<std::string> &trigger_contents,
+			const std::set<std::string> &required_neighbors,
+			float trigger_interval, u32 trigger_chance):
+		m_lua(L),
+		m_id(id),
+		m_trigger_contents(trigger_contents),
+		m_required_neighbors(required_neighbors),
+		m_trigger_interval(trigger_interval),
+		m_trigger_chance(trigger_chance)
+	{
+	}
+	virtual std::set<std::string> getTriggerContents()
+	{
+		return m_trigger_contents;
+	}
+	virtual std::set<std::string> getRequiredNeighbors()
+	{
+		return m_required_neighbors;
+	}
+	virtual float getTriggerInterval()
+	{
+		return m_trigger_interval;
+	}
+	virtual u32 getTriggerChance()
+	{
+		return m_trigger_chance;
+	}
+	virtual void trigger(ServerEnvironment *env, v3s16 p, MapNode n,
+			u32 active_object_count, u32 active_object_count_wider)
+	{
+		lua_State *L = m_lua;
+
+		realitycheck(L);
+		assert(lua_checkstack(L, 20));
+		StackUnroller stack_unroller(L);
+
+		// Get minetest.registered_abms
+		lua_getglobal(L, "minetest");
+		lua_getfield(L, -1, "registered_abms");
+		luaL_checktype(L, -1, LUA_TTABLE);
+		int registered_abms = lua_gettop(L);
+
+		// Get minetest.registered_abms[m_id]
+		lua_pushnumber(L, m_id);
+		lua_gettable(L, registered_abms);
+		if(lua_isnil(L, -1))
+			assert(0);
+
+		// Call action
+		luaL_checktype(L, -1, LUA_TTABLE);
+		lua_getfield(L, -1, "action");
+		luaL_checktype(L, -1, LUA_TFUNCTION);
+		push_v3s16(L, p);
+		pushnode(L, n, env->getGameDef()->ndef());
+		lua_pushnumber(L, active_object_count);
+		lua_pushnumber(L, active_object_count_wider);
+		if(lua_pcall(L, 4, 0, 0))
+			script_error(L, "error: %s", lua_tostring(L, -1));
+	}
+};
+
+/*
+	EnvRef
+*/
+
+int EnvRef::gc_object(lua_State *L) {
+	EnvRef *o = *(EnvRef **)(lua_touserdata(L, 1));
+	delete o;
+	return 0;
+}
+
+EnvRef* EnvRef::checkobject(lua_State *L, int narg)
+{
+	luaL_checktype(L, narg, LUA_TUSERDATA);
+	void *ud = luaL_checkudata(L, narg, className);
+	if(!ud) luaL_typerror(L, narg, className);
+	return *(EnvRef**)ud;  // unbox pointer
+}
+
+// Exported functions
+
+// EnvRef:set_node(pos, node)
+// pos = {x=num, y=num, z=num}
+int EnvRef::l_set_node(lua_State *L)
+{
+	EnvRef *o = checkobject(L, 1);
+	ServerEnvironment *env = o->m_env;
+	if(env == NULL) return 0;
+	INodeDefManager *ndef = env->getGameDef()->ndef();
+	// parameters
+	v3s16 pos = read_v3s16(L, 2);
+	MapNode n = readnode(L, 3, ndef);
+	// Do it
+	bool succeeded = env->setNode(pos, n);
+	lua_pushboolean(L, succeeded);
+	return 1;
+}
+
+int EnvRef::l_add_node(lua_State *L)
+{
+	return l_set_node(L);
+}
+
+// EnvRef:remove_node(pos)
+// pos = {x=num, y=num, z=num}
+int EnvRef::l_remove_node(lua_State *L)
+{
+	EnvRef *o = checkobject(L, 1);
+	ServerEnvironment *env = o->m_env;
+	if(env == NULL) return 0;
+
+	// parameters
+	v3s16 pos = read_v3s16(L, 2);
+	// Do it
+	bool succeeded = env->removeNode(pos);
+	lua_pushboolean(L, succeeded);
+	return 1;
+}
+
+// EnvRef:get_node(pos)
+// pos = {x=num, y=num, z=num}
+int EnvRef::l_get_node(lua_State *L)
+{
+	EnvRef *o = checkobject(L, 1);
+	ServerEnvironment *env = o->m_env;
+	if(env == NULL) return 0;
+	// pos
+	v3s16 pos = read_v3s16(L, 2);
+	// Do it
+	MapNode n = env->getMap().getNodeNoEx(pos);
+	// Return node
+	pushnode(L, n, env->getGameDef()->ndef());
+	return 1;
+}
+
+// EnvRef:get_node_or_nil(pos)
+// pos = {x=num, y=num, z=num}
+int EnvRef::l_get_node_or_nil(lua_State *L)
+{
+	EnvRef *o = checkobject(L, 1);
+	ServerEnvironment *env = o->m_env;
+	if(env == NULL) return 0;
+	// pos
+	v3s16 pos = read_v3s16(L, 2);
+	// Do it
+	try{
+		MapNode n = env->getMap().getNode(pos);
+		// Return node
+		pushnode(L, n, env->getGameDef()->ndef());
+		return 1;
+	} catch(InvalidPositionException &e)
+	{
+		lua_pushnil(L);
+		return 1;
+	}
+}
+
+// EnvRef:get_node_light(pos, timeofday)
+// pos = {x=num, y=num, z=num}
+// timeofday: nil = current time, 0 = night, 0.5 = day
+int EnvRef::l_get_node_light(lua_State *L)
+{
+	EnvRef *o = checkobject(L, 1);
+	ServerEnvironment *env = o->m_env;
+	if(env == NULL) return 0;
+	// Do it
+	v3s16 pos = read_v3s16(L, 2);
+	u32 time_of_day = env->getTimeOfDay();
+	if(lua_isnumber(L, 3))
+		time_of_day = 24000.0 * lua_tonumber(L, 3);
+	time_of_day %= 24000;
+	u32 dnr = time_to_daynight_ratio(time_of_day, true);
+	MapNode n = env->getMap().getNodeNoEx(pos);
+	try{
+		MapNode n = env->getMap().getNode(pos);
+		INodeDefManager *ndef = env->getGameDef()->ndef();
+		lua_pushinteger(L, n.getLightBlend(dnr, ndef));
+		return 1;
+	} catch(InvalidPositionException &e)
+	{
+		lua_pushnil(L);
+		return 1;
+	}
+}
+
+// EnvRef:place_node(pos, node)
+// pos = {x=num, y=num, z=num}
+int EnvRef::l_place_node(lua_State *L)
+{
+	EnvRef *o = checkobject(L, 1);
+	ServerEnvironment *env = o->m_env;
+	if(env == NULL) return 0;
+	v3s16 pos = read_v3s16(L, 2);
+	MapNode n = readnode(L, 3, env->getGameDef()->ndef());
+
+	// Don't attempt to load non-loaded area as of now
+	MapNode n_old = env->getMap().getNodeNoEx(pos);
+	if(n_old.getContent() == CONTENT_IGNORE){
+		lua_pushboolean(L, false);
+		return 1;
+	}
+	// Create item to place
+	INodeDefManager *ndef = get_server(L)->ndef();
+	IItemDefManager *idef = get_server(L)->idef();
+	ItemStack item(ndef->get(n).name, 1, 0, "", idef);
+	// Make pointed position
+	PointedThing pointed;
+	pointed.type = POINTEDTHING_NODE;
+	pointed.node_abovesurface = pos;
+	pointed.node_undersurface = pos + v3s16(0,-1,0);
+	// Place it with a NULL placer (appears in Lua as a non-functional
+	// ObjectRef)
+	bool success = scriptapi_item_on_place(L, item, NULL, pointed);
+	lua_pushboolean(L, success);
+	return 1;
+}
+
+// EnvRef:dig_node(pos)
+// pos = {x=num, y=num, z=num}
+int EnvRef::l_dig_node(lua_State *L)
+{
+	EnvRef *o = checkobject(L, 1);
+	ServerEnvironment *env = o->m_env;
+	if(env == NULL) return 0;
+	v3s16 pos = read_v3s16(L, 2);
+
+	// Don't attempt to load non-loaded area as of now
+	MapNode n = env->getMap().getNodeNoEx(pos);
+	if(n.getContent() == CONTENT_IGNORE){
+		lua_pushboolean(L, false);
+		return 1;
+	}
+	// Dig it out with a NULL digger (appears in Lua as a
+	// non-functional ObjectRef)
+	bool success = scriptapi_node_on_dig(L, pos, n, NULL);
+	lua_pushboolean(L, success);
+	return 1;
+}
+
+// EnvRef:punch_node(pos)
+// pos = {x=num, y=num, z=num}
+int EnvRef::l_punch_node(lua_State *L)
+{
+	EnvRef *o = checkobject(L, 1);
+	ServerEnvironment *env = o->m_env;
+	if(env == NULL) return 0;
+	v3s16 pos = read_v3s16(L, 2);
+
+	// Don't attempt to load non-loaded area as of now
+	MapNode n = env->getMap().getNodeNoEx(pos);
+	if(n.getContent() == CONTENT_IGNORE){
+		lua_pushboolean(L, false);
+		return 1;
+	}
+	// Punch it with a NULL puncher (appears in Lua as a non-functional
+	// ObjectRef)
+	bool success = scriptapi_node_on_punch(L, pos, n, NULL);
+	lua_pushboolean(L, success);
+	return 1;
+}
+
+// EnvRef:get_meta(pos)
+int EnvRef::l_get_meta(lua_State *L)
+{
+	//infostream<<"EnvRef::l_get_meta()"<<std::endl;
+	EnvRef *o = checkobject(L, 1);
+	ServerEnvironment *env = o->m_env;
+	if(env == NULL) return 0;
+	// Do it
+	v3s16 p = read_v3s16(L, 2);
+	NodeMetaRef::create(L, p, env);
+	return 1;
+}
+
+// EnvRef:get_node_timer(pos)
+int EnvRef::l_get_node_timer(lua_State *L)
+{
+	EnvRef *o = checkobject(L, 1);
+	ServerEnvironment *env = o->m_env;
+	if(env == NULL) return 0;
+	// Do it
+	v3s16 p = read_v3s16(L, 2);
+	NodeTimerRef::create(L, p, env);
+	return 1;
+}
+
+// EnvRef:add_entity(pos, entityname) -> ObjectRef or nil
+// pos = {x=num, y=num, z=num}
+int EnvRef::l_add_entity(lua_State *L)
+{
+	//infostream<<"EnvRef::l_add_entity()"<<std::endl;
+	EnvRef *o = checkobject(L, 1);
+	ServerEnvironment *env = o->m_env;
+	if(env == NULL) return 0;
+	// pos
+	v3f pos = checkFloatPos(L, 2);
+	// content
+	const char *name = luaL_checkstring(L, 3);
+	// Do it
+	ServerActiveObject *obj = new LuaEntitySAO(env, pos, name, "");
+	int objectid = env->addActiveObject(obj);
+	// If failed to add, return nothing (reads as nil)
+	if(objectid == 0)
+		return 0;
+	// Return ObjectRef
+	objectref_get_or_create(L, obj);
+	return 1;
+}
+
+// EnvRef:add_item(pos, itemstack or itemstring or table) -> ObjectRef or nil
+// pos = {x=num, y=num, z=num}
+int EnvRef::l_add_item(lua_State *L)
+{
+	//infostream<<"EnvRef::l_add_item()"<<std::endl;
+	EnvRef *o = checkobject(L, 1);
+	ServerEnvironment *env = o->m_env;
+	if(env == NULL) return 0;
+	// pos
+	v3f pos = checkFloatPos(L, 2);
+	// item
+	ItemStack item = read_item(L, 3);
+	if(item.empty() || !item.isKnown(get_server(L)->idef()))
+		return 0;
+	// Use minetest.spawn_item to spawn a __builtin:item
+	lua_getglobal(L, "minetest");
+	lua_getfield(L, -1, "spawn_item");
+	if(lua_isnil(L, -1))
+		return 0;
+	lua_pushvalue(L, 2);
+	lua_pushstring(L, item.getItemString().c_str());
+	if(lua_pcall(L, 2, 1, 0))
+		script_error(L, "error: %s", lua_tostring(L, -1));
+	return 1;
+	/*lua_pushvalue(L, 1);
+	lua_pushstring(L, "__builtin:item");
+	lua_pushstring(L, item.getItemString().c_str());
+	return l_add_entity(L);*/
+	/*// Do it
+	ServerActiveObject *obj = createItemSAO(env, pos, item.getItemString());
+	int objectid = env->addActiveObject(obj);
+	// If failed to add, return nothing (reads as nil)
+	if(objectid == 0)
+		return 0;
+	// Return ObjectRef
+	objectref_get_or_create(L, obj);
+	return 1;*/
+}
+
+// EnvRef:add_rat(pos)
+// pos = {x=num, y=num, z=num}
+int EnvRef::l_add_rat(lua_State *L)
+{
+	infostream<<"EnvRef::l_add_rat(): C++ mobs have been removed."
+			<<" Doing nothing."<<std::endl;
+	return 0;
+}
+
+// EnvRef:add_firefly(pos)
+// pos = {x=num, y=num, z=num}
+int EnvRef::l_add_firefly(lua_State *L)
+{
+	infostream<<"EnvRef::l_add_firefly(): C++ mobs have been removed."
+			<<" Doing nothing."<<std::endl;
+	return 0;
+}
+
+// EnvRef:get_player_by_name(name)
+int EnvRef::l_get_player_by_name(lua_State *L)
+{
+	EnvRef *o = checkobject(L, 1);
+	ServerEnvironment *env = o->m_env;
+	if(env == NULL) return 0;
+	// Do it
+	const char *name = luaL_checkstring(L, 2);
+	Player *player = env->getPlayer(name);
+	if(player == NULL){
+		lua_pushnil(L);
+		return 1;
+	}
+	PlayerSAO *sao = player->getPlayerSAO();
+	if(sao == NULL){
+		lua_pushnil(L);
+		return 1;
+	}
+	// Put player on stack
+	objectref_get_or_create(L, sao);
+	return 1;
+}
+
+// EnvRef:get_objects_inside_radius(pos, radius)
+int EnvRef::l_get_objects_inside_radius(lua_State *L)
+{
+	// Get the table insert function
+	lua_getglobal(L, "table");
+	lua_getfield(L, -1, "insert");
+	int table_insert = lua_gettop(L);
+	// Get environemnt
+	EnvRef *o = checkobject(L, 1);
+	ServerEnvironment *env = o->m_env;
+	if(env == NULL) return 0;
+	// Do it
+	v3f pos = checkFloatPos(L, 2);
+	float radius = luaL_checknumber(L, 3) * BS;
+	std::set<u16> ids = env->getObjectsInsideRadius(pos, radius);
+	lua_newtable(L);
+	int table = lua_gettop(L);
+	for(std::set<u16>::const_iterator
+			i = ids.begin(); i != ids.end(); i++){
+		ServerActiveObject *obj = env->getActiveObject(*i);
+		// Insert object reference into table
+		lua_pushvalue(L, table_insert);
+		lua_pushvalue(L, table);
+		objectref_get_or_create(L, obj);
+		if(lua_pcall(L, 2, 0, 0))
+			script_error(L, "error: %s", lua_tostring(L, -1));
+	}
+	return 1;
+}
+
+// EnvRef:set_timeofday(val)
+// val = 0...1
+int EnvRef::l_set_timeofday(lua_State *L)
+{
+	EnvRef *o = checkobject(L, 1);
+	ServerEnvironment *env = o->m_env;
+	if(env == NULL) return 0;
+	// Do it
+	float timeofday_f = luaL_checknumber(L, 2);
+	assert(timeofday_f >= 0.0 && timeofday_f <= 1.0);
+	int timeofday_mh = (int)(timeofday_f * 24000.0);
+	// This should be set directly in the environment but currently
+	// such changes aren't immediately sent to the clients, so call
+	// the server instead.
+	//env->setTimeOfDay(timeofday_mh);
+	get_server(L)->setTimeOfDay(timeofday_mh);
+	return 0;
+}
+
+// EnvRef:get_timeofday() -> 0...1
+int EnvRef::l_get_timeofday(lua_State *L)
+{
+	EnvRef *o = checkobject(L, 1);
+	ServerEnvironment *env = o->m_env;
+	if(env == NULL) return 0;
+	// Do it
+	int timeofday_mh = env->getTimeOfDay();
+	float timeofday_f = (float)timeofday_mh / 24000.0;
+	lua_pushnumber(L, timeofday_f);
+	return 1;
+}
+
+
+// EnvRef:find_node_near(pos, radius, nodenames) -> pos or nil
+// nodenames: eg. {"ignore", "group:tree"} or "default:dirt"
+int EnvRef::l_find_node_near(lua_State *L)
+{
+	EnvRef *o = checkobject(L, 1);
+	ServerEnvironment *env = o->m_env;
+	if(env == NULL) return 0;
+	INodeDefManager *ndef = get_server(L)->ndef();
+	v3s16 pos = read_v3s16(L, 2);
+	int radius = luaL_checkinteger(L, 3);
+	std::set<content_t> filter;
+	if(lua_istable(L, 4)){
+		int table = 4;
+		lua_pushnil(L);
+		while(lua_next(L, table) != 0){
+			// key at index -2 and value at index -1
+			luaL_checktype(L, -1, LUA_TSTRING);
+			ndef->getIds(lua_tostring(L, -1), filter);
+			// removes value, keeps key for next iteration
+			lua_pop(L, 1);
+		}
+	} else if(lua_isstring(L, 4)){
+		ndef->getIds(lua_tostring(L, 4), filter);
+	}
+
+	for(int d=1; d<=radius; d++){
+		std::list<v3s16> list;
+		getFacePositions(list, d);
+		for(std::list<v3s16>::iterator i = list.begin();
+				i != list.end(); ++i){
+			v3s16 p = pos + (*i);
+			content_t c = env->getMap().getNodeNoEx(p).getContent();
+			if(filter.count(c) != 0){
+				push_v3s16(L, p);
+				return 1;
+			}
+		}
+	}
+	return 0;
+}
+
+// EnvRef:find_nodes_in_area(minp, maxp, nodenames) -> list of positions
+// nodenames: eg. {"ignore", "group:tree"} or "default:dirt"
+int EnvRef::l_find_nodes_in_area(lua_State *L)
+{
+	EnvRef *o = checkobject(L, 1);
+	ServerEnvironment *env = o->m_env;
+	if(env == NULL) return 0;
+	INodeDefManager *ndef = get_server(L)->ndef();
+	v3s16 minp = read_v3s16(L, 2);
+	v3s16 maxp = read_v3s16(L, 3);
+	std::set<content_t> filter;
+	if(lua_istable(L, 4)){
+		int table = 4;
+		lua_pushnil(L);
+		while(lua_next(L, table) != 0){
+			// key at index -2 and value at index -1
+			luaL_checktype(L, -1, LUA_TSTRING);
+			ndef->getIds(lua_tostring(L, -1), filter);
+			// removes value, keeps key for next iteration
+			lua_pop(L, 1);
+		}
+	} else if(lua_isstring(L, 4)){
+		ndef->getIds(lua_tostring(L, 4), filter);
+	}
+
+	// Get the table insert function
+	lua_getglobal(L, "table");
+	lua_getfield(L, -1, "insert");
+	int table_insert = lua_gettop(L);
+
+	lua_newtable(L);
+	int table = lua_gettop(L);
+	for(s16 x=minp.X; x<=maxp.X; x++)
+	for(s16 y=minp.Y; y<=maxp.Y; y++)
+	for(s16 z=minp.Z; z<=maxp.Z; z++)
+	{
+		v3s16 p(x,y,z);
+		content_t c = env->getMap().getNodeNoEx(p).getContent();
+		if(filter.count(c) != 0){
+			lua_pushvalue(L, table_insert);
+			lua_pushvalue(L, table);
+			push_v3s16(L, p);
+			if(lua_pcall(L, 2, 0, 0))
+				script_error(L, "error: %s", lua_tostring(L, -1));
+		}
+	}
+	return 1;
+}
+
+//	EnvRef:get_perlin(seeddiff, octaves, persistence, scale)
+//  returns world-specific PerlinNoise
+int EnvRef::l_get_perlin(lua_State *L)
+{
+	EnvRef *o = checkobject(L, 1);
+	ServerEnvironment *env = o->m_env;
+	if(env == NULL) return 0;
+
+	int seeddiff = luaL_checkint(L, 2);
+	int octaves = luaL_checkint(L, 3);
+	float persistence = luaL_checknumber(L, 4);
+	float scale = luaL_checknumber(L, 5);
+
+	LuaPerlinNoise *n = new LuaPerlinNoise(seeddiff + int(env->getServerMap().getSeed()), octaves, persistence, scale);
+	*(void **)(lua_newuserdata(L, sizeof(void *))) = n;
+	luaL_getmetatable(L, "PerlinNoise");
+	lua_setmetatable(L, -2);
+	return 1;
+}
+
+//  EnvRef:get_perlin_map(noiseparams, size)
+//  returns world-specific PerlinNoiseMap
+int EnvRef::l_get_perlin_map(lua_State *L)
+{
+	EnvRef *o = checkobject(L, 1);
+	ServerEnvironment *env = o->m_env;
+	if (env == NULL)
+		return 0;
+
+	NoiseParams *np = read_noiseparams(L, 2);
+	if (!np)
+		return 0;
+	v3s16 size = read_v3s16(L, 3);
+
+	int seed = (int)(env->getServerMap().getSeed());
+	LuaPerlinNoiseMap *n = new LuaPerlinNoiseMap(np, seed, size);
+	*(void **)(lua_newuserdata(L, sizeof(void *))) = n;
+	luaL_getmetatable(L, "PerlinNoiseMap");
+	lua_setmetatable(L, -2);
+	return 1;
+}
+
+// EnvRef:clear_objects()
+// clear all objects in the environment
+int EnvRef::l_clear_objects(lua_State *L)
+{
+	EnvRef *o = checkobject(L, 1);
+	o->m_env->clearAllObjects();
+	return 0;
+}
+
+int EnvRef::l_spawn_tree(lua_State *L)
+{
+	EnvRef *o = checkobject(L, 1);
+	ServerEnvironment *env = o->m_env;
+	if(env == NULL) return 0;
+	v3s16 p0 = read_v3s16(L, 2);
+
+	treegen::TreeDef tree_def;
+	std::string trunk,leaves,fruit;
+	INodeDefManager *ndef = env->getGameDef()->ndef();
+
+	if(lua_istable(L, 3))
+	{
+		getstringfield(L, 3, "axiom", tree_def.initial_axiom);
+		getstringfield(L, 3, "rules_a", tree_def.rules_a);
+		getstringfield(L, 3, "rules_b", tree_def.rules_b);
+		getstringfield(L, 3, "rules_c", tree_def.rules_c);
+		getstringfield(L, 3, "rules_d", tree_def.rules_d);
+		getstringfield(L, 3, "trunk", trunk);
+		tree_def.trunknode=ndef->getId(trunk);
+		getstringfield(L, 3, "leaves", leaves);
+		tree_def.leavesnode=ndef->getId(leaves);
+		tree_def.leaves2_chance=0;
+		getstringfield(L, 3, "leaves2", leaves);
+		if (leaves !="")
+		{
+			tree_def.leaves2node=ndef->getId(leaves);
+			getintfield(L, 3, "leaves2_chance", tree_def.leaves2_chance);
+		}
+		getintfield(L, 3, "angle", tree_def.angle);
+		getintfield(L, 3, "iterations", tree_def.iterations);
+		getintfield(L, 3, "random_level", tree_def.iterations_random_level);
+		getstringfield(L, 3, "trunk_type", tree_def.trunk_type);
+		getboolfield(L, 3, "thin_branches", tree_def.thin_branches);
+		tree_def.fruit_chance=0;
+		getstringfield(L, 3, "fruit", fruit);
+		if (fruit != "")
+		{
+			tree_def.fruitnode=ndef->getId(fruit);
+			getintfield(L, 3, "fruit_chance",tree_def.fruit_chance);
+		}
+		getintfield(L, 3, "seed", tree_def.seed);
+	}
+	else
+		return 0;
+	treegen::spawn_ltree (env, p0, ndef, tree_def);
+	return 1;
+}
+
+
+EnvRef::EnvRef(ServerEnvironment *env):
+	m_env(env)
+{
+	//infostream<<"EnvRef created"<<std::endl;
+}
+
+EnvRef::~EnvRef()
+{
+	//infostream<<"EnvRef destructing"<<std::endl;
+}
+
+// Creates an EnvRef and leaves it on top of stack
+// Not callable from Lua; all references are created on the C side.
+void EnvRef::create(lua_State *L, ServerEnvironment *env)
+{
+	EnvRef *o = new EnvRef(env);
+	//infostream<<"EnvRef::create: o="<<o<<std::endl;
+	*(void **)(lua_newuserdata(L, sizeof(void *))) = o;
+	luaL_getmetatable(L, className);
+	lua_setmetatable(L, -2);
+}
+
+void EnvRef::set_null(lua_State *L)
+{
+	EnvRef *o = checkobject(L, -1);
+	o->m_env = NULL;
+}
+
+void EnvRef::Register(lua_State *L)
+{
+	lua_newtable(L);
+	int methodtable = lua_gettop(L);
+	luaL_newmetatable(L, className);
+	int metatable = lua_gettop(L);
+
+	lua_pushliteral(L, "__metatable");
+	lua_pushvalue(L, methodtable);
+	lua_settable(L, metatable);  // hide metatable from Lua getmetatable()
+
+	lua_pushliteral(L, "__index");
+	lua_pushvalue(L, methodtable);
+	lua_settable(L, metatable);
+
+	lua_pushliteral(L, "__gc");
+	lua_pushcfunction(L, gc_object);
+	lua_settable(L, metatable);
+
+	lua_pop(L, 1);  // drop metatable
+
+	luaL_openlib(L, 0, methods, 0);  // fill methodtable
+	lua_pop(L, 1);  // drop methodtable
+
+	// Cannot be created from Lua
+	//lua_register(L, className, create_object);
+}
+
+const char EnvRef::className[] = "EnvRef";
+const luaL_reg EnvRef::methods[] = {
+	luamethod(EnvRef, set_node),
+	luamethod(EnvRef, add_node),
+	luamethod(EnvRef, remove_node),
+	luamethod(EnvRef, get_node),
+	luamethod(EnvRef, get_node_or_nil),
+	luamethod(EnvRef, get_node_light),
+	luamethod(EnvRef, place_node),
+	luamethod(EnvRef, dig_node),
+	luamethod(EnvRef, punch_node),
+	luamethod(EnvRef, add_entity),
+	luamethod(EnvRef, add_item),
+	luamethod(EnvRef, add_rat),
+	luamethod(EnvRef, add_firefly),
+	luamethod(EnvRef, get_meta),
+	luamethod(EnvRef, get_node_timer),
+	luamethod(EnvRef, get_player_by_name),
+	luamethod(EnvRef, get_objects_inside_radius),
+	luamethod(EnvRef, set_timeofday),
+	luamethod(EnvRef, get_timeofday),
+	luamethod(EnvRef, find_node_near),
+	luamethod(EnvRef, find_nodes_in_area),
+	luamethod(EnvRef, get_perlin),
+	luamethod(EnvRef, get_perlin_map),
+	luamethod(EnvRef, clear_objects),
+	luamethod(EnvRef, spawn_tree),
+	{0,0}
+};
+
+void scriptapi_environment_on_generated(lua_State *L, v3s16 minp, v3s16 maxp,
+		u32 blockseed)
+{
+	realitycheck(L);
+	assert(lua_checkstack(L, 20));
+	//infostream<<"scriptapi_environment_on_generated"<<std::endl;
+	StackUnroller stack_unroller(L);
+
+	// Get minetest.registered_on_generateds
+	lua_getglobal(L, "minetest");
+	lua_getfield(L, -1, "registered_on_generateds");
+	// Call callbacks
+	push_v3s16(L, minp);
+	push_v3s16(L, maxp);
+	lua_pushnumber(L, blockseed);
+	scriptapi_run_callbacks(L, 3, RUN_CALLBACKS_MODE_FIRST);
+}
+
+void scriptapi_environment_step(lua_State *L, float dtime)
+{
+	realitycheck(L);
+	assert(lua_checkstack(L, 20));
+	//infostream<<"scriptapi_environment_step"<<std::endl;
+	StackUnroller stack_unroller(L);
+
+	// Get minetest.registered_globalsteps
+	lua_getglobal(L, "minetest");
+	lua_getfield(L, -1, "registered_globalsteps");
+	// Call callbacks
+	lua_pushnumber(L, dtime);
+	scriptapi_run_callbacks(L, 1, RUN_CALLBACKS_MODE_FIRST);
+}
+
+void scriptapi_add_environment(lua_State *L, ServerEnvironment *env)
+{
+	realitycheck(L);
+	assert(lua_checkstack(L, 20));
+	verbosestream<<"scriptapi_add_environment"<<std::endl;
+	StackUnroller stack_unroller(L);
+
+	// Create EnvRef on stack
+	EnvRef::create(L, env);
+	int envref = lua_gettop(L);
+
+	// minetest.env = envref
+	lua_getglobal(L, "minetest");
+	luaL_checktype(L, -1, LUA_TTABLE);
+	lua_pushvalue(L, envref);
+	lua_setfield(L, -2, "env");
+
+	// Store environment as light userdata in registry
+	lua_pushlightuserdata(L, env);
+	lua_setfield(L, LUA_REGISTRYINDEX, "minetest_env");
+
+	/*
+		Add ActiveBlockModifiers to environment
+	*/
+
+	// Get minetest.registered_abms
+	lua_getglobal(L, "minetest");
+	lua_getfield(L, -1, "registered_abms");
+	luaL_checktype(L, -1, LUA_TTABLE);
+	int registered_abms = lua_gettop(L);
+
+	if(lua_istable(L, registered_abms)){
+		int table = lua_gettop(L);
+		lua_pushnil(L);
+		while(lua_next(L, table) != 0){
+			// key at index -2 and value at index -1
+			int id = lua_tonumber(L, -2);
+			int current_abm = lua_gettop(L);
+
+			std::set<std::string> trigger_contents;
+			lua_getfield(L, current_abm, "nodenames");
+			if(lua_istable(L, -1)){
+				int table = lua_gettop(L);
+				lua_pushnil(L);
+				while(lua_next(L, table) != 0){
+					// key at index -2 and value at index -1
+					luaL_checktype(L, -1, LUA_TSTRING);
+					trigger_contents.insert(lua_tostring(L, -1));
+					// removes value, keeps key for next iteration
+					lua_pop(L, 1);
+				}
+			} else if(lua_isstring(L, -1)){
+				trigger_contents.insert(lua_tostring(L, -1));
+			}
+			lua_pop(L, 1);
+
+			std::set<std::string> required_neighbors;
+			lua_getfield(L, current_abm, "neighbors");
+			if(lua_istable(L, -1)){
+				int table = lua_gettop(L);
+				lua_pushnil(L);
+				while(lua_next(L, table) != 0){
+					// key at index -2 and value at index -1
+					luaL_checktype(L, -1, LUA_TSTRING);
+					required_neighbors.insert(lua_tostring(L, -1));
+					// removes value, keeps key for next iteration
+					lua_pop(L, 1);
+				}
+			} else if(lua_isstring(L, -1)){
+				required_neighbors.insert(lua_tostring(L, -1));
+			}
+			lua_pop(L, 1);
+
+			float trigger_interval = 10.0;
+			getfloatfield(L, current_abm, "interval", trigger_interval);
+
+			int trigger_chance = 50;
+			getintfield(L, current_abm, "chance", trigger_chance);
+
+			LuaABM *abm = new LuaABM(L, id, trigger_contents,
+					required_neighbors, trigger_interval, trigger_chance);
+
+			env->addActiveBlockModifier(abm);
+
+			// removes value, keeps key for next iteration
+			lua_pop(L, 1);
+		}
+	}
+	lua_pop(L, 1);
+}
diff --git a/src/scriptapi_env.h b/src/scriptapi_env.h
new file mode 100644
index 0000000000000000000000000000000000000000..1599969a4d18e7d31c779e723b8be6392103df07
--- /dev/null
+++ b/src/scriptapi_env.h
@@ -0,0 +1,164 @@
+/*
+Minetest-c55
+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 LUA_ENVIRONMENT_H_
+#define LUA_ENVIRONMENT_H_
+
+extern "C" {
+#include <lua.h>
+#include <lauxlib.h>
+}
+
+#include "environment.h"
+
+/*
+	EnvRef
+*/
+
+class EnvRef
+{
+private:
+	ServerEnvironment *m_env;
+
+	static const char className[];
+	static const luaL_reg methods[];
+
+	static int gc_object(lua_State *L) ;
+
+	static EnvRef *checkobject(lua_State *L, int narg);
+
+	// Exported functions
+
+	// EnvRef:set_node(pos, node)
+	// pos = {x=num, y=num, z=num}
+	static int l_set_node(lua_State *L);
+
+	static int l_add_node(lua_State *L);
+
+	// EnvRef:remove_node(pos)
+	// pos = {x=num, y=num, z=num}
+	static int l_remove_node(lua_State *L);
+
+	// EnvRef:get_node(pos)
+	// pos = {x=num, y=num, z=num}
+	static int l_get_node(lua_State *L);
+
+	// EnvRef:get_node_or_nil(pos)
+	// pos = {x=num, y=num, z=num}
+	static int l_get_node_or_nil(lua_State *L);
+
+	// EnvRef:get_node_light(pos, timeofday)
+	// pos = {x=num, y=num, z=num}
+	// timeofday: nil = current time, 0 = night, 0.5 = day
+	static int l_get_node_light(lua_State *L);
+
+	// EnvRef:place_node(pos, node)
+	// pos = {x=num, y=num, z=num}
+	static int l_place_node(lua_State *L);
+
+	// EnvRef:dig_node(pos)
+	// pos = {x=num, y=num, z=num}
+	static int l_dig_node(lua_State *L);
+
+	// EnvRef:punch_node(pos)
+	// pos = {x=num, y=num, z=num}
+	static int l_punch_node(lua_State *L);
+
+	// EnvRef:get_meta(pos)
+	static int l_get_meta(lua_State *L);
+
+	// EnvRef:get_node_timer(pos)
+	static int l_get_node_timer(lua_State *L);
+
+	// EnvRef:add_entity(pos, entityname) -> ObjectRef or nil
+	// pos = {x=num, y=num, z=num}
+	static int l_add_entity(lua_State *L);
+
+	// EnvRef:add_item(pos, itemstack or itemstring or table) -> ObjectRef or nil
+	// pos = {x=num, y=num, z=num}
+	static int l_add_item(lua_State *L);
+
+	// EnvRef:add_rat(pos)
+	// pos = {x=num, y=num, z=num}
+	static int l_add_rat(lua_State *L);
+
+	// EnvRef:add_firefly(pos)
+	// pos = {x=num, y=num, z=num}
+	static int l_add_firefly(lua_State *L);
+
+	// EnvRef:get_player_by_name(name)
+	static int l_get_player_by_name(lua_State *L);
+
+	// EnvRef:get_objects_inside_radius(pos, radius)
+	static int l_get_objects_inside_radius(lua_State *L);
+
+	// EnvRef:set_timeofday(val)
+	// val = 0...1
+	static int l_set_timeofday(lua_State *L);
+
+	// EnvRef:get_timeofday() -> 0...1
+	static int l_get_timeofday(lua_State *L);
+
+	// EnvRef:find_node_near(pos, radius, nodenames) -> pos or nil
+	// nodenames: eg. {"ignore", "group:tree"} or "default:dirt"
+	static int l_find_node_near(lua_State *L);
+
+	// EnvRef:find_nodes_in_area(minp, maxp, nodenames) -> list of positions
+	// nodenames: eg. {"ignore", "group:tree"} or "default:dirt"
+	static int l_find_nodes_in_area(lua_State *L);
+
+	//	EnvRef:get_perlin(seeddiff, octaves, persistence, scale)
+	//  returns world-specific PerlinNoise
+	static int l_get_perlin(lua_State *L);
+
+	//  EnvRef:get_perlin_map(noiseparams, size)
+	//  returns world-specific PerlinNoiseMap
+	static int l_get_perlin_map(lua_State *L);
+
+	// EnvRef:clear_objects()
+	// clear all objects in the environment
+	static int l_clear_objects(lua_State *L);
+
+	static int l_spawn_tree(lua_State *L);
+
+public:
+	EnvRef(ServerEnvironment *env);
+
+	~EnvRef();
+
+	// Creates an EnvRef and leaves it on top of stack
+	// Not callable from Lua; all references are created on the C side.
+	static void create(lua_State *L, ServerEnvironment *env);
+
+	static void set_null(lua_State *L);
+
+	static void Register(lua_State *L);
+};
+
+/*****************************************************************************/
+/* Minetest interface                                                        */
+/*****************************************************************************/
+// On environment step
+void scriptapi_environment_step(lua_State *L, float dtime);
+// After generating a piece of map
+void scriptapi_environment_on_generated(lua_State *L, v3s16 minp, v3s16 maxp,
+		u32 blockseed);
+void scriptapi_add_environment(lua_State *L, ServerEnvironment *env);
+
+#endif /* LUA_ENVIRONMENT_H_ */
diff --git a/src/scriptapi_inventory.cpp b/src/scriptapi_inventory.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..bb40748dbd622544b3e10fc56d6ee700dd019f4e
--- /dev/null
+++ b/src/scriptapi_inventory.cpp
@@ -0,0 +1,726 @@
+/*
+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 "scriptapi.h"
+#include "scriptapi_inventory.h"
+#include "server.h"
+#include "script.h"
+#include "log.h"
+#include "scriptapi_types.h"
+#include "scriptapi_common.h"
+#include "scriptapi_inventory.h"
+#include "scriptapi_item.h"
+#include "scriptapi_object.h"
+
+
+/*
+	InvRef
+*/
+InvRef* InvRef::checkobject(lua_State *L, int narg)
+{
+	luaL_checktype(L, narg, LUA_TUSERDATA);
+	void *ud = luaL_checkudata(L, narg, className);
+	if(!ud) luaL_typerror(L, narg, className);
+	return *(InvRef**)ud;  // unbox pointer
+}
+
+Inventory* InvRef::getinv(lua_State *L, InvRef *ref)
+{
+	return get_server(L)->getInventory(ref->m_loc);
+}
+
+InventoryList* InvRef::getlist(lua_State *L, InvRef *ref,
+		const char *listname)
+{
+	Inventory *inv = getinv(L, ref);
+	if(!inv)
+		return NULL;
+	return inv->getList(listname);
+}
+
+void InvRef::reportInventoryChange(lua_State *L, InvRef *ref)
+{
+	// Inform other things that the inventory has changed
+	get_server(L)->setInventoryModified(ref->m_loc);
+}
+
+// Exported functions
+
+// garbage collector
+int InvRef::gc_object(lua_State *L) {
+	InvRef *o = *(InvRef **)(lua_touserdata(L, 1));
+	delete o;
+	return 0;
+}
+
+// is_empty(self, listname) -> true/false
+int InvRef::l_is_empty(lua_State *L)
+{
+	InvRef *ref = checkobject(L, 1);
+	const char *listname = luaL_checkstring(L, 2);
+	InventoryList *list = getlist(L, ref, listname);
+	if(list && list->getUsedSlots() > 0){
+		lua_pushboolean(L, false);
+	} else {
+		lua_pushboolean(L, true);
+	}
+	return 1;
+}
+
+// get_size(self, listname)
+int InvRef::l_get_size(lua_State *L)
+{
+	InvRef *ref = checkobject(L, 1);
+	const char *listname = luaL_checkstring(L, 2);
+	InventoryList *list = getlist(L, ref, listname);
+	if(list){
+		lua_pushinteger(L, list->getSize());
+	} else {
+		lua_pushinteger(L, 0);
+	}
+	return 1;
+}
+
+// get_width(self, listname)
+int InvRef::l_get_width(lua_State *L)
+{
+	InvRef *ref = checkobject(L, 1);
+	const char *listname = luaL_checkstring(L, 2);
+	InventoryList *list = getlist(L, ref, listname);
+	if(list){
+		lua_pushinteger(L, list->getWidth());
+	} else {
+		lua_pushinteger(L, 0);
+	}
+	return 1;
+}
+
+// set_size(self, listname, size)
+int InvRef::l_set_size(lua_State *L)
+{
+	InvRef *ref = checkobject(L, 1);
+	const char *listname = luaL_checkstring(L, 2);
+	int newsize = luaL_checknumber(L, 3);
+	Inventory *inv = getinv(L, ref);
+	if(newsize == 0){
+		inv->deleteList(listname);
+		reportInventoryChange(L, ref);
+		return 0;
+	}
+	InventoryList *list = inv->getList(listname);
+	if(list){
+		list->setSize(newsize);
+	} else {
+		list = inv->addList(listname, newsize);
+	}
+	reportInventoryChange(L, ref);
+	return 0;
+}
+
+// set_width(self, listname, size)
+int InvRef::l_set_width(lua_State *L)
+{
+	InvRef *ref = checkobject(L, 1);
+	const char *listname = luaL_checkstring(L, 2);
+	int newwidth = luaL_checknumber(L, 3);
+	Inventory *inv = getinv(L, ref);
+	InventoryList *list = inv->getList(listname);
+	if(list){
+		list->setWidth(newwidth);
+	} else {
+		return 0;
+	}
+	reportInventoryChange(L, ref);
+	return 0;
+}
+
+// get_stack(self, listname, i) -> itemstack
+int InvRef::l_get_stack(lua_State *L)
+{
+	InvRef *ref = checkobject(L, 1);
+	const char *listname = luaL_checkstring(L, 2);
+	int i = luaL_checknumber(L, 3) - 1;
+	InventoryList *list = getlist(L, ref, listname);
+	ItemStack item;
+	if(list != NULL && i >= 0 && i < (int) list->getSize())
+		item = list->getItem(i);
+	LuaItemStack::create(L, item);
+	return 1;
+}
+
+// set_stack(self, listname, i, stack) -> true/false
+int InvRef::l_set_stack(lua_State *L)
+{
+	InvRef *ref = checkobject(L, 1);
+	const char *listname = luaL_checkstring(L, 2);
+	int i = luaL_checknumber(L, 3) - 1;
+	ItemStack newitem = read_item(L, 4);
+	InventoryList *list = getlist(L, ref, listname);
+	if(list != NULL && i >= 0 && i < (int) list->getSize()){
+		list->changeItem(i, newitem);
+		reportInventoryChange(L, ref);
+		lua_pushboolean(L, true);
+	} else {
+		lua_pushboolean(L, false);
+	}
+	return 1;
+}
+
+// get_list(self, listname) -> list or nil
+int InvRef::l_get_list(lua_State *L)
+{
+	InvRef *ref = checkobject(L, 1);
+	const char *listname = luaL_checkstring(L, 2);
+	Inventory *inv = getinv(L, ref);
+	inventory_get_list_to_lua(inv, listname, L);
+	return 1;
+}
+
+// set_list(self, listname, list)
+int InvRef::l_set_list(lua_State *L)
+{
+	InvRef *ref = checkobject(L, 1);
+	const char *listname = luaL_checkstring(L, 2);
+	Inventory *inv = getinv(L, ref);
+	InventoryList *list = inv->getList(listname);
+	if(list)
+		inventory_set_list_from_lua(inv, listname, L, 3,
+				list->getSize());
+	else
+		inventory_set_list_from_lua(inv, listname, L, 3);
+	reportInventoryChange(L, ref);
+	return 0;
+}
+
+// add_item(self, listname, itemstack or itemstring or table or nil) -> itemstack
+// Returns the leftover stack
+int InvRef::l_add_item(lua_State *L)
+{
+	InvRef *ref = checkobject(L, 1);
+	const char *listname = luaL_checkstring(L, 2);
+	ItemStack item = read_item(L, 3);
+	InventoryList *list = getlist(L, ref, listname);
+	if(list){
+		ItemStack leftover = list->addItem(item);
+		if(leftover.count != item.count)
+			reportInventoryChange(L, ref);
+		LuaItemStack::create(L, leftover);
+	} else {
+		LuaItemStack::create(L, item);
+	}
+	return 1;
+}
+
+// room_for_item(self, listname, itemstack or itemstring or table or nil) -> true/false
+// Returns true if the item completely fits into the list
+int InvRef::l_room_for_item(lua_State *L)
+{
+	InvRef *ref = checkobject(L, 1);
+	const char *listname = luaL_checkstring(L, 2);
+	ItemStack item = read_item(L, 3);
+	InventoryList *list = getlist(L, ref, listname);
+	if(list){
+		lua_pushboolean(L, list->roomForItem(item));
+	} else {
+		lua_pushboolean(L, false);
+	}
+	return 1;
+}
+
+// contains_item(self, listname, itemstack or itemstring or table or nil) -> true/false
+// Returns true if the list contains the given count of the given item name
+int InvRef::l_contains_item(lua_State *L)
+{
+	InvRef *ref = checkobject(L, 1);
+	const char *listname = luaL_checkstring(L, 2);
+	ItemStack item = read_item(L, 3);
+	InventoryList *list = getlist(L, ref, listname);
+	if(list){
+		lua_pushboolean(L, list->containsItem(item));
+	} else {
+		lua_pushboolean(L, false);
+	}
+	return 1;
+}
+
+// remove_item(self, listname, itemstack or itemstring or table or nil) -> itemstack
+// Returns the items that were actually removed
+int InvRef::l_remove_item(lua_State *L)
+{
+	InvRef *ref = checkobject(L, 1);
+	const char *listname = luaL_checkstring(L, 2);
+	ItemStack item = read_item(L, 3);
+	InventoryList *list = getlist(L, ref, listname);
+	if(list){
+		ItemStack removed = list->removeItem(item);
+		if(!removed.empty())
+			reportInventoryChange(L, ref);
+		LuaItemStack::create(L, removed);
+	} else {
+		LuaItemStack::create(L, ItemStack());
+	}
+	return 1;
+}
+
+// get_location() -> location (like minetest.get_inventory(location))
+int InvRef::l_get_location(lua_State *L)
+{
+	InvRef *ref = checkobject(L, 1);
+	const InventoryLocation &loc = ref->m_loc;
+	switch(loc.type){
+	case InventoryLocation::PLAYER:
+		lua_newtable(L);
+		lua_pushstring(L, "player");
+		lua_setfield(L, -2, "type");
+		lua_pushstring(L, loc.name.c_str());
+		lua_setfield(L, -2, "name");
+		return 1;
+	case InventoryLocation::NODEMETA:
+		lua_newtable(L);
+		lua_pushstring(L, "nodemeta");
+		lua_setfield(L, -2, "type");
+		push_v3s16(L, loc.p);
+		lua_setfield(L, -2, "name");
+		return 1;
+	case InventoryLocation::DETACHED:
+		lua_newtable(L);
+		lua_pushstring(L, "detached");
+		lua_setfield(L, -2, "type");
+		lua_pushstring(L, loc.name.c_str());
+		lua_setfield(L, -2, "name");
+		return 1;
+	case InventoryLocation::UNDEFINED:
+	case InventoryLocation::CURRENT_PLAYER:
+		break;
+	}
+	lua_newtable(L);
+	lua_pushstring(L, "undefined");
+	lua_setfield(L, -2, "type");
+	return 1;
+}
+
+
+InvRef::InvRef(const InventoryLocation &loc):
+	m_loc(loc)
+{
+}
+
+InvRef::~InvRef()
+{
+}
+
+// Creates an InvRef and leaves it on top of stack
+// Not callable from Lua; all references are created on the C side.
+void InvRef::create(lua_State *L, const InventoryLocation &loc)
+{
+	InvRef *o = new InvRef(loc);
+	*(void **)(lua_newuserdata(L, sizeof(void *))) = o;
+	luaL_getmetatable(L, className);
+	lua_setmetatable(L, -2);
+}
+void InvRef::createPlayer(lua_State *L, Player *player)
+{
+	InventoryLocation loc;
+	loc.setPlayer(player->getName());
+	create(L, loc);
+}
+void InvRef::createNodeMeta(lua_State *L, v3s16 p)
+{
+	InventoryLocation loc;
+	loc.setNodeMeta(p);
+	create(L, loc);
+}
+
+void InvRef::Register(lua_State *L)
+{
+	lua_newtable(L);
+	int methodtable = lua_gettop(L);
+	luaL_newmetatable(L, className);
+	int metatable = lua_gettop(L);
+
+	lua_pushliteral(L, "__metatable");
+	lua_pushvalue(L, methodtable);
+	lua_settable(L, metatable);  // hide metatable from Lua getmetatable()
+
+	lua_pushliteral(L, "__index");
+	lua_pushvalue(L, methodtable);
+	lua_settable(L, metatable);
+
+	lua_pushliteral(L, "__gc");
+	lua_pushcfunction(L, gc_object);
+	lua_settable(L, metatable);
+
+	lua_pop(L, 1);  // drop metatable
+
+	luaL_openlib(L, 0, methods, 0);  // fill methodtable
+	lua_pop(L, 1);  // drop methodtable
+
+	// Cannot be created from Lua
+	//lua_register(L, className, create_object);
+}
+
+const char InvRef::className[] = "InvRef";
+const luaL_reg InvRef::methods[] = {
+	luamethod(InvRef, is_empty),
+	luamethod(InvRef, get_size),
+	luamethod(InvRef, set_size),
+	luamethod(InvRef, get_width),
+	luamethod(InvRef, set_width),
+	luamethod(InvRef, get_stack),
+	luamethod(InvRef, set_stack),
+	luamethod(InvRef, get_list),
+	luamethod(InvRef, set_list),
+	luamethod(InvRef, add_item),
+	luamethod(InvRef, room_for_item),
+	luamethod(InvRef, contains_item),
+	luamethod(InvRef, remove_item),
+	luamethod(InvRef, get_location),
+	{0,0}
+};
+
+void inventory_get_list_to_lua(Inventory *inv, const char *name,
+		lua_State *L)
+{
+	InventoryList *invlist = inv->getList(name);
+	if(invlist == NULL){
+		lua_pushnil(L);
+		return;
+	}
+	std::vector<ItemStack> items;
+	for(u32 i=0; i<invlist->getSize(); i++)
+		items.push_back(invlist->getItem(i));
+	push_items(L, items);
+}
+
+void inventory_set_list_from_lua(Inventory *inv, const char *name,
+		lua_State *L, int tableindex, int forcesize)
+{
+	if(tableindex < 0)
+		tableindex = lua_gettop(L) + 1 + tableindex;
+	// If nil, delete list
+	if(lua_isnil(L, tableindex)){
+		inv->deleteList(name);
+		return;
+	}
+	// Otherwise set list
+	std::vector<ItemStack> items = read_items(L, tableindex);
+	int listsize = (forcesize != -1) ? forcesize : items.size();
+	InventoryList *invlist = inv->addList(name, listsize);
+	int index = 0;
+	for(std::vector<ItemStack>::const_iterator
+			i = items.begin(); i != items.end(); i++){
+		if(forcesize != -1 && index == forcesize)
+			break;
+		invlist->changeItem(index, *i);
+		index++;
+	}
+	while(forcesize != -1 && index < forcesize){
+		invlist->deleteItem(index);
+		index++;
+	}
+}
+
+// get_inventory(location)
+int l_get_inventory(lua_State *L)
+{
+	InventoryLocation loc;
+
+	std::string type = checkstringfield(L, 1, "type");
+	if(type == "player"){
+		std::string name = checkstringfield(L, 1, "name");
+		loc.setPlayer(name);
+	} else if(type == "node"){
+		lua_getfield(L, 1, "pos");
+		v3s16 pos = check_v3s16(L, -1);
+		loc.setNodeMeta(pos);
+	} else if(type == "detached"){
+		std::string name = checkstringfield(L, 1, "name");
+		loc.setDetached(name);
+	}
+
+	if(get_server(L)->getInventory(loc) != NULL)
+		InvRef::create(L, loc);
+	else
+		lua_pushnil(L);
+	return 1;
+}
+
+/*
+	Detached inventory callbacks
+*/
+
+// Retrieves minetest.detached_inventories[name][callbackname]
+// If that is nil or on error, return false and stack is unchanged
+// If that is a function, returns true and pushes the
+// function onto the stack
+static bool get_detached_inventory_callback(lua_State *L,
+		const std::string &name, const char *callbackname)
+{
+	lua_getglobal(L, "minetest");
+	lua_getfield(L, -1, "detached_inventories");
+	lua_remove(L, -2);
+	luaL_checktype(L, -1, LUA_TTABLE);
+	lua_getfield(L, -1, name.c_str());
+	lua_remove(L, -2);
+	// Should be a table
+	if(lua_type(L, -1) != LUA_TTABLE)
+	{
+		errorstream<<"Item \""<<name<<"\" not defined"<<std::endl;
+		lua_pop(L, 1);
+		return false;
+	}
+	lua_getfield(L, -1, callbackname);
+	lua_remove(L, -2);
+	// Should be a function or nil
+	if(lua_type(L, -1) == LUA_TFUNCTION)
+	{
+		return true;
+	}
+	else if(lua_isnil(L, -1))
+	{
+		lua_pop(L, 1);
+		return false;
+	}
+	else
+	{
+		errorstream<<"Detached inventory \""<<name<<"\" callback \""
+			<<callbackname<<"\" is not a function"<<std::endl;
+		lua_pop(L, 1);
+		return false;
+	}
+}
+
+// Return number of accepted items to be moved
+int scriptapi_detached_inventory_allow_move(lua_State *L,
+		const std::string &name,
+		const std::string &from_list, int from_index,
+		const std::string &to_list, int to_index,
+		int count, ServerActiveObject *player)
+{
+	realitycheck(L);
+	assert(lua_checkstack(L, 20));
+	StackUnroller stack_unroller(L);
+
+	// Push callback function on stack
+	if(!get_detached_inventory_callback(L, name, "allow_move"))
+		return count;
+
+	// function(inv, from_list, from_index, to_list, to_index, count, player)
+	// inv
+	InventoryLocation loc;
+	loc.setDetached(name);
+	InvRef::create(L, loc);
+	// from_list
+	lua_pushstring(L, from_list.c_str());
+	// from_index
+	lua_pushinteger(L, from_index + 1);
+	// to_list
+	lua_pushstring(L, to_list.c_str());
+	// to_index
+	lua_pushinteger(L, to_index + 1);
+	// count
+	lua_pushinteger(L, count);
+	// player
+	objectref_get_or_create(L, player);
+	if(lua_pcall(L, 7, 1, 0))
+		script_error(L, "error: %s", lua_tostring(L, -1));
+	if(!lua_isnumber(L, -1))
+		throw LuaError(L, "allow_move should return a number");
+	return luaL_checkinteger(L, -1);
+}
+
+// Return number of accepted items to be put
+int scriptapi_detached_inventory_allow_put(lua_State *L,
+		const std::string &name,
+		const std::string &listname, int index, ItemStack &stack,
+		ServerActiveObject *player)
+{
+	realitycheck(L);
+	assert(lua_checkstack(L, 20));
+	StackUnroller stack_unroller(L);
+
+	// Push callback function on stack
+	if(!get_detached_inventory_callback(L, name, "allow_put"))
+		return stack.count; // All will be accepted
+
+	// Call function(inv, listname, index, stack, player)
+	// inv
+	InventoryLocation loc;
+	loc.setDetached(name);
+	InvRef::create(L, loc);
+	// listname
+	lua_pushstring(L, listname.c_str());
+	// index
+	lua_pushinteger(L, index + 1);
+	// stack
+	LuaItemStack::create(L, stack);
+	// player
+	objectref_get_or_create(L, player);
+	if(lua_pcall(L, 5, 1, 0))
+		script_error(L, "error: %s", lua_tostring(L, -1));
+	if(!lua_isnumber(L, -1))
+		throw LuaError(L, "allow_put should return a number");
+	return luaL_checkinteger(L, -1);
+}
+
+// Return number of accepted items to be taken
+int scriptapi_detached_inventory_allow_take(lua_State *L,
+		const std::string &name,
+		const std::string &listname, int index, ItemStack &stack,
+		ServerActiveObject *player)
+{
+	realitycheck(L);
+	assert(lua_checkstack(L, 20));
+	StackUnroller stack_unroller(L);
+
+	// Push callback function on stack
+	if(!get_detached_inventory_callback(L, name, "allow_take"))
+		return stack.count; // All will be accepted
+
+	// Call function(inv, listname, index, stack, player)
+	// inv
+	InventoryLocation loc;
+	loc.setDetached(name);
+	InvRef::create(L, loc);
+	// listname
+	lua_pushstring(L, listname.c_str());
+	// index
+	lua_pushinteger(L, index + 1);
+	// stack
+	LuaItemStack::create(L, stack);
+	// player
+	objectref_get_or_create(L, player);
+	if(lua_pcall(L, 5, 1, 0))
+		script_error(L, "error: %s", lua_tostring(L, -1));
+	if(!lua_isnumber(L, -1))
+		throw LuaError(L, "allow_take should return a number");
+	return luaL_checkinteger(L, -1);
+}
+
+// Report moved items
+void scriptapi_detached_inventory_on_move(lua_State *L,
+		const std::string &name,
+		const std::string &from_list, int from_index,
+		const std::string &to_list, int to_index,
+		int count, ServerActiveObject *player)
+{
+	realitycheck(L);
+	assert(lua_checkstack(L, 20));
+	StackUnroller stack_unroller(L);
+
+	// Push callback function on stack
+	if(!get_detached_inventory_callback(L, name, "on_move"))
+		return;
+
+	// function(inv, from_list, from_index, to_list, to_index, count, player)
+	// inv
+	InventoryLocation loc;
+	loc.setDetached(name);
+	InvRef::create(L, loc);
+	// from_list
+	lua_pushstring(L, from_list.c_str());
+	// from_index
+	lua_pushinteger(L, from_index + 1);
+	// to_list
+	lua_pushstring(L, to_list.c_str());
+	// to_index
+	lua_pushinteger(L, to_index + 1);
+	// count
+	lua_pushinteger(L, count);
+	// player
+	objectref_get_or_create(L, player);
+	if(lua_pcall(L, 7, 0, 0))
+		script_error(L, "error: %s", lua_tostring(L, -1));
+}
+
+// Report put items
+void scriptapi_detached_inventory_on_put(lua_State *L,
+		const std::string &name,
+		const std::string &listname, int index, ItemStack &stack,
+		ServerActiveObject *player)
+{
+	realitycheck(L);
+	assert(lua_checkstack(L, 20));
+	StackUnroller stack_unroller(L);
+
+	// Push callback function on stack
+	if(!get_detached_inventory_callback(L, name, "on_put"))
+		return;
+
+	// Call function(inv, listname, index, stack, player)
+	// inv
+	InventoryLocation loc;
+	loc.setDetached(name);
+	InvRef::create(L, loc);
+	// listname
+	lua_pushstring(L, listname.c_str());
+	// index
+	lua_pushinteger(L, index + 1);
+	// stack
+	LuaItemStack::create(L, stack);
+	// player
+	objectref_get_or_create(L, player);
+	if(lua_pcall(L, 5, 0, 0))
+		script_error(L, "error: %s", lua_tostring(L, -1));
+}
+
+// Report taken items
+void scriptapi_detached_inventory_on_take(lua_State *L,
+		const std::string &name,
+		const std::string &listname, int index, ItemStack &stack,
+		ServerActiveObject *player)
+{
+	realitycheck(L);
+	assert(lua_checkstack(L, 20));
+	StackUnroller stack_unroller(L);
+
+	// Push callback function on stack
+	if(!get_detached_inventory_callback(L, name, "on_take"))
+		return;
+
+	// Call function(inv, listname, index, stack, player)
+	// inv
+	InventoryLocation loc;
+	loc.setDetached(name);
+	InvRef::create(L, loc);
+	// listname
+	lua_pushstring(L, listname.c_str());
+	// index
+	lua_pushinteger(L, index + 1);
+	// stack
+	LuaItemStack::create(L, stack);
+	// player
+	objectref_get_or_create(L, player);
+	if(lua_pcall(L, 5, 0, 0))
+		script_error(L, "error: %s", lua_tostring(L, -1));
+}
+
+// create_detached_inventory_raw(name)
+int l_create_detached_inventory_raw(lua_State *L)
+{
+	const char *name = luaL_checkstring(L, 1);
+	if(get_server(L)->createDetachedInventory(name) != NULL){
+		InventoryLocation loc;
+		loc.setDetached(name);
+		InvRef::create(L, loc);
+	}else{
+		lua_pushnil(L);
+	}
+	return 1;
+}
diff --git a/src/scriptapi_inventory.h b/src/scriptapi_inventory.h
new file mode 100644
index 0000000000000000000000000000000000000000..14f4fe02608b15d51995b979cd49482fc10e7885
--- /dev/null
+++ b/src/scriptapi_inventory.h
@@ -0,0 +1,165 @@
+/*
+Minetest-c55
+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 LUA_INVENTORY_H_
+#define LUA_INVENTORY_H_
+
+extern "C" {
+#include <lua.h>
+#include <lauxlib.h>
+}
+
+#include "inventorymanager.h"
+#include "player.h"
+#include "serverobject.h"
+#include "inventory.h"
+
+/*
+	InvRef
+*/
+
+class InvRef
+{
+private:
+	InventoryLocation m_loc;
+
+	static const char className[];
+	static const luaL_reg methods[];
+
+	static InvRef *checkobject(lua_State *L, int narg);
+
+	static Inventory* getinv(lua_State *L, InvRef *ref);
+
+	static InventoryList* getlist(lua_State *L, InvRef *ref,
+			const char *listname);
+
+	static void reportInventoryChange(lua_State *L, InvRef *ref);
+
+	// Exported functions
+
+	// garbage collector
+	static int gc_object(lua_State *L);
+
+	// is_empty(self, listname) -> true/false
+	static int l_is_empty(lua_State *L);
+
+	// get_size(self, listname)
+	static int l_get_size(lua_State *L);
+
+	// get_width(self, listname)
+	static int l_get_width(lua_State *L);
+
+	// set_size(self, listname, size)
+	static int l_set_size(lua_State *L);
+
+	// set_width(self, listname, size)
+	static int l_set_width(lua_State *L);
+
+	// get_stack(self, listname, i) -> itemstack
+	static int l_get_stack(lua_State *L);
+
+	// set_stack(self, listname, i, stack) -> true/false
+	static int l_set_stack(lua_State *L);
+
+	// get_list(self, listname) -> list or nil
+	static int l_get_list(lua_State *L);
+
+	// set_list(self, listname, list)
+	static int l_set_list(lua_State *L);
+
+	// add_item(self, listname, itemstack or itemstring or table or nil) -> itemstack
+	// Returns the leftover stack
+	static int l_add_item(lua_State *L);
+
+	// room_for_item(self, listname, itemstack or itemstring or table or nil) -> true/false
+	// Returns true if the item completely fits into the list
+	static int l_room_for_item(lua_State *L);
+
+	// contains_item(self, listname, itemstack or itemstring or table or nil) -> true/false
+	// Returns true if the list contains the given count of the given item name
+	static int l_contains_item(lua_State *L);
+
+	// remove_item(self, listname, itemstack or itemstring or table or nil) -> itemstack
+	// Returns the items that were actually removed
+	static int l_remove_item(lua_State *L);
+
+	// get_location() -> location (like minetest.get_inventory(location))
+	static int l_get_location(lua_State *L);
+
+public:
+	InvRef(const InventoryLocation &loc);
+
+	~InvRef();
+
+	// Creates an InvRef and leaves it on top of stack
+	// Not callable from Lua; all references are created on the C side.
+	static void create(lua_State *L, const InventoryLocation &loc);
+	static void createPlayer(lua_State *L, Player *player);
+	static void createNodeMeta(lua_State *L, v3s16 p);
+	static void Register(lua_State *L);
+};
+
+void inventory_get_list_to_lua(Inventory *inv, const char *name,lua_State *L);
+void inventory_set_list_from_lua(Inventory *inv, const char *name,
+								lua_State *L, int tableindex, int forcesize=-1);
+
+/*****************************************************************************/
+/* Minetest interface                                                        */
+/*****************************************************************************/
+/* Detached inventory callbacks */
+// Return number of accepted items to be moved
+int scriptapi_detached_inventory_allow_move(lua_State *L,
+		const std::string &name,
+		const std::string &from_list, int from_index,
+		const std::string &to_list, int to_index,
+		int count, ServerActiveObject *player);
+// Return number of accepted items to be put
+int scriptapi_detached_inventory_allow_put(lua_State *L,
+		const std::string &name,
+		const std::string &listname, int index, ItemStack &stack,
+		ServerActiveObject *player);
+// Return number of accepted items to be taken
+int scriptapi_detached_inventory_allow_take(lua_State *L,
+		const std::string &name,
+		const std::string &listname, int index, ItemStack &stack,
+		ServerActiveObject *player);
+// Report moved items
+void scriptapi_detached_inventory_on_move(lua_State *L,
+		const std::string &name,
+		const std::string &from_list, int from_index,
+		const std::string &to_list, int to_index,
+		int count, ServerActiveObject *player);
+// Report put items
+void scriptapi_detached_inventory_on_put(lua_State *L,
+		const std::string &name,
+		const std::string &listname, int index, ItemStack &stack,
+		ServerActiveObject *player);
+// Report taken items
+void scriptapi_detached_inventory_on_take(lua_State *L,
+		const std::string &name,
+		const std::string &listname, int index, ItemStack &stack,
+		ServerActiveObject *player);
+
+/*****************************************************************************/
+/* Mod API                                                                   */
+/*****************************************************************************/
+int l_create_detached_inventory_raw(lua_State *L);
+int l_get_inventory(lua_State *L);
+
+#endif /* LUA_INVENTORY_H_ */
diff --git a/src/scriptapi_item.cpp b/src/scriptapi_item.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..b266d856d26fbd332fa9be2940584fb24b706f54
--- /dev/null
+++ b/src/scriptapi_item.cpp
@@ -0,0 +1,718 @@
+/*
+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 "scriptapi.h"
+#include "server.h"
+#include "script.h"
+#include "tool.h"
+#include "nodedef.h"
+#include "util/pointedthing.h"
+#include "scriptapi_item.h"
+#include "scriptapi_types.h"
+#include "scriptapi_common.h"
+#include "scriptapi_object.h"
+#include "scriptapi_content.h"
+
+
+struct EnumString es_ItemType[] =
+{
+	{ITEM_NONE, "none"},
+	{ITEM_NODE, "node"},
+	{ITEM_CRAFT, "craft"},
+	{ITEM_TOOL, "tool"},
+	{0, NULL},
+};
+
+
+/*
+	ItemDefinition
+*/
+
+ItemDefinition read_item_definition(lua_State *L, int index,
+		ItemDefinition default_def)
+{
+	if(index < 0)
+		index = lua_gettop(L) + 1 + index;
+
+	// Read the item definition
+	ItemDefinition def = default_def;
+
+	def.type = (ItemType)getenumfield(L, index, "type",
+			es_ItemType, ITEM_NONE);
+	getstringfield(L, index, "name", def.name);
+	getstringfield(L, index, "description", def.description);
+	getstringfield(L, index, "inventory_image", def.inventory_image);
+	getstringfield(L, index, "wield_image", def.wield_image);
+
+	lua_getfield(L, index, "wield_scale");
+	if(lua_istable(L, -1)){
+		def.wield_scale = check_v3f(L, -1);
+	}
+	lua_pop(L, 1);
+
+	def.stack_max = getintfield_default(L, index, "stack_max", def.stack_max);
+	if(def.stack_max == 0)
+		def.stack_max = 1;
+
+	lua_getfield(L, index, "on_use");
+	def.usable = lua_isfunction(L, -1);
+	lua_pop(L, 1);
+
+	getboolfield(L, index, "liquids_pointable", def.liquids_pointable);
+
+	warn_if_field_exists(L, index, "tool_digging_properties",
+			"deprecated: use tool_capabilities");
+
+	lua_getfield(L, index, "tool_capabilities");
+	if(lua_istable(L, -1)){
+		def.tool_capabilities = new ToolCapabilities(
+				read_tool_capabilities(L, -1));
+	}
+
+	// If name is "" (hand), ensure there are ToolCapabilities
+	// because it will be looked up there whenever any other item has
+	// no ToolCapabilities
+	if(def.name == "" && def.tool_capabilities == NULL){
+		def.tool_capabilities = new ToolCapabilities();
+	}
+
+	lua_getfield(L, index, "groups");
+	read_groups(L, -1, def.groups);
+	lua_pop(L, 1);
+
+	lua_getfield(L, index, "sounds");
+	if(lua_istable(L, -1)){
+		lua_getfield(L, -1, "place");
+		read_soundspec(L, -1, def.sound_place);
+		lua_pop(L, 1);
+	}
+	lua_pop(L, 1);
+
+	// Client shall immediately place this node when player places the item.
+	// Server will update the precise end result a moment later.
+	// "" = no prediction
+	getstringfield(L, index, "node_placement_prediction",
+			def.node_placement_prediction);
+
+	return def;
+}
+
+// register_item_raw({lots of stuff})
+int l_register_item_raw(lua_State *L)
+{
+	luaL_checktype(L, 1, LUA_TTABLE);
+	int table = 1;
+
+	// Get the writable item and node definition managers from the server
+	IWritableItemDefManager *idef =
+			get_server(L)->getWritableItemDefManager();
+	IWritableNodeDefManager *ndef =
+			get_server(L)->getWritableNodeDefManager();
+
+	// Check if name is defined
+	std::string name;
+	lua_getfield(L, table, "name");
+	if(lua_isstring(L, -1)){
+		name = lua_tostring(L, -1);
+		verbosestream<<"register_item_raw: "<<name<<std::endl;
+	} else {
+		throw LuaError(L, "register_item_raw: name is not defined or not a string");
+	}
+
+	// Check if on_use is defined
+
+	ItemDefinition def;
+	// Set a distinctive default value to check if this is set
+	def.node_placement_prediction = "__default";
+
+	// Read the item definition
+	def = read_item_definition(L, table, def);
+
+	// Default to having client-side placement prediction for nodes
+	// ("" in item definition sets it off)
+	if(def.node_placement_prediction == "__default"){
+		if(def.type == ITEM_NODE)
+			def.node_placement_prediction = name;
+		else
+			def.node_placement_prediction = "";
+	}
+
+	// Register item definition
+	idef->registerItem(def);
+
+	// Read the node definition (content features) and register it
+	if(def.type == ITEM_NODE)
+	{
+		ContentFeatures f = read_content_features(L, table);
+		ndef->set(f.name, f);
+	}
+
+	return 0; /* number of results */
+}
+
+// register_alias_raw(name, convert_to_name)
+int l_register_alias_raw(lua_State *L)
+{
+	std::string name = luaL_checkstring(L, 1);
+	std::string convert_to = luaL_checkstring(L, 2);
+
+	// Get the writable item definition manager from the server
+	IWritableItemDefManager *idef =
+			get_server(L)->getWritableItemDefManager();
+
+	idef->registerAlias(name, convert_to);
+
+	return 0; /* number of results */
+}
+
+// Retrieves minetest.registered_items[name][callbackname]
+// If that is nil or on error, return false and stack is unchanged
+// If that is a function, returns true and pushes the
+// function onto the stack
+// If minetest.registered_items[name] doesn't exist, minetest.nodedef_default
+// is tried instead so unknown items can still be manipulated to some degree
+bool get_item_callback(lua_State *L,
+		const char *name, const char *callbackname)
+{
+	lua_getglobal(L, "minetest");
+	lua_getfield(L, -1, "registered_items");
+	lua_remove(L, -2);
+	luaL_checktype(L, -1, LUA_TTABLE);
+	lua_getfield(L, -1, name);
+	lua_remove(L, -2);
+	// Should be a table
+	if(lua_type(L, -1) != LUA_TTABLE)
+	{
+		// Report error and clean up
+		errorstream<<"Item \""<<name<<"\" not defined"<<std::endl;
+		lua_pop(L, 1);
+
+		// Try minetest.nodedef_default instead
+		lua_getglobal(L, "minetest");
+		lua_getfield(L, -1, "nodedef_default");
+		lua_remove(L, -2);
+		luaL_checktype(L, -1, LUA_TTABLE);
+	}
+	lua_getfield(L, -1, callbackname);
+	lua_remove(L, -2);
+	// Should be a function or nil
+	if(lua_type(L, -1) == LUA_TFUNCTION)
+	{
+		return true;
+	}
+	else if(lua_isnil(L, -1))
+	{
+		lua_pop(L, 1);
+		return false;
+	}
+	else
+	{
+		errorstream<<"Item \""<<name<<"\" callback \""
+			<<callbackname<<" is not a function"<<std::endl;
+		lua_pop(L, 1);
+		return false;
+	}
+}
+
+bool scriptapi_item_on_drop(lua_State *L, ItemStack &item,
+		ServerActiveObject *dropper, v3f pos)
+{
+	realitycheck(L);
+	assert(lua_checkstack(L, 20));
+	StackUnroller stack_unroller(L);
+
+	// Push callback function on stack
+	if(!get_item_callback(L, item.name.c_str(), "on_drop"))
+		return false;
+
+	// Call function
+	LuaItemStack::create(L, item);
+	objectref_get_or_create(L, dropper);
+	pushFloatPos(L, pos);
+	if(lua_pcall(L, 3, 1, 0))
+		script_error(L, "error: %s", lua_tostring(L, -1));
+	if(!lua_isnil(L, -1))
+		item = read_item(L, -1);
+	return true;
+}
+
+bool scriptapi_item_on_place(lua_State *L, ItemStack &item,
+		ServerActiveObject *placer, const PointedThing &pointed)
+{
+	realitycheck(L);
+	assert(lua_checkstack(L, 20));
+	StackUnroller stack_unroller(L);
+
+	// Push callback function on stack
+	if(!get_item_callback(L, item.name.c_str(), "on_place"))
+		return false;
+
+	// Call function
+	LuaItemStack::create(L, item);
+	objectref_get_or_create(L, placer);
+	push_pointed_thing(L, pointed);
+	if(lua_pcall(L, 3, 1, 0))
+		script_error(L, "error: %s", lua_tostring(L, -1));
+	if(!lua_isnil(L, -1))
+		item = read_item(L, -1);
+	return true;
+}
+
+bool scriptapi_item_on_use(lua_State *L, ItemStack &item,
+		ServerActiveObject *user, const PointedThing &pointed)
+{
+	realitycheck(L);
+	assert(lua_checkstack(L, 20));
+	StackUnroller stack_unroller(L);
+
+	// Push callback function on stack
+	if(!get_item_callback(L, item.name.c_str(), "on_use"))
+		return false;
+
+	// Call function
+	LuaItemStack::create(L, item);
+	objectref_get_or_create(L, user);
+	push_pointed_thing(L, pointed);
+	if(lua_pcall(L, 3, 1, 0))
+		script_error(L, "error: %s", lua_tostring(L, -1));
+	if(!lua_isnil(L, -1))
+		item = read_item(L, -1);
+	return true;
+}
+
+// garbage collector
+int LuaItemStack::gc_object(lua_State *L)
+{
+	LuaItemStack *o = *(LuaItemStack **)(lua_touserdata(L, 1));
+	delete o;
+	return 0;
+}
+
+// is_empty(self) -> true/false
+int LuaItemStack::l_is_empty(lua_State *L)
+{
+	LuaItemStack *o = checkobject(L, 1);
+	ItemStack &item = o->m_stack;
+	lua_pushboolean(L, item.empty());
+	return 1;
+}
+
+// get_name(self) -> string
+int LuaItemStack::l_get_name(lua_State *L)
+{
+	LuaItemStack *o = checkobject(L, 1);
+	ItemStack &item = o->m_stack;
+	lua_pushstring(L, item.name.c_str());
+	return 1;
+}
+
+// get_count(self) -> number
+int LuaItemStack::l_get_count(lua_State *L)
+{
+	LuaItemStack *o = checkobject(L, 1);
+	ItemStack &item = o->m_stack;
+	lua_pushinteger(L, item.count);
+	return 1;
+}
+
+// get_wear(self) -> number
+int LuaItemStack::l_get_wear(lua_State *L)
+{
+	LuaItemStack *o = checkobject(L, 1);
+	ItemStack &item = o->m_stack;
+	lua_pushinteger(L, item.wear);
+	return 1;
+}
+
+// get_metadata(self) -> string
+int LuaItemStack::l_get_metadata(lua_State *L)
+{
+	LuaItemStack *o = checkobject(L, 1);
+	ItemStack &item = o->m_stack;
+	lua_pushlstring(L, item.metadata.c_str(), item.metadata.size());
+	return 1;
+}
+
+// clear(self) -> true
+int LuaItemStack::l_clear(lua_State *L)
+{
+	LuaItemStack *o = checkobject(L, 1);
+	o->m_stack.clear();
+	lua_pushboolean(L, true);
+	return 1;
+}
+
+// replace(self, itemstack or itemstring or table or nil) -> true
+int LuaItemStack::l_replace(lua_State *L)
+{
+	LuaItemStack *o = checkobject(L, 1);
+	o->m_stack = read_item(L, 2);
+	lua_pushboolean(L, true);
+	return 1;
+}
+
+// to_string(self) -> string
+int LuaItemStack::l_to_string(lua_State *L)
+{
+	LuaItemStack *o = checkobject(L, 1);
+	std::string itemstring = o->m_stack.getItemString();
+	lua_pushstring(L, itemstring.c_str());
+	return 1;
+}
+
+// to_table(self) -> table or nil
+int LuaItemStack::l_to_table(lua_State *L)
+{
+	LuaItemStack *o = checkobject(L, 1);
+	const ItemStack &item = o->m_stack;
+	if(item.empty())
+	{
+		lua_pushnil(L);
+	}
+	else
+	{
+		lua_newtable(L);
+		lua_pushstring(L, item.name.c_str());
+		lua_setfield(L, -2, "name");
+		lua_pushinteger(L, item.count);
+		lua_setfield(L, -2, "count");
+		lua_pushinteger(L, item.wear);
+		lua_setfield(L, -2, "wear");
+		lua_pushlstring(L, item.metadata.c_str(), item.metadata.size());
+		lua_setfield(L, -2, "metadata");
+	}
+	return 1;
+}
+
+// get_stack_max(self) -> number
+int LuaItemStack::l_get_stack_max(lua_State *L)
+{
+	LuaItemStack *o = checkobject(L, 1);
+	ItemStack &item = o->m_stack;
+	lua_pushinteger(L, item.getStackMax(get_server(L)->idef()));
+	return 1;
+}
+
+// get_free_space(self) -> number
+int LuaItemStack::l_get_free_space(lua_State *L)
+{
+	LuaItemStack *o = checkobject(L, 1);
+	ItemStack &item = o->m_stack;
+	lua_pushinteger(L, item.freeSpace(get_server(L)->idef()));
+	return 1;
+}
+
+// is_known(self) -> true/false
+// Checks if the item is defined.
+int LuaItemStack::l_is_known(lua_State *L)
+{
+	LuaItemStack *o = checkobject(L, 1);
+	ItemStack &item = o->m_stack;
+	bool is_known = item.isKnown(get_server(L)->idef());
+	lua_pushboolean(L, is_known);
+	return 1;
+}
+
+// get_definition(self) -> table
+// Returns the item definition table from minetest.registered_items,
+// or a fallback one (name="unknown")
+int LuaItemStack::l_get_definition(lua_State *L)
+{
+	LuaItemStack *o = checkobject(L, 1);
+	ItemStack &item = o->m_stack;
+
+	// Get minetest.registered_items[name]
+	lua_getglobal(L, "minetest");
+	lua_getfield(L, -1, "registered_items");
+	luaL_checktype(L, -1, LUA_TTABLE);
+	lua_getfield(L, -1, item.name.c_str());
+	if(lua_isnil(L, -1))
+	{
+		lua_pop(L, 1);
+		lua_getfield(L, -1, "unknown");
+	}
+	return 1;
+}
+
+// get_tool_capabilities(self) -> table
+// Returns the effective tool digging properties.
+// Returns those of the hand ("") if this item has none associated.
+int LuaItemStack::l_get_tool_capabilities(lua_State *L)
+{
+	LuaItemStack *o = checkobject(L, 1);
+	ItemStack &item = o->m_stack;
+	const ToolCapabilities &prop =
+		item.getToolCapabilities(get_server(L)->idef());
+	push_tool_capabilities(L, prop);
+	return 1;
+}
+
+// add_wear(self, amount) -> true/false
+// The range for "amount" is [0,65535]. Wear is only added if the item
+// is a tool. Adding wear might destroy the item.
+// Returns true if the item is (or was) a tool.
+int LuaItemStack::l_add_wear(lua_State *L)
+{
+	LuaItemStack *o = checkobject(L, 1);
+	ItemStack &item = o->m_stack;
+	int amount = lua_tointeger(L, 2);
+	bool result = item.addWear(amount, get_server(L)->idef());
+	lua_pushboolean(L, result);
+	return 1;
+}
+
+// add_item(self, itemstack or itemstring or table or nil) -> itemstack
+// Returns leftover item stack
+int LuaItemStack::l_add_item(lua_State *L)
+{
+	LuaItemStack *o = checkobject(L, 1);
+	ItemStack &item = o->m_stack;
+	ItemStack newitem = read_item(L, 2);
+	ItemStack leftover = item.addItem(newitem, get_server(L)->idef());
+	create(L, leftover);
+	return 1;
+}
+
+// item_fits(self, itemstack or itemstring or table or nil) -> true/false, itemstack
+// First return value is true iff the new item fits fully into the stack
+// Second return value is the would-be-left-over item stack
+int LuaItemStack::l_item_fits(lua_State *L)
+{
+	LuaItemStack *o = checkobject(L, 1);
+	ItemStack &item = o->m_stack;
+	ItemStack newitem = read_item(L, 2);
+	ItemStack restitem;
+	bool fits = item.itemFits(newitem, &restitem, get_server(L)->idef());
+	lua_pushboolean(L, fits);  // first return value
+	create(L, restitem);       // second return value
+	return 2;
+}
+
+// take_item(self, takecount=1) -> itemstack
+int LuaItemStack::l_take_item(lua_State *L)
+{
+	LuaItemStack *o = checkobject(L, 1);
+	ItemStack &item = o->m_stack;
+	u32 takecount = 1;
+	if(!lua_isnone(L, 2))
+		takecount = luaL_checkinteger(L, 2);
+	ItemStack taken = item.takeItem(takecount);
+	create(L, taken);
+	return 1;
+}
+
+// peek_item(self, peekcount=1) -> itemstack
+int LuaItemStack::l_peek_item(lua_State *L)
+{
+	LuaItemStack *o = checkobject(L, 1);
+	ItemStack &item = o->m_stack;
+	u32 peekcount = 1;
+	if(!lua_isnone(L, 2))
+		peekcount = lua_tointeger(L, 2);
+	ItemStack peekaboo = item.peekItem(peekcount);
+	create(L, peekaboo);
+	return 1;
+}
+
+LuaItemStack::LuaItemStack(const ItemStack &item):
+	m_stack(item)
+{
+}
+
+LuaItemStack::~LuaItemStack()
+{
+}
+
+const ItemStack& LuaItemStack::getItem() const
+{
+	return m_stack;
+}
+ItemStack& LuaItemStack::getItem()
+{
+	return m_stack;
+}
+
+// LuaItemStack(itemstack or itemstring or table or nil)
+// Creates an LuaItemStack and leaves it on top of stack
+int LuaItemStack::create_object(lua_State *L)
+{
+	ItemStack item = read_item(L, 1);
+	LuaItemStack *o = new LuaItemStack(item);
+	*(void **)(lua_newuserdata(L, sizeof(void *))) = o;
+	luaL_getmetatable(L, className);
+	lua_setmetatable(L, -2);
+	return 1;
+}
+// Not callable from Lua
+int LuaItemStack::create(lua_State *L, const ItemStack &item)
+{
+	LuaItemStack *o = new LuaItemStack(item);
+	*(void **)(lua_newuserdata(L, sizeof(void *))) = o;
+	luaL_getmetatable(L, className);
+	lua_setmetatable(L, -2);
+	return 1;
+}
+
+LuaItemStack* LuaItemStack::checkobject(lua_State *L, int narg)
+{
+	luaL_checktype(L, narg, LUA_TUSERDATA);
+	void *ud = luaL_checkudata(L, narg, className);
+	if(!ud) luaL_typerror(L, narg, className);
+	return *(LuaItemStack**)ud;  // unbox pointer
+}
+
+void LuaItemStack::Register(lua_State *L)
+{
+	lua_newtable(L);
+	int methodtable = lua_gettop(L);
+	luaL_newmetatable(L, className);
+	int metatable = lua_gettop(L);
+
+	lua_pushliteral(L, "__metatable");
+	lua_pushvalue(L, methodtable);
+	lua_settable(L, metatable);  // hide metatable from Lua getmetatable()
+
+	lua_pushliteral(L, "__index");
+	lua_pushvalue(L, methodtable);
+	lua_settable(L, metatable);
+
+	lua_pushliteral(L, "__gc");
+	lua_pushcfunction(L, gc_object);
+	lua_settable(L, metatable);
+
+	lua_pop(L, 1);  // drop metatable
+
+	luaL_openlib(L, 0, methods, 0);  // fill methodtable
+	lua_pop(L, 1);  // drop methodtable
+
+	// Can be created from Lua (LuaItemStack(itemstack or itemstring or table or nil))
+	lua_register(L, className, create_object);
+}
+
+const char LuaItemStack::className[] = "ItemStack";
+const luaL_reg LuaItemStack::methods[] = {
+	luamethod(LuaItemStack, is_empty),
+	luamethod(LuaItemStack, get_name),
+	luamethod(LuaItemStack, get_count),
+	luamethod(LuaItemStack, get_wear),
+	luamethod(LuaItemStack, get_metadata),
+	luamethod(LuaItemStack, clear),
+	luamethod(LuaItemStack, replace),
+	luamethod(LuaItemStack, to_string),
+	luamethod(LuaItemStack, to_table),
+	luamethod(LuaItemStack, get_stack_max),
+	luamethod(LuaItemStack, get_free_space),
+	luamethod(LuaItemStack, is_known),
+	luamethod(LuaItemStack, get_definition),
+	luamethod(LuaItemStack, get_tool_capabilities),
+	luamethod(LuaItemStack, add_wear),
+	luamethod(LuaItemStack, add_item),
+	luamethod(LuaItemStack, item_fits),
+	luamethod(LuaItemStack, take_item),
+	luamethod(LuaItemStack, peek_item),
+	{0,0}
+};
+
+ItemStack read_item(lua_State *L, int index)
+{
+	if(index < 0)
+		index = lua_gettop(L) + 1 + index;
+
+	if(lua_isnil(L, index))
+	{
+		return ItemStack();
+	}
+	else if(lua_isuserdata(L, index))
+	{
+		// Convert from LuaItemStack
+		LuaItemStack *o = LuaItemStack::checkobject(L, index);
+		return o->getItem();
+	}
+	else if(lua_isstring(L, index))
+	{
+		// Convert from itemstring
+		std::string itemstring = lua_tostring(L, index);
+		IItemDefManager *idef = get_server(L)->idef();
+		try
+		{
+			ItemStack item;
+			item.deSerialize(itemstring, idef);
+			return item;
+		}
+		catch(SerializationError &e)
+		{
+			infostream<<"WARNING: unable to create item from itemstring"
+					<<": "<<itemstring<<std::endl;
+			return ItemStack();
+		}
+	}
+	else if(lua_istable(L, index))
+	{
+		// Convert from table
+		IItemDefManager *idef = get_server(L)->idef();
+		std::string name = getstringfield_default(L, index, "name", "");
+		int count = getintfield_default(L, index, "count", 1);
+		int wear = getintfield_default(L, index, "wear", 0);
+		std::string metadata = getstringfield_default(L, index, "metadata", "");
+		return ItemStack(name, count, wear, metadata, idef);
+	}
+	else
+	{
+		throw LuaError(L, "Expecting itemstack, itemstring, table or nil");
+	}
+}
+
+std::vector<ItemStack> read_items(lua_State *L, int index)
+{
+	if(index < 0)
+		index = lua_gettop(L) + 1 + index;
+
+	std::vector<ItemStack> items;
+	luaL_checktype(L, index, LUA_TTABLE);
+	lua_pushnil(L);
+	while(lua_next(L, index) != 0){
+		// key at index -2 and value at index -1
+		items.push_back(read_item(L, -1));
+		// removes value, keeps key for next iteration
+		lua_pop(L, 1);
+	}
+	return items;
+}
+
+// creates a table of ItemStacks
+void push_items(lua_State *L, const std::vector<ItemStack> &items)
+{
+	// Get the table insert function
+	lua_getglobal(L, "table");
+	lua_getfield(L, -1, "insert");
+	int table_insert = lua_gettop(L);
+	// Create and fill table
+	lua_newtable(L);
+	int table = lua_gettop(L);
+	for(u32 i=0; i<items.size(); i++){
+		ItemStack item = items[i];
+		lua_pushvalue(L, table_insert);
+		lua_pushvalue(L, table);
+		LuaItemStack::create(L, item);
+		if(lua_pcall(L, 2, 0, 0))
+			script_error(L, "error: %s", lua_tostring(L, -1));
+	}
+	lua_remove(L, -2); // Remove table
+	lua_remove(L, -2); // Remove insert
+}
diff --git a/src/scriptapi_item.h b/src/scriptapi_item.h
new file mode 100644
index 0000000000000000000000000000000000000000..e0f2139905fedcafc105514d366252597c775aa9
--- /dev/null
+++ b/src/scriptapi_item.h
@@ -0,0 +1,166 @@
+/*
+Minetest-c55
+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 LUA_ITEM_H_
+#define LUA_ITEM_H_
+
+extern "C" {
+#include <lua.h>
+#include <lauxlib.h>
+}
+
+#include <vector>
+
+#include "itemdef.h"
+#include "content_sao.h"
+#include "util/pointedthing.h"
+#include "inventory.h"
+
+#include "inventory.h"
+
+ItemStack read_item(lua_State *L, int index);
+std::vector<ItemStack> read_items(lua_State *L, int index);
+void push_items(lua_State *L, const std::vector<ItemStack> &items);
+
+class LuaItemStack
+{
+private:
+	ItemStack m_stack;
+
+	static const char className[];
+	static const luaL_reg methods[];
+
+	// Exported functions
+
+	// garbage collector
+	static int gc_object(lua_State *L);
+
+	// is_empty(self) -> true/false
+	static int l_is_empty(lua_State *L);
+
+	// get_name(self) -> string
+	static int l_get_name(lua_State *L);
+
+	// get_count(self) -> number
+	static int l_get_count(lua_State *L);
+
+	// get_wear(self) -> number
+	static int l_get_wear(lua_State *L);
+
+	// get_metadata(self) -> string
+	static int l_get_metadata(lua_State *L);
+
+	// clear(self) -> true
+	static int l_clear(lua_State *L);
+
+	// replace(self, itemstack or itemstring or table or nil) -> true
+	static int l_replace(lua_State *L);
+
+	// to_string(self) -> string
+	static int l_to_string(lua_State *L);
+
+	// to_table(self) -> table or nil
+	static int l_to_table(lua_State *L);
+
+	// get_stack_max(self) -> number
+	static int l_get_stack_max(lua_State *L);
+
+	// get_free_space(self) -> number
+	static int l_get_free_space(lua_State *L);
+
+	// is_known(self) -> true/false
+	// Checks if the item is defined.
+	static int l_is_known(lua_State *L);
+
+	// get_definition(self) -> table
+	// Returns the item definition table from minetest.registered_items,
+	// or a fallback one (name="unknown")
+	static int l_get_definition(lua_State *L);
+
+	// get_tool_capabilities(self) -> table
+	// Returns the effective tool digging properties.
+	// Returns those of the hand ("") if this item has none associated.
+	static int l_get_tool_capabilities(lua_State *L);
+
+	// add_wear(self, amount) -> true/false
+	// The range for "amount" is [0,65535]. Wear is only added if the item
+	// is a tool. Adding wear might destroy the item.
+	// Returns true if the item is (or was) a tool.
+	static int l_add_wear(lua_State *L);
+
+	// add_item(self, itemstack or itemstring or table or nil) -> itemstack
+	// Returns leftover item stack
+	static int l_add_item(lua_State *L);
+
+	// item_fits(self, itemstack or itemstring or table or nil) -> true/false, itemstack
+	// First return value is true iff the new item fits fully into the stack
+	// Second return value is the would-be-left-over item stack
+	static int l_item_fits(lua_State *L);
+
+	// take_item(self, takecount=1) -> itemstack
+	static int l_take_item(lua_State *L);
+
+	// peek_item(self, peekcount=1) -> itemstack
+	static int l_peek_item(lua_State *L);
+
+public:
+	LuaItemStack(const ItemStack &item);
+	~LuaItemStack();
+
+	const ItemStack& getItem() const;
+	ItemStack& getItem();
+
+	// LuaItemStack(itemstack or itemstring or table or nil)
+	// Creates an LuaItemStack and leaves it on top of stack
+	static int create_object(lua_State *L);
+	// Not callable from Lua
+	static int create(lua_State *L, const ItemStack &item);
+	static LuaItemStack* checkobject(lua_State *L, int narg);
+	static void Register(lua_State *L);
+
+};
+
+/*****************************************************************************/
+/* Mod API                                                                   */
+/*****************************************************************************/
+int l_register_item_raw(lua_State *L);
+int l_register_alias_raw(lua_State *L);
+
+/*****************************************************************************/
+/* scriptapi internal                                                        */
+/*****************************************************************************/
+bool get_item_callback(lua_State *L,
+		const char *name, const char *callbackname);
+ItemDefinition read_item_definition(lua_State *L, int index,
+		ItemDefinition default_def = ItemDefinition());
+
+extern struct EnumString es_ItemType[];
+
+/*****************************************************************************/
+/* Minetest interface                                                        */
+/*****************************************************************************/
+bool scriptapi_item_on_drop(lua_State *L, ItemStack &item,
+		ServerActiveObject *dropper, v3f pos);
+bool scriptapi_item_on_place(lua_State *L, ItemStack &item,
+		ServerActiveObject *placer, const PointedThing &pointed);
+bool scriptapi_item_on_use(lua_State *L, ItemStack &item,
+		ServerActiveObject *user, const PointedThing &pointed);
+
+
+#endif /* LUA_ITEM_H_ */
diff --git a/src/scriptapi_node.cpp b/src/scriptapi_node.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..5ffcaeb9d93245bb8f6b7cb818c5820cebef5c37
--- /dev/null
+++ b/src/scriptapi_node.cpp
@@ -0,0 +1,243 @@
+/*
+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 "scriptapi.h"
+#include "scriptapi_node.h"
+#include "util/pointedthing.h"
+#include "script.h"
+#include "scriptapi_common.h"
+#include "scriptapi_types.h"
+#include "scriptapi_item.h"
+#include "scriptapi_object.h"
+
+
+struct EnumString es_DrawType[] =
+{
+	{NDT_NORMAL, "normal"},
+	{NDT_AIRLIKE, "airlike"},
+	{NDT_LIQUID, "liquid"},
+	{NDT_FLOWINGLIQUID, "flowingliquid"},
+	{NDT_GLASSLIKE, "glasslike"},
+	{NDT_ALLFACES, "allfaces"},
+	{NDT_ALLFACES_OPTIONAL, "allfaces_optional"},
+	{NDT_TORCHLIKE, "torchlike"},
+	{NDT_SIGNLIKE, "signlike"},
+	{NDT_PLANTLIKE, "plantlike"},
+	{NDT_FENCELIKE, "fencelike"},
+	{NDT_RAILLIKE, "raillike"},
+	{NDT_NODEBOX, "nodebox"},
+	{0, NULL},
+};
+
+struct EnumString es_ContentParamType[] =
+{
+	{CPT_NONE, "none"},
+	{CPT_LIGHT, "light"},
+	{0, NULL},
+};
+
+struct EnumString es_ContentParamType2[] =
+{
+	{CPT2_NONE, "none"},
+	{CPT2_FULL, "full"},
+	{CPT2_FLOWINGLIQUID, "flowingliquid"},
+	{CPT2_FACEDIR, "facedir"},
+	{CPT2_WALLMOUNTED, "wallmounted"},
+	{0, NULL},
+};
+
+struct EnumString es_LiquidType[] =
+{
+	{LIQUID_NONE, "none"},
+	{LIQUID_FLOWING, "flowing"},
+	{LIQUID_SOURCE, "source"},
+	{0, NULL},
+};
+
+struct EnumString es_NodeBoxType[] =
+{
+	{NODEBOX_REGULAR, "regular"},
+	{NODEBOX_FIXED, "fixed"},
+	{NODEBOX_WALLMOUNTED, "wallmounted"},
+	{0, NULL},
+};
+
+
+bool scriptapi_node_on_punch(lua_State *L, v3s16 p, MapNode node,
+		ServerActiveObject *puncher)
+{
+	realitycheck(L);
+	assert(lua_checkstack(L, 20));
+	StackUnroller stack_unroller(L);
+
+	INodeDefManager *ndef = get_server(L)->ndef();
+
+	// Push callback function on stack
+	if(!get_item_callback(L, ndef->get(node).name.c_str(), "on_punch"))
+		return false;
+
+	// Call function
+	push_v3s16(L, p);
+	pushnode(L, node, ndef);
+	objectref_get_or_create(L, puncher);
+	if(lua_pcall(L, 3, 0, 0))
+		script_error(L, "error: %s", lua_tostring(L, -1));
+	return true;
+}
+
+bool scriptapi_node_on_dig(lua_State *L, v3s16 p, MapNode node,
+		ServerActiveObject *digger)
+{
+	realitycheck(L);
+	assert(lua_checkstack(L, 20));
+	StackUnroller stack_unroller(L);
+
+	INodeDefManager *ndef = get_server(L)->ndef();
+
+	// Push callback function on stack
+	if(!get_item_callback(L, ndef->get(node).name.c_str(), "on_dig"))
+		return false;
+
+	// Call function
+	push_v3s16(L, p);
+	pushnode(L, node, ndef);
+	objectref_get_or_create(L, digger);
+	if(lua_pcall(L, 3, 0, 0))
+		script_error(L, "error: %s", lua_tostring(L, -1));
+	return true;
+}
+
+void scriptapi_node_on_construct(lua_State *L, v3s16 p, MapNode node)
+{
+	realitycheck(L);
+	assert(lua_checkstack(L, 20));
+	StackUnroller stack_unroller(L);
+
+	INodeDefManager *ndef = get_server(L)->ndef();
+
+	// Push callback function on stack
+	if(!get_item_callback(L, ndef->get(node).name.c_str(), "on_construct"))
+		return;
+
+	// Call function
+	push_v3s16(L, p);
+	if(lua_pcall(L, 1, 0, 0))
+		script_error(L, "error: %s", lua_tostring(L, -1));
+}
+
+void scriptapi_node_on_destruct(lua_State *L, v3s16 p, MapNode node)
+{
+	realitycheck(L);
+	assert(lua_checkstack(L, 20));
+	StackUnroller stack_unroller(L);
+
+	INodeDefManager *ndef = get_server(L)->ndef();
+
+	// Push callback function on stack
+	if(!get_item_callback(L, ndef->get(node).name.c_str(), "on_destruct"))
+		return;
+
+	// Call function
+	push_v3s16(L, p);
+	if(lua_pcall(L, 1, 0, 0))
+		script_error(L, "error: %s", lua_tostring(L, -1));
+}
+
+void scriptapi_node_after_destruct(lua_State *L, v3s16 p, MapNode node)
+{
+	realitycheck(L);
+	assert(lua_checkstack(L, 20));
+	StackUnroller stack_unroller(L);
+
+	INodeDefManager *ndef = get_server(L)->ndef();
+
+	// Push callback function on stack
+	if(!get_item_callback(L, ndef->get(node).name.c_str(), "after_destruct"))
+		return;
+
+	// Call function
+	push_v3s16(L, p);
+	pushnode(L, node, ndef);
+	if(lua_pcall(L, 2, 0, 0))
+		script_error(L, "error: %s", lua_tostring(L, -1));
+}
+
+bool scriptapi_node_on_timer(lua_State *L, v3s16 p, MapNode node, f32 dtime)
+{
+	realitycheck(L);
+	assert(lua_checkstack(L, 20));
+	StackUnroller stack_unroller(L);
+
+	INodeDefManager *ndef = get_server(L)->ndef();
+
+	// Push callback function on stack
+	if(!get_item_callback(L, ndef->get(node).name.c_str(), "on_timer"))
+		return false;
+
+	// Call function
+	push_v3s16(L, p);
+	lua_pushnumber(L,dtime);
+	if(lua_pcall(L, 2, 1, 0))
+		script_error(L, "error: %s", lua_tostring(L, -1));
+	if((bool)lua_isboolean(L,-1) && (bool)lua_toboolean(L,-1) == true)
+		return true;
+
+	return false;
+}
+
+void scriptapi_node_on_receive_fields(lua_State *L, v3s16 p,
+		const std::string &formname,
+		const std::map<std::string, std::string> &fields,
+		ServerActiveObject *sender)
+{
+	realitycheck(L);
+	assert(lua_checkstack(L, 20));
+	StackUnroller stack_unroller(L);
+
+	INodeDefManager *ndef = get_server(L)->ndef();
+
+	// If node doesn't exist, we don't know what callback to call
+	MapNode node = get_env(L)->getMap().getNodeNoEx(p);
+	if(node.getContent() == CONTENT_IGNORE)
+		return;
+
+	// Push callback function on stack
+	if(!get_item_callback(L, ndef->get(node).name.c_str(), "on_receive_fields"))
+		return;
+
+	// Call function
+	// param 1
+	push_v3s16(L, p);
+	// param 2
+	lua_pushstring(L, formname.c_str());
+	// param 3
+	lua_newtable(L);
+	for(std::map<std::string, std::string>::const_iterator
+			i = fields.begin(); i != fields.end(); i++){
+		const std::string &name = i->first;
+		const std::string &value = i->second;
+		lua_pushstring(L, name.c_str());
+		lua_pushlstring(L, value.c_str(), value.size());
+		lua_settable(L, -3);
+	}
+	// param 4
+	objectref_get_or_create(L, sender);
+	if(lua_pcall(L, 4, 0, 0))
+		script_error(L, "error: %s", lua_tostring(L, -1));
+}
diff --git a/src/scriptapi_node.h b/src/scriptapi_node.h
new file mode 100644
index 0000000000000000000000000000000000000000..665b58bfc1934531f241fa737ead3083dbd0b154
--- /dev/null
+++ b/src/scriptapi_node.h
@@ -0,0 +1,55 @@
+/*
+Minetest-c55
+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 LUA_NODE_H_
+#define LUA_NODE_H_
+
+#include <iostream>
+#include <map>
+
+extern "C" {
+#include <lua.h>
+}
+
+#include "content_sao.h"
+#include "map.h"
+
+/*****************************************************************************/
+/* Minetest interface                                                        */
+/*****************************************************************************/
+bool scriptapi_node_on_punch(lua_State *L, v3s16 p, MapNode node,
+		ServerActiveObject *puncher);
+bool scriptapi_node_on_dig(lua_State *L, v3s16 p, MapNode node,
+		ServerActiveObject *digger);
+void scriptapi_node_on_construct(lua_State *L, v3s16 p, MapNode node);
+void scriptapi_node_on_destruct(lua_State *L, v3s16 p, MapNode node);
+void scriptapi_node_after_destruct(lua_State *L, v3s16 p, MapNode node);
+bool scriptapi_node_on_timer(lua_State *L, v3s16 p, MapNode node, f32 dtime);
+void scriptapi_node_on_receive_fields(lua_State *L, v3s16 p,
+		const std::string &formname,
+		const std::map<std::string, std::string> &fields,
+		ServerActiveObject *sender);
+
+extern struct EnumString es_DrawType[];
+extern struct EnumString es_ContentParamType[];
+extern struct EnumString es_ContentParamType2[];
+extern struct EnumString es_LiquidType[];
+extern struct EnumString es_NodeBoxType[];
+
+#endif /* LUA_NODE_H_ */
diff --git a/src/scriptapi_nodemeta.cpp b/src/scriptapi_nodemeta.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..b66c9a023ca95448391fda55818c56dc29e96801
--- /dev/null
+++ b/src/scriptapi_nodemeta.cpp
@@ -0,0 +1,571 @@
+/*
+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 "scriptapi.h"
+#include "scriptapi_nodemeta.h"
+#include "map.h"
+#include "script.h"
+#include "scriptapi_types.h"
+#include "scriptapi_inventory.h"
+#include "scriptapi_common.h"
+#include "scriptapi_item.h"
+#include "scriptapi_object.h"
+
+
+/*
+	NodeMetaRef
+*/
+NodeMetaRef* NodeMetaRef::checkobject(lua_State *L, int narg)
+{
+	luaL_checktype(L, narg, LUA_TUSERDATA);
+	void *ud = luaL_checkudata(L, narg, className);
+	if(!ud) luaL_typerror(L, narg, className);
+	return *(NodeMetaRef**)ud;  // unbox pointer
+}
+
+NodeMetadata* NodeMetaRef::getmeta(NodeMetaRef *ref, bool auto_create)
+{
+	NodeMetadata *meta = ref->m_env->getMap().getNodeMetadata(ref->m_p);
+	if(meta == NULL && auto_create)
+	{
+		meta = new NodeMetadata(ref->m_env->getGameDef());
+		ref->m_env->getMap().setNodeMetadata(ref->m_p, meta);
+	}
+	return meta;
+}
+
+void NodeMetaRef::reportMetadataChange(NodeMetaRef *ref)
+{
+	// NOTE: This same code is in rollback_interface.cpp
+	// Inform other things that the metadata has changed
+	v3s16 blockpos = getNodeBlockPos(ref->m_p);
+	MapEditEvent event;
+	event.type = MEET_BLOCK_NODE_METADATA_CHANGED;
+	event.p = blockpos;
+	ref->m_env->getMap().dispatchEvent(&event);
+	// Set the block to be saved
+	MapBlock *block = ref->m_env->getMap().getBlockNoCreateNoEx(blockpos);
+	if(block)
+		block->raiseModified(MOD_STATE_WRITE_NEEDED,
+				"NodeMetaRef::reportMetadataChange");
+}
+
+// Exported functions
+
+// garbage collector
+int NodeMetaRef::gc_object(lua_State *L) {
+	NodeMetaRef *o = *(NodeMetaRef **)(lua_touserdata(L, 1));
+	delete o;
+	return 0;
+}
+
+// get_string(self, name)
+int NodeMetaRef::l_get_string(lua_State *L)
+{
+	NodeMetaRef *ref = checkobject(L, 1);
+	std::string name = luaL_checkstring(L, 2);
+
+	NodeMetadata *meta = getmeta(ref, false);
+	if(meta == NULL){
+		lua_pushlstring(L, "", 0);
+		return 1;
+	}
+	std::string str = meta->getString(name);
+	lua_pushlstring(L, str.c_str(), str.size());
+	return 1;
+}
+
+// set_string(self, name, var)
+int NodeMetaRef::l_set_string(lua_State *L)
+{
+	NodeMetaRef *ref = checkobject(L, 1);
+	std::string name = luaL_checkstring(L, 2);
+	size_t len = 0;
+	const char *s = lua_tolstring(L, 3, &len);
+	std::string str(s, len);
+
+	NodeMetadata *meta = getmeta(ref, !str.empty());
+	if(meta == NULL || str == meta->getString(name))
+		return 0;
+	meta->setString(name, str);
+	reportMetadataChange(ref);
+	return 0;
+}
+
+// get_int(self, name)
+int NodeMetaRef::l_get_int(lua_State *L)
+{
+	NodeMetaRef *ref = checkobject(L, 1);
+	std::string name = lua_tostring(L, 2);
+
+	NodeMetadata *meta = getmeta(ref, false);
+	if(meta == NULL){
+		lua_pushnumber(L, 0);
+		return 1;
+	}
+	std::string str = meta->getString(name);
+	lua_pushnumber(L, stoi(str));
+	return 1;
+}
+
+// set_int(self, name, var)
+int NodeMetaRef::l_set_int(lua_State *L)
+{
+	NodeMetaRef *ref = checkobject(L, 1);
+	std::string name = lua_tostring(L, 2);
+	int a = lua_tointeger(L, 3);
+	std::string str = itos(a);
+
+	NodeMetadata *meta = getmeta(ref, true);
+	if(meta == NULL || str == meta->getString(name))
+		return 0;
+	meta->setString(name, str);
+	reportMetadataChange(ref);
+	return 0;
+}
+
+// get_float(self, name)
+int NodeMetaRef::l_get_float(lua_State *L)
+{
+	NodeMetaRef *ref = checkobject(L, 1);
+	std::string name = lua_tostring(L, 2);
+
+	NodeMetadata *meta = getmeta(ref, false);
+	if(meta == NULL){
+		lua_pushnumber(L, 0);
+		return 1;
+	}
+	std::string str = meta->getString(name);
+	lua_pushnumber(L, stof(str));
+	return 1;
+}
+
+// set_float(self, name, var)
+int NodeMetaRef::l_set_float(lua_State *L)
+{
+	NodeMetaRef *ref = checkobject(L, 1);
+	std::string name = lua_tostring(L, 2);
+	float a = lua_tonumber(L, 3);
+	std::string str = ftos(a);
+
+	NodeMetadata *meta = getmeta(ref, true);
+	if(meta == NULL || str == meta->getString(name))
+		return 0;
+	meta->setString(name, str);
+	reportMetadataChange(ref);
+	return 0;
+}
+
+// get_inventory(self)
+int NodeMetaRef::l_get_inventory(lua_State *L)
+{
+	NodeMetaRef *ref = checkobject(L, 1);
+	getmeta(ref, true);  // try to ensure the metadata exists
+	InvRef::createNodeMeta(L, ref->m_p);
+	return 1;
+}
+
+// to_table(self)
+int NodeMetaRef::l_to_table(lua_State *L)
+{
+	NodeMetaRef *ref = checkobject(L, 1);
+
+	NodeMetadata *meta = getmeta(ref, true);
+	if(meta == NULL){
+		lua_pushnil(L);
+		return 1;
+	}
+	lua_newtable(L);
+	// fields
+	lua_newtable(L);
+	{
+		std::map<std::string, std::string> fields = meta->getStrings();
+		for(std::map<std::string, std::string>::const_iterator
+				i = fields.begin(); i != fields.end(); i++){
+			const std::string &name = i->first;
+			const std::string &value = i->second;
+			lua_pushlstring(L, name.c_str(), name.size());
+			lua_pushlstring(L, value.c_str(), value.size());
+			lua_settable(L, -3);
+		}
+	}
+	lua_setfield(L, -2, "fields");
+	// inventory
+	lua_newtable(L);
+	Inventory *inv = meta->getInventory();
+	if(inv){
+		std::vector<const InventoryList*> lists = inv->getLists();
+		for(std::vector<const InventoryList*>::const_iterator
+				i = lists.begin(); i != lists.end(); i++){
+			inventory_get_list_to_lua(inv, (*i)->getName().c_str(), L);
+			lua_setfield(L, -2, (*i)->getName().c_str());
+		}
+	}
+	lua_setfield(L, -2, "inventory");
+	return 1;
+}
+
+// from_table(self, table)
+int NodeMetaRef::l_from_table(lua_State *L)
+{
+	NodeMetaRef *ref = checkobject(L, 1);
+	int base = 2;
+
+	if(lua_isnil(L, base)){
+		// No metadata
+		ref->m_env->getMap().removeNodeMetadata(ref->m_p);
+		lua_pushboolean(L, true);
+		return 1;
+	}
+
+	// Has metadata; clear old one first
+	ref->m_env->getMap().removeNodeMetadata(ref->m_p);
+	// Create new metadata
+	NodeMetadata *meta = getmeta(ref, true);
+	// Set fields
+	lua_getfield(L, base, "fields");
+	int fieldstable = lua_gettop(L);
+	lua_pushnil(L);
+	while(lua_next(L, fieldstable) != 0){
+		// key at index -2 and value at index -1
+		std::string name = lua_tostring(L, -2);
+		size_t cl;
+		const char *cs = lua_tolstring(L, -1, &cl);
+		std::string value(cs, cl);
+		meta->setString(name, value);
+		lua_pop(L, 1); // removes value, keeps key for next iteration
+	}
+	// Set inventory
+	Inventory *inv = meta->getInventory();
+	lua_getfield(L, base, "inventory");
+	int inventorytable = lua_gettop(L);
+	lua_pushnil(L);
+	while(lua_next(L, inventorytable) != 0){
+		// key at index -2 and value at index -1
+		std::string name = lua_tostring(L, -2);
+		inventory_set_list_from_lua(inv, name.c_str(), L, -1);
+		lua_pop(L, 1); // removes value, keeps key for next iteration
+	}
+	reportMetadataChange(ref);
+	lua_pushboolean(L, true);
+	return 1;
+}
+
+
+NodeMetaRef::NodeMetaRef(v3s16 p, ServerEnvironment *env):
+	m_p(p),
+	m_env(env)
+{
+}
+
+NodeMetaRef::~NodeMetaRef()
+{
+}
+
+// Creates an NodeMetaRef and leaves it on top of stack
+// Not callable from Lua; all references are created on the C side.
+void NodeMetaRef::create(lua_State *L, v3s16 p, ServerEnvironment *env)
+{
+	NodeMetaRef *o = new NodeMetaRef(p, env);
+	//infostream<<"NodeMetaRef::create: o="<<o<<std::endl;
+	*(void **)(lua_newuserdata(L, sizeof(void *))) = o;
+	luaL_getmetatable(L, className);
+	lua_setmetatable(L, -2);
+}
+
+void NodeMetaRef::Register(lua_State *L)
+{
+	lua_newtable(L);
+	int methodtable = lua_gettop(L);
+	luaL_newmetatable(L, className);
+	int metatable = lua_gettop(L);
+
+	lua_pushliteral(L, "__metatable");
+	lua_pushvalue(L, methodtable);
+	lua_settable(L, metatable);  // hide metatable from Lua getmetatable()
+
+	lua_pushliteral(L, "__index");
+	lua_pushvalue(L, methodtable);
+	lua_settable(L, metatable);
+
+	lua_pushliteral(L, "__gc");
+	lua_pushcfunction(L, gc_object);
+	lua_settable(L, metatable);
+
+	lua_pop(L, 1);  // drop metatable
+
+	luaL_openlib(L, 0, methods, 0);  // fill methodtable
+	lua_pop(L, 1);  // drop methodtable
+
+	// Cannot be created from Lua
+	//lua_register(L, className, create_object);
+}
+
+const char NodeMetaRef::className[] = "NodeMetaRef";
+const luaL_reg NodeMetaRef::methods[] = {
+	luamethod(NodeMetaRef, get_string),
+	luamethod(NodeMetaRef, set_string),
+	luamethod(NodeMetaRef, get_int),
+	luamethod(NodeMetaRef, set_int),
+	luamethod(NodeMetaRef, get_float),
+	luamethod(NodeMetaRef, set_float),
+	luamethod(NodeMetaRef, get_inventory),
+	luamethod(NodeMetaRef, to_table),
+	luamethod(NodeMetaRef, from_table),
+	{0,0}
+};
+
+/*
+	Node metadata inventory callbacks
+*/
+
+// Return number of accepted items to be moved
+int scriptapi_nodemeta_inventory_allow_move(lua_State *L, v3s16 p,
+		const std::string &from_list, int from_index,
+		const std::string &to_list, int to_index,
+		int count, ServerActiveObject *player)
+{
+	realitycheck(L);
+	assert(lua_checkstack(L, 20));
+	StackUnroller stack_unroller(L);
+
+	INodeDefManager *ndef = get_server(L)->ndef();
+
+	// If node doesn't exist, we don't know what callback to call
+	MapNode node = get_env(L)->getMap().getNodeNoEx(p);
+	if(node.getContent() == CONTENT_IGNORE)
+		return 0;
+
+	// Push callback function on stack
+	if(!get_item_callback(L, ndef->get(node).name.c_str(),
+			"allow_metadata_inventory_move"))
+		return count;
+
+	// function(pos, from_list, from_index, to_list, to_index, count, player)
+	// pos
+	push_v3s16(L, p);
+	// from_list
+	lua_pushstring(L, from_list.c_str());
+	// from_index
+	lua_pushinteger(L, from_index + 1);
+	// to_list
+	lua_pushstring(L, to_list.c_str());
+	// to_index
+	lua_pushinteger(L, to_index + 1);
+	// count
+	lua_pushinteger(L, count);
+	// player
+	objectref_get_or_create(L, player);
+	if(lua_pcall(L, 7, 1, 0))
+		script_error(L, "error: %s", lua_tostring(L, -1));
+	if(!lua_isnumber(L, -1))
+		throw LuaError(L, "allow_metadata_inventory_move should return a number");
+	return luaL_checkinteger(L, -1);
+}
+
+// Return number of accepted items to be put
+int scriptapi_nodemeta_inventory_allow_put(lua_State *L, v3s16 p,
+		const std::string &listname, int index, ItemStack &stack,
+		ServerActiveObject *player)
+{
+	realitycheck(L);
+	assert(lua_checkstack(L, 20));
+	StackUnroller stack_unroller(L);
+
+	INodeDefManager *ndef = get_server(L)->ndef();
+
+	// If node doesn't exist, we don't know what callback to call
+	MapNode node = get_env(L)->getMap().getNodeNoEx(p);
+	if(node.getContent() == CONTENT_IGNORE)
+		return 0;
+
+	// Push callback function on stack
+	if(!get_item_callback(L, ndef->get(node).name.c_str(),
+			"allow_metadata_inventory_put"))
+		return stack.count;
+
+	// Call function(pos, listname, index, stack, player)
+	// pos
+	push_v3s16(L, p);
+	// listname
+	lua_pushstring(L, listname.c_str());
+	// index
+	lua_pushinteger(L, index + 1);
+	// stack
+	LuaItemStack::create(L, stack);
+	// player
+	objectref_get_or_create(L, player);
+	if(lua_pcall(L, 5, 1, 0))
+		script_error(L, "error: %s", lua_tostring(L, -1));
+	if(!lua_isnumber(L, -1))
+		throw LuaError(L, "allow_metadata_inventory_put should return a number");
+	return luaL_checkinteger(L, -1);
+}
+
+// Return number of accepted items to be taken
+int scriptapi_nodemeta_inventory_allow_take(lua_State *L, v3s16 p,
+		const std::string &listname, int index, ItemStack &stack,
+		ServerActiveObject *player)
+{
+	realitycheck(L);
+	assert(lua_checkstack(L, 20));
+	StackUnroller stack_unroller(L);
+
+	INodeDefManager *ndef = get_server(L)->ndef();
+
+	// If node doesn't exist, we don't know what callback to call
+	MapNode node = get_env(L)->getMap().getNodeNoEx(p);
+	if(node.getContent() == CONTENT_IGNORE)
+		return 0;
+
+	// Push callback function on stack
+	if(!get_item_callback(L, ndef->get(node).name.c_str(),
+			"allow_metadata_inventory_take"))
+		return stack.count;
+
+	// Call function(pos, listname, index, count, player)
+	// pos
+	push_v3s16(L, p);
+	// listname
+	lua_pushstring(L, listname.c_str());
+	// index
+	lua_pushinteger(L, index + 1);
+	// stack
+	LuaItemStack::create(L, stack);
+	// player
+	objectref_get_or_create(L, player);
+	if(lua_pcall(L, 5, 1, 0))
+		script_error(L, "error: %s", lua_tostring(L, -1));
+	if(!lua_isnumber(L, -1))
+		throw LuaError(L, "allow_metadata_inventory_take should return a number");
+	return luaL_checkinteger(L, -1);
+}
+
+// Report moved items
+void scriptapi_nodemeta_inventory_on_move(lua_State *L, v3s16 p,
+		const std::string &from_list, int from_index,
+		const std::string &to_list, int to_index,
+		int count, ServerActiveObject *player)
+{
+	realitycheck(L);
+	assert(lua_checkstack(L, 20));
+	StackUnroller stack_unroller(L);
+
+	INodeDefManager *ndef = get_server(L)->ndef();
+
+	// If node doesn't exist, we don't know what callback to call
+	MapNode node = get_env(L)->getMap().getNodeNoEx(p);
+	if(node.getContent() == CONTENT_IGNORE)
+		return;
+
+	// Push callback function on stack
+	if(!get_item_callback(L, ndef->get(node).name.c_str(),
+			"on_metadata_inventory_move"))
+		return;
+
+	// function(pos, from_list, from_index, to_list, to_index, count, player)
+	// pos
+	push_v3s16(L, p);
+	// from_list
+	lua_pushstring(L, from_list.c_str());
+	// from_index
+	lua_pushinteger(L, from_index + 1);
+	// to_list
+	lua_pushstring(L, to_list.c_str());
+	// to_index
+	lua_pushinteger(L, to_index + 1);
+	// count
+	lua_pushinteger(L, count);
+	// player
+	objectref_get_or_create(L, player);
+	if(lua_pcall(L, 7, 0, 0))
+		script_error(L, "error: %s", lua_tostring(L, -1));
+}
+
+// Report put items
+void scriptapi_nodemeta_inventory_on_put(lua_State *L, v3s16 p,
+		const std::string &listname, int index, ItemStack &stack,
+		ServerActiveObject *player)
+{
+	realitycheck(L);
+	assert(lua_checkstack(L, 20));
+	StackUnroller stack_unroller(L);
+
+	INodeDefManager *ndef = get_server(L)->ndef();
+
+	// If node doesn't exist, we don't know what callback to call
+	MapNode node = get_env(L)->getMap().getNodeNoEx(p);
+	if(node.getContent() == CONTENT_IGNORE)
+		return;
+
+	// Push callback function on stack
+	if(!get_item_callback(L, ndef->get(node).name.c_str(),
+			"on_metadata_inventory_put"))
+		return;
+
+	// Call function(pos, listname, index, stack, player)
+	// pos
+	push_v3s16(L, p);
+	// listname
+	lua_pushstring(L, listname.c_str());
+	// index
+	lua_pushinteger(L, index + 1);
+	// stack
+	LuaItemStack::create(L, stack);
+	// player
+	objectref_get_or_create(L, player);
+	if(lua_pcall(L, 5, 0, 0))
+		script_error(L, "error: %s", lua_tostring(L, -1));
+}
+
+// Report taken items
+void scriptapi_nodemeta_inventory_on_take(lua_State *L, v3s16 p,
+		const std::string &listname, int index, ItemStack &stack,
+		ServerActiveObject *player)
+{
+	realitycheck(L);
+	assert(lua_checkstack(L, 20));
+	StackUnroller stack_unroller(L);
+
+	INodeDefManager *ndef = get_server(L)->ndef();
+
+	// If node doesn't exist, we don't know what callback to call
+	MapNode node = get_env(L)->getMap().getNodeNoEx(p);
+	if(node.getContent() == CONTENT_IGNORE)
+		return;
+
+	// Push callback function on stack
+	if(!get_item_callback(L, ndef->get(node).name.c_str(),
+			"on_metadata_inventory_take"))
+		return;
+
+	// Call function(pos, listname, index, stack, player)
+	// pos
+	push_v3s16(L, p);
+	// listname
+	lua_pushstring(L, listname.c_str());
+	// index
+	lua_pushinteger(L, index + 1);
+	// stack
+	LuaItemStack::create(L, stack);
+	// player
+	objectref_get_or_create(L, player);
+	if(lua_pcall(L, 5, 0, 0))
+		script_error(L, "error: %s", lua_tostring(L, -1));
+}
diff --git a/src/scriptapi_nodemeta.h b/src/scriptapi_nodemeta.h
new file mode 100644
index 0000000000000000000000000000000000000000..017abe181a23cbe8890fddaf0ecf501c7f0f0502
--- /dev/null
+++ b/src/scriptapi_nodemeta.h
@@ -0,0 +1,123 @@
+/*
+Minetest-c55
+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 LUA_NODEMETA_H_
+#define LUA_NODEMETA_H_
+
+extern "C" {
+#include <lua.h>
+#include <lauxlib.h>
+}
+
+#include "environment.h"
+#include "nodemetadata.h"
+
+/*
+	NodeMetaRef
+*/
+
+class NodeMetaRef
+{
+private:
+	v3s16 m_p;
+	ServerEnvironment *m_env;
+
+	static const char className[];
+	static const luaL_reg methods[];
+
+	static NodeMetaRef *checkobject(lua_State *L, int narg);
+
+	static NodeMetadata* getmeta(NodeMetaRef *ref, bool auto_create);
+
+	static void reportMetadataChange(NodeMetaRef *ref);
+
+	// Exported functions
+
+	// garbage collector
+	static int gc_object(lua_State *L);
+
+	// get_string(self, name)
+	static int l_get_string(lua_State *L);
+
+	// set_string(self, name, var)
+	static int l_set_string(lua_State *L);
+
+	// get_int(self, name)
+	static int l_get_int(lua_State *L);
+
+	// set_int(self, name, var)
+	static int l_set_int(lua_State *L);
+
+	// get_float(self, name)
+	static int l_get_float(lua_State *L);
+
+	// set_float(self, name, var)
+	static int l_set_float(lua_State *L);
+
+	// get_inventory(self)
+	static int l_get_inventory(lua_State *L);
+
+	// to_table(self)
+	static int l_to_table(lua_State *L);
+
+	// from_table(self, table)
+	static int l_from_table(lua_State *L);
+
+public:
+	NodeMetaRef(v3s16 p, ServerEnvironment *env);
+
+	~NodeMetaRef();
+
+	// Creates an NodeMetaRef and leaves it on top of stack
+	// Not callable from Lua; all references are created on the C side.
+	static void create(lua_State *L, v3s16 p, ServerEnvironment *env);
+
+	static void Register(lua_State *L);
+};
+
+/*****************************************************************************/
+/* Minetest interface                                                        */
+/*****************************************************************************/
+// Return number of accepted items to be moved
+int scriptapi_nodemeta_inventory_allow_move(lua_State *L, v3s16 p,
+		const std::string &from_list, int from_index,
+		const std::string &to_list, int to_index,
+		int count, ServerActiveObject *player);
+// Return number of accepted items to be put
+int scriptapi_nodemeta_inventory_allow_put(lua_State *L, v3s16 p,
+		const std::string &listname, int index, ItemStack &stack,
+		ServerActiveObject *player);
+// Return number of accepted items to be taken
+int scriptapi_nodemeta_inventory_allow_take(lua_State *L, v3s16 p,
+		const std::string &listname, int index, ItemStack &stack,
+		ServerActiveObject *player);
+// Report moved items
+void scriptapi_nodemeta_inventory_on_move(lua_State *L, v3s16 p,
+		const std::string &from_list, int from_index,
+		const std::string &to_list, int to_index,
+		int count, ServerActiveObject *player);
+// Report put items
+void scriptapi_nodemeta_inventory_on_put(lua_State *L, v3s16 p,
+		const std::string &listname, int index, ItemStack &stack,
+		ServerActiveObject *player);
+// Report taken items
+void scriptapi_nodemeta_inventory_on_take(lua_State *L, v3s16 p,
+		const std::string &listname, int index, ItemStack &stack,
+		ServerActiveObject *player);
+
+#endif //LUA_NODEMETA_H_
diff --git a/src/scriptapi_nodetimer.cpp b/src/scriptapi_nodetimer.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..5e3289aee958ca54211f341ceba9eabffc7ffa45
--- /dev/null
+++ b/src/scriptapi_nodetimer.cpp
@@ -0,0 +1,166 @@
+/*
+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 "scriptapi.h"
+#include "scriptapi_nodetimer.h"
+#include "map.h"
+
+
+int NodeTimerRef::gc_object(lua_State *L) {
+	NodeTimerRef *o = *(NodeTimerRef **)(lua_touserdata(L, 1));
+	delete o;
+	return 0;
+}
+
+NodeTimerRef* NodeTimerRef::checkobject(lua_State *L, int narg)
+{
+	luaL_checktype(L, narg, LUA_TUSERDATA);
+	void *ud = luaL_checkudata(L, narg, className);
+	if(!ud) luaL_typerror(L, narg, className);
+	return *(NodeTimerRef**)ud;  // unbox pointer
+}
+
+int NodeTimerRef::l_set(lua_State *L)
+{
+	NodeTimerRef *o = checkobject(L, 1);
+	ServerEnvironment *env = o->m_env;
+	if(env == NULL) return 0;
+	f32 t = luaL_checknumber(L,2);
+	f32 e = luaL_checknumber(L,3);
+	env->getMap().setNodeTimer(o->m_p,NodeTimer(t,e));
+	return 0;
+}
+
+int NodeTimerRef::l_start(lua_State *L)
+{
+	NodeTimerRef *o = checkobject(L, 1);
+	ServerEnvironment *env = o->m_env;
+	if(env == NULL) return 0;
+	f32 t = luaL_checknumber(L,2);
+	env->getMap().setNodeTimer(o->m_p,NodeTimer(t,0));
+	return 0;
+}
+
+int NodeTimerRef::l_stop(lua_State *L)
+{
+	NodeTimerRef *o = checkobject(L, 1);
+	ServerEnvironment *env = o->m_env;
+	if(env == NULL) return 0;
+	env->getMap().removeNodeTimer(o->m_p);
+	return 0;
+}
+
+int NodeTimerRef::l_is_started(lua_State *L)
+{
+	NodeTimerRef *o = checkobject(L, 1);
+	ServerEnvironment *env = o->m_env;
+	if(env == NULL) return 0;
+
+	NodeTimer t = env->getMap().getNodeTimer(o->m_p);
+	lua_pushboolean(L,(t.timeout != 0));
+	return 1;
+}
+
+int NodeTimerRef::l_get_timeout(lua_State *L)
+{
+	NodeTimerRef *o = checkobject(L, 1);
+	ServerEnvironment *env = o->m_env;
+	if(env == NULL) return 0;
+
+	NodeTimer t = env->getMap().getNodeTimer(o->m_p);
+	lua_pushnumber(L,t.timeout);
+	return 1;
+}
+
+int NodeTimerRef::l_get_elapsed(lua_State *L)
+{
+	NodeTimerRef *o = checkobject(L, 1);
+	ServerEnvironment *env = o->m_env;
+	if(env == NULL) return 0;
+
+	NodeTimer t = env->getMap().getNodeTimer(o->m_p);
+	lua_pushnumber(L,t.elapsed);
+	return 1;
+}
+
+
+NodeTimerRef::NodeTimerRef(v3s16 p, ServerEnvironment *env):
+	m_p(p),
+	m_env(env)
+{
+}
+
+NodeTimerRef::~NodeTimerRef()
+{
+}
+
+// Creates an NodeTimerRef and leaves it on top of stack
+// Not callable from Lua; all references are created on the C side.
+void NodeTimerRef::create(lua_State *L, v3s16 p, ServerEnvironment *env)
+{
+	NodeTimerRef *o = new NodeTimerRef(p, env);
+	*(void **)(lua_newuserdata(L, sizeof(void *))) = o;
+	luaL_getmetatable(L, className);
+	lua_setmetatable(L, -2);
+}
+
+void NodeTimerRef::set_null(lua_State *L)
+{
+	NodeTimerRef *o = checkobject(L, -1);
+	o->m_env = NULL;
+}
+
+void NodeTimerRef::Register(lua_State *L)
+{
+	lua_newtable(L);
+	int methodtable = lua_gettop(L);
+	luaL_newmetatable(L, className);
+	int metatable = lua_gettop(L);
+
+	lua_pushliteral(L, "__metatable");
+	lua_pushvalue(L, methodtable);
+	lua_settable(L, metatable);  // hide metatable from Lua getmetatable()
+
+	lua_pushliteral(L, "__index");
+	lua_pushvalue(L, methodtable);
+	lua_settable(L, metatable);
+
+	lua_pushliteral(L, "__gc");
+	lua_pushcfunction(L, gc_object);
+	lua_settable(L, metatable);
+
+	lua_pop(L, 1);  // drop metatable
+
+	luaL_openlib(L, 0, methods, 0);  // fill methodtable
+	lua_pop(L, 1);  // drop methodtable
+
+	// Cannot be created from Lua
+	//lua_register(L, className, create_object);
+}
+
+const char NodeTimerRef::className[] = "NodeTimerRef";
+const luaL_reg NodeTimerRef::methods[] = {
+	luamethod(NodeTimerRef, start),
+	luamethod(NodeTimerRef, set),
+	luamethod(NodeTimerRef, stop),
+	luamethod(NodeTimerRef, is_started),
+	luamethod(NodeTimerRef, get_timeout),
+	luamethod(NodeTimerRef, get_elapsed),
+	{0,0}
+};
diff --git a/src/scriptapi_nodetimer.h b/src/scriptapi_nodetimer.h
new file mode 100644
index 0000000000000000000000000000000000000000..a4536d9478a4452b82a2fc279edffcfee6edbd93
--- /dev/null
+++ b/src/scriptapi_nodetimer.h
@@ -0,0 +1,70 @@
+/*
+Minetest-c55
+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 LUA_NODETIMER_H_
+#define LUA_NODETIMER_H_
+
+extern "C" {
+#include <lua.h>
+#include <lauxlib.h>
+}
+
+#include "environment.h"
+
+class NodeTimerRef
+{
+private:
+	v3s16 m_p;
+	ServerEnvironment *m_env;
+
+	static const char className[];
+	static const luaL_reg methods[];
+
+	static int gc_object(lua_State *L);
+
+	static NodeTimerRef *checkobject(lua_State *L, int narg);
+
+	static int l_set(lua_State *L);
+
+	static int l_start(lua_State *L);
+
+	static int l_stop(lua_State *L);
+
+	static int l_is_started(lua_State *L);
+
+	static int l_get_timeout(lua_State *L);
+
+	static int l_get_elapsed(lua_State *L);
+
+public:
+	NodeTimerRef(v3s16 p, ServerEnvironment *env);
+	~NodeTimerRef();
+
+	// Creates an NodeTimerRef and leaves it on top of stack
+	// Not callable from Lua; all references are created on the C side.
+	static void create(lua_State *L, v3s16 p, ServerEnvironment *env);
+
+	static void set_null(lua_State *L);
+
+	static void Register(lua_State *L);
+};
+
+
+
+#endif /* LUA_NODETIMER_H_ */
diff --git a/src/scriptapi_noise.cpp b/src/scriptapi_noise.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..2c1a83c4c5f10adbcd5c28166112e73f8a76fd55
--- /dev/null
+++ b/src/scriptapi_noise.cpp
@@ -0,0 +1,388 @@
+/*
+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 "scriptapi.h"
+#include "scriptapi_noise.h"
+#include "scriptapi_types.h"
+#include "script.h"
+
+// garbage collector
+int LuaPerlinNoise::gc_object(lua_State *L)
+{
+	LuaPerlinNoise *o = *(LuaPerlinNoise **)(lua_touserdata(L, 1));
+	delete o;
+	return 0;
+}
+
+int LuaPerlinNoise::l_get2d(lua_State *L)
+{
+	LuaPerlinNoise *o = checkobject(L, 1);
+	v2f pos2d = read_v2f(L,2);
+	lua_Number val = noise2d_perlin(pos2d.X/o->scale, pos2d.Y/o->scale, o->seed, o->octaves, o->persistence);
+	lua_pushnumber(L, val);
+	return 1;
+}
+int LuaPerlinNoise::l_get3d(lua_State *L)
+{
+	LuaPerlinNoise *o = checkobject(L, 1);
+	v3f pos3d = read_v3f(L,2);
+	lua_Number val = noise3d_perlin(pos3d.X/o->scale, pos3d.Y/o->scale, pos3d.Z/o->scale, o->seed, o->octaves, o->persistence);
+	lua_pushnumber(L, val);
+	return 1;
+}
+
+
+LuaPerlinNoise::LuaPerlinNoise(int a_seed, int a_octaves, float a_persistence,
+		float a_scale):
+	seed(a_seed),
+	octaves(a_octaves),
+	persistence(a_persistence),
+	scale(a_scale)
+{
+}
+
+LuaPerlinNoise::~LuaPerlinNoise()
+{
+}
+
+// LuaPerlinNoise(seed, octaves, persistence, scale)
+// Creates an LuaPerlinNoise and leaves it on top of stack
+int LuaPerlinNoise::create_object(lua_State *L)
+{
+	int seed = luaL_checkint(L, 1);
+	int octaves = luaL_checkint(L, 2);
+	float persistence = luaL_checknumber(L, 3);
+	float scale = luaL_checknumber(L, 4);
+	LuaPerlinNoise *o = new LuaPerlinNoise(seed, octaves, persistence, scale);
+	*(void **)(lua_newuserdata(L, sizeof(void *))) = o;
+	luaL_getmetatable(L, className);
+	lua_setmetatable(L, -2);
+	return 1;
+}
+
+LuaPerlinNoise* LuaPerlinNoise::checkobject(lua_State *L, int narg)
+{
+	luaL_checktype(L, narg, LUA_TUSERDATA);
+	void *ud = luaL_checkudata(L, narg, className);
+	if(!ud) luaL_typerror(L, narg, className);
+	return *(LuaPerlinNoise**)ud;  // unbox pointer
+}
+
+void LuaPerlinNoise::Register(lua_State *L)
+{
+	lua_newtable(L);
+	int methodtable = lua_gettop(L);
+	luaL_newmetatable(L, className);
+	int metatable = lua_gettop(L);
+
+	lua_pushliteral(L, "__metatable");
+	lua_pushvalue(L, methodtable);
+	lua_settable(L, metatable);  // hide metatable from Lua getmetatable()
+
+	lua_pushliteral(L, "__index");
+	lua_pushvalue(L, methodtable);
+	lua_settable(L, metatable);
+
+	lua_pushliteral(L, "__gc");
+	lua_pushcfunction(L, gc_object);
+	lua_settable(L, metatable);
+
+	lua_pop(L, 1);  // drop metatable
+
+	luaL_openlib(L, 0, methods, 0);  // fill methodtable
+	lua_pop(L, 1);  // drop methodtable
+
+	// Can be created from Lua (PerlinNoise(seed, octaves, persistence)
+	lua_register(L, className, create_object);
+}
+
+const char LuaPerlinNoise::className[] = "PerlinNoise";
+const luaL_reg LuaPerlinNoise::methods[] = {
+	luamethod(LuaPerlinNoise, get2d),
+	luamethod(LuaPerlinNoise, get3d),
+	{0,0}
+};
+
+/*
+  PerlinNoiseMap
+ */
+
+
+int LuaPerlinNoiseMap::gc_object(lua_State *L)
+{
+	LuaPerlinNoiseMap *o = *(LuaPerlinNoiseMap **)(lua_touserdata(L, 1));
+	delete o;
+	return 0;
+}
+
+int LuaPerlinNoiseMap::l_get2dMap(lua_State *L)
+{
+	int i = 0;
+
+	LuaPerlinNoiseMap *o = checkobject(L, 1);
+	v2f p = read_v2f(L, 2);
+
+	Noise *n = o->noise;
+	n->perlinMap2D(p.X, p.Y);
+
+	lua_newtable(L);
+	for (int y = 0; y != n->sy; y++) {
+		lua_newtable(L);
+		for (int x = 0; x != n->sx; x++) {
+			float noiseval = n->np->offset + n->np->scale * n->result[i++];
+			lua_pushnumber(L, noiseval);
+			lua_rawseti(L, -2, x + 1);
+		}
+		lua_rawseti(L, -2, y + 1);
+	}
+	return 1;
+}
+
+int LuaPerlinNoiseMap::l_get3dMap(lua_State *L)
+{
+	int i = 0;
+
+	LuaPerlinNoiseMap *o = checkobject(L, 1);
+	v3f p = read_v3f(L, 2);
+
+	Noise *n = o->noise;
+	n->perlinMap3D(p.X, p.Y, p.Z);
+
+	lua_newtable(L);
+	for (int z = 0; z != n->sz; z++) {
+		lua_newtable(L);
+		for (int y = 0; y != n->sy; y++) {
+			lua_newtable(L);
+			for (int x = 0; x != n->sx; x++) {
+				lua_pushnumber(L, n->np->offset + n->np->scale * n->result[i++]);
+				lua_rawseti(L, -2, x + 1);
+			}
+			lua_rawseti(L, -2, y + 1);
+		}
+		lua_rawseti(L, -2, z + 1);
+	}
+	return 1;
+}
+
+LuaPerlinNoiseMap::LuaPerlinNoiseMap(NoiseParams *np, int seed, v3s16 size) {
+	noise = new Noise(np, seed, size.X, size.Y, size.Z);
+}
+
+LuaPerlinNoiseMap::~LuaPerlinNoiseMap()
+{
+	delete noise->np;
+	delete noise;
+}
+
+// LuaPerlinNoiseMap(np, size)
+// Creates an LuaPerlinNoiseMap and leaves it on top of stack
+int LuaPerlinNoiseMap::create_object(lua_State *L)
+{
+	NoiseParams *np = read_noiseparams(L, 1);
+	if (!np)
+		return 0;
+	v3s16 size = read_v3s16(L, 2);
+
+	LuaPerlinNoiseMap *o = new LuaPerlinNoiseMap(np, 0, size);
+	*(void **)(lua_newuserdata(L, sizeof(void *))) = o;
+	luaL_getmetatable(L, className);
+	lua_setmetatable(L, -2);
+	return 1;
+}
+
+LuaPerlinNoiseMap* LuaPerlinNoiseMap::checkobject(lua_State *L, int narg)
+{
+	luaL_checktype(L, narg, LUA_TUSERDATA);
+
+	void *ud = luaL_checkudata(L, narg, className);
+	if (!ud)
+		luaL_typerror(L, narg, className);
+
+	return *(LuaPerlinNoiseMap **)ud;  // unbox pointer
+}
+
+void LuaPerlinNoiseMap::Register(lua_State *L)
+{
+	lua_newtable(L);
+	int methodtable = lua_gettop(L);
+	luaL_newmetatable(L, className);
+	int metatable = lua_gettop(L);
+
+	lua_pushliteral(L, "__metatable");
+	lua_pushvalue(L, methodtable);
+	lua_settable(L, metatable);  // hide metatable from Lua getmetatable()
+
+	lua_pushliteral(L, "__index");
+	lua_pushvalue(L, methodtable);
+	lua_settable(L, metatable);
+
+	lua_pushliteral(L, "__gc");
+	lua_pushcfunction(L, gc_object);
+	lua_settable(L, metatable);
+
+	lua_pop(L, 1);  // drop metatable
+
+	luaL_openlib(L, 0, methods, 0);  // fill methodtable
+	lua_pop(L, 1);  // drop methodtable
+
+	// Can be created from Lua (PerlinNoiseMap(np, size)
+	lua_register(L, className, create_object);
+}
+
+const char LuaPerlinNoiseMap::className[] = "PerlinNoiseMap";
+const luaL_reg LuaPerlinNoiseMap::methods[] = {
+	luamethod(LuaPerlinNoiseMap, get2dMap),
+	luamethod(LuaPerlinNoiseMap, get3dMap),
+	{0,0}
+};
+
+/*
+	NoiseParams
+*/
+NoiseParams *read_noiseparams(lua_State *L, int index)
+{
+	if (index < 0)
+		index = lua_gettop(L) + 1 + index;
+
+	if (!lua_istable(L, index))
+		return NULL;
+
+	NoiseParams *np = new NoiseParams;
+
+	np->offset  = getfloatfield_default(L, index, "offset", 0.0);
+	np->scale   = getfloatfield_default(L, index, "scale", 0.0);
+	lua_getfield(L, index, "spread");
+	np->spread  = read_v3f(L, -1);
+	lua_pop(L, 1);
+	np->seed    = getintfield_default(L, index, "seed", 0);
+	np->octaves = getintfield_default(L, index, "octaves", 0);
+	np->persist = getfloatfield_default(L, index, "persist", 0.0);
+
+	return np;
+}
+
+
+/*
+	LuaPseudoRandom
+*/
+
+// garbage collector
+int LuaPseudoRandom::gc_object(lua_State *L)
+{
+	LuaPseudoRandom *o = *(LuaPseudoRandom **)(lua_touserdata(L, 1));
+	delete o;
+	return 0;
+}
+
+// next(self, min=0, max=32767) -> get next value
+int LuaPseudoRandom::l_next(lua_State *L)
+{
+	LuaPseudoRandom *o = checkobject(L, 1);
+	int min = 0;
+	int max = 32767;
+	lua_settop(L, 3); // Fill 2 and 3 with nil if they don't exist
+	if(!lua_isnil(L, 2))
+		min = luaL_checkinteger(L, 2);
+	if(!lua_isnil(L, 3))
+		max = luaL_checkinteger(L, 3);
+	if(max < min){
+		errorstream<<"PseudoRandom.next(): max="<<max<<" min="<<min<<std::endl;
+		throw LuaError(L, "PseudoRandom.next(): max < min");
+	}
+	if(max - min != 32767 && max - min > 32767/5)
+		throw LuaError(L, "PseudoRandom.next() max-min is not 32767 and is > 32768/5. This is disallowed due to the bad random distribution the implementation would otherwise make.");
+	PseudoRandom &pseudo = o->m_pseudo;
+	int val = pseudo.next();
+	val = (val % (max-min+1)) + min;
+	lua_pushinteger(L, val);
+	return 1;
+}
+
+
+LuaPseudoRandom::LuaPseudoRandom(int seed):
+	m_pseudo(seed)
+{
+}
+
+LuaPseudoRandom::~LuaPseudoRandom()
+{
+}
+
+const PseudoRandom& LuaPseudoRandom::getItem() const
+{
+	return m_pseudo;
+}
+PseudoRandom& LuaPseudoRandom::getItem()
+{
+	return m_pseudo;
+}
+
+// LuaPseudoRandom(seed)
+// Creates an LuaPseudoRandom and leaves it on top of stack
+int LuaPseudoRandom::create_object(lua_State *L)
+{
+	int seed = luaL_checknumber(L, 1);
+	LuaPseudoRandom *o = new LuaPseudoRandom(seed);
+	*(void **)(lua_newuserdata(L, sizeof(void *))) = o;
+	luaL_getmetatable(L, className);
+	lua_setmetatable(L, -2);
+	return 1;
+}
+
+LuaPseudoRandom* LuaPseudoRandom::checkobject(lua_State *L, int narg)
+{
+	luaL_checktype(L, narg, LUA_TUSERDATA);
+	void *ud = luaL_checkudata(L, narg, className);
+	if(!ud) luaL_typerror(L, narg, className);
+	return *(LuaPseudoRandom**)ud;  // unbox pointer
+}
+
+void LuaPseudoRandom::Register(lua_State *L)
+{
+	lua_newtable(L);
+	int methodtable = lua_gettop(L);
+	luaL_newmetatable(L, className);
+	int metatable = lua_gettop(L);
+
+	lua_pushliteral(L, "__metatable");
+	lua_pushvalue(L, methodtable);
+	lua_settable(L, metatable);  // hide metatable from Lua getmetatable()
+
+	lua_pushliteral(L, "__index");
+	lua_pushvalue(L, methodtable);
+	lua_settable(L, metatable);
+
+	lua_pushliteral(L, "__gc");
+	lua_pushcfunction(L, gc_object);
+	lua_settable(L, metatable);
+
+	lua_pop(L, 1);  // drop metatable
+
+	luaL_openlib(L, 0, methods, 0);  // fill methodtable
+	lua_pop(L, 1);  // drop methodtable
+
+	// Can be created from Lua (LuaPseudoRandom(seed))
+	lua_register(L, className, create_object);
+}
+
+const char LuaPseudoRandom::className[] = "PseudoRandom";
+const luaL_reg LuaPseudoRandom::methods[] = {
+	luamethod(LuaPseudoRandom, next),
+	{0,0}
+};
diff --git a/src/scriptapi_noise.h b/src/scriptapi_noise.h
new file mode 100644
index 0000000000000000000000000000000000000000..a02383fde86fea4e235d9e0d53cd1ce455a4dea6
--- /dev/null
+++ b/src/scriptapi_noise.h
@@ -0,0 +1,133 @@
+/*
+Minetest-c55
+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 LUA_PERLIN_H_
+#define LUA_PERLIN_H_
+
+extern "C" {
+#include <lua.h>
+#include <lauxlib.h>
+}
+
+#include "noise.h"
+
+class LuaPerlinNoise
+{
+private:
+	int seed;
+	int octaves;
+	float persistence;
+	float scale;
+	static const char className[];
+	static const luaL_reg methods[];
+
+	// Exported functions
+
+	// garbage collector
+	static int gc_object(lua_State *L);
+
+	static int l_get2d(lua_State *L);
+	static int l_get3d(lua_State *L);
+
+public:
+	LuaPerlinNoise(int a_seed, int a_octaves, float a_persistence,
+			float a_scale);
+
+	~LuaPerlinNoise();
+
+	// LuaPerlinNoise(seed, octaves, persistence, scale)
+	// Creates an LuaPerlinNoise and leaves it on top of stack
+	static int create_object(lua_State *L);
+
+	static LuaPerlinNoise* checkobject(lua_State *L, int narg);
+
+	static void Register(lua_State *L);
+};
+
+/*
+  PerlinNoiseMap
+ */
+class LuaPerlinNoiseMap
+{
+private:
+	Noise *noise;
+	static const char className[];
+	static const luaL_reg methods[];
+
+	static int gc_object(lua_State *L);
+
+	static int l_get2dMap(lua_State *L);
+
+	static int l_get3dMap(lua_State *L);
+
+public:
+	LuaPerlinNoiseMap(NoiseParams *np, int seed, v3s16 size);
+
+	~LuaPerlinNoiseMap();
+
+	// LuaPerlinNoiseMap(np, size)
+	// Creates an LuaPerlinNoiseMap and leaves it on top of stack
+	static int create_object(lua_State *L);
+
+	static LuaPerlinNoiseMap *checkobject(lua_State *L, int narg);
+
+	static void Register(lua_State *L);
+};
+
+/*
+	LuaPseudoRandom
+*/
+
+
+class LuaPseudoRandom
+{
+private:
+	PseudoRandom m_pseudo;
+
+	static const char className[];
+	static const luaL_reg methods[];
+
+	// Exported functions
+
+	// garbage collector
+	static int gc_object(lua_State *L);
+
+	// next(self, min=0, max=32767) -> get next value
+	static int l_next(lua_State *L);
+
+public:
+	LuaPseudoRandom(int seed);
+
+	~LuaPseudoRandom();
+
+	const PseudoRandom& getItem() const;
+	PseudoRandom& getItem();
+
+	// LuaPseudoRandom(seed)
+	// Creates an LuaPseudoRandom and leaves it on top of stack
+	static int create_object(lua_State *L);
+
+	static LuaPseudoRandom* checkobject(lua_State *L, int narg);
+
+	static void Register(lua_State *L);
+};
+
+NoiseParams *read_noiseparams(lua_State *L, int index);
+
+#endif /* LUA_PERLIN_H_ */
diff --git a/src/scriptapi_object.cpp b/src/scriptapi_object.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..a0f93cbbad33ec0ce5eafe6eb3792c5a668c9926
--- /dev/null
+++ b/src/scriptapi_object.cpp
@@ -0,0 +1,945 @@
+/*
+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 "scriptapi.h"
+#include "scriptapi_object.h"
+#include "log.h"
+#include "tool.h"
+#include "scriptapi_types.h"
+#include "scriptapi_inventory.h"
+#include "scriptapi_item.h"
+#include "scriptapi_entity.h"
+#include "scriptapi_common.h"
+
+/*
+	ObjectRef
+*/
+
+
+ObjectRef* ObjectRef::checkobject(lua_State *L, int narg)
+{
+	luaL_checktype(L, narg, LUA_TUSERDATA);
+	void *ud = luaL_checkudata(L, narg, className);
+	if(!ud) luaL_typerror(L, narg, className);
+	return *(ObjectRef**)ud;  // unbox pointer
+}
+
+ServerActiveObject* ObjectRef::getobject(ObjectRef *ref)
+{
+	ServerActiveObject *co = ref->m_object;
+	return co;
+}
+
+LuaEntitySAO* ObjectRef::getluaobject(ObjectRef *ref)
+{
+	ServerActiveObject *obj = getobject(ref);
+	if(obj == NULL)
+		return NULL;
+	if(obj->getType() != ACTIVEOBJECT_TYPE_LUAENTITY)
+		return NULL;
+	return (LuaEntitySAO*)obj;
+}
+
+PlayerSAO* ObjectRef::getplayersao(ObjectRef *ref)
+{
+	ServerActiveObject *obj = getobject(ref);
+	if(obj == NULL)
+		return NULL;
+	if(obj->getType() != ACTIVEOBJECT_TYPE_PLAYER)
+		return NULL;
+	return (PlayerSAO*)obj;
+}
+
+Player* ObjectRef::getplayer(ObjectRef *ref)
+{
+	PlayerSAO *playersao = getplayersao(ref);
+	if(playersao == NULL)
+		return NULL;
+	return playersao->getPlayer();
+}
+
+// Exported functions
+
+// garbage collector
+int ObjectRef::gc_object(lua_State *L) {
+	ObjectRef *o = *(ObjectRef **)(lua_touserdata(L, 1));
+	//infostream<<"ObjectRef::gc_object: o="<<o<<std::endl;
+	delete o;
+	return 0;
+}
+
+// remove(self)
+int ObjectRef::l_remove(lua_State *L)
+{
+	ObjectRef *ref = checkobject(L, 1);
+	ServerActiveObject *co = getobject(ref);
+	if(co == NULL) return 0;
+	verbosestream<<"ObjectRef::l_remove(): id="<<co->getId()<<std::endl;
+	co->m_removed = true;
+	return 0;
+}
+
+// getpos(self)
+// returns: {x=num, y=num, z=num}
+int ObjectRef::l_getpos(lua_State *L)
+{
+	ObjectRef *ref = checkobject(L, 1);
+	ServerActiveObject *co = getobject(ref);
+	if(co == NULL) return 0;
+	v3f pos = co->getBasePosition() / BS;
+	lua_newtable(L);
+	lua_pushnumber(L, pos.X);
+	lua_setfield(L, -2, "x");
+	lua_pushnumber(L, pos.Y);
+	lua_setfield(L, -2, "y");
+	lua_pushnumber(L, pos.Z);
+	lua_setfield(L, -2, "z");
+	return 1;
+}
+
+// setpos(self, pos)
+int ObjectRef::l_setpos(lua_State *L)
+{
+	ObjectRef *ref = checkobject(L, 1);
+	//LuaEntitySAO *co = getluaobject(ref);
+	ServerActiveObject *co = getobject(ref);
+	if(co == NULL) return 0;
+	// pos
+	v3f pos = checkFloatPos(L, 2);
+	// Do it
+	co->setPos(pos);
+	return 0;
+}
+
+// moveto(self, pos, continuous=false)
+int ObjectRef::l_moveto(lua_State *L)
+{
+	ObjectRef *ref = checkobject(L, 1);
+	//LuaEntitySAO *co = getluaobject(ref);
+	ServerActiveObject *co = getobject(ref);
+	if(co == NULL) return 0;
+	// pos
+	v3f pos = checkFloatPos(L, 2);
+	// continuous
+	bool continuous = lua_toboolean(L, 3);
+	// Do it
+	co->moveTo(pos, continuous);
+	return 0;
+}
+
+// punch(self, puncher, time_from_last_punch, tool_capabilities, dir)
+int ObjectRef::l_punch(lua_State *L)
+{
+	ObjectRef *ref = checkobject(L, 1);
+	ObjectRef *puncher_ref = checkobject(L, 2);
+	ServerActiveObject *co = getobject(ref);
+	ServerActiveObject *puncher = getobject(puncher_ref);
+	if(co == NULL) return 0;
+	if(puncher == NULL) return 0;
+	v3f dir;
+	if(lua_type(L, 5) != LUA_TTABLE)
+		dir = co->getBasePosition() - puncher->getBasePosition();
+	else
+		dir = read_v3f(L, 5);
+	float time_from_last_punch = 1000000;
+	if(lua_isnumber(L, 3))
+		time_from_last_punch = lua_tonumber(L, 3);
+	ToolCapabilities toolcap = read_tool_capabilities(L, 4);
+	dir.normalize();
+	// Do it
+	co->punch(dir, &toolcap, puncher, time_from_last_punch);
+	return 0;
+}
+
+// right_click(self, clicker); clicker = an another ObjectRef
+int ObjectRef::l_right_click(lua_State *L)
+{
+	ObjectRef *ref = checkobject(L, 1);
+	ObjectRef *ref2 = checkobject(L, 2);
+	ServerActiveObject *co = getobject(ref);
+	ServerActiveObject *co2 = getobject(ref2);
+	if(co == NULL) return 0;
+	if(co2 == NULL) return 0;
+	// Do it
+	co->rightClick(co2);
+	return 0;
+}
+
+// set_hp(self, hp)
+// hp = number of hitpoints (2 * number of hearts)
+// returns: nil
+int ObjectRef::l_set_hp(lua_State *L)
+{
+	ObjectRef *ref = checkobject(L, 1);
+	luaL_checknumber(L, 2);
+	ServerActiveObject *co = getobject(ref);
+	if(co == NULL) return 0;
+	int hp = lua_tonumber(L, 2);
+	/*infostream<<"ObjectRef::l_set_hp(): id="<<co->getId()
+			<<" hp="<<hp<<std::endl;*/
+	// Do it
+	co->setHP(hp);
+	// Return
+	return 0;
+}
+
+// get_hp(self)
+// returns: number of hitpoints (2 * number of hearts)
+// 0 if not applicable to this type of object
+int ObjectRef::l_get_hp(lua_State *L)
+{
+	ObjectRef *ref = checkobject(L, 1);
+	ServerActiveObject *co = getobject(ref);
+	if(co == NULL){
+		// Default hp is 1
+		lua_pushnumber(L, 1);
+		return 1;
+	}
+	int hp = co->getHP();
+	/*infostream<<"ObjectRef::l_get_hp(): id="<<co->getId()
+			<<" hp="<<hp<<std::endl;*/
+	// Return
+	lua_pushnumber(L, hp);
+	return 1;
+}
+
+// get_inventory(self)
+int ObjectRef::l_get_inventory(lua_State *L)
+{
+	ObjectRef *ref = checkobject(L, 1);
+	ServerActiveObject *co = getobject(ref);
+	if(co == NULL) return 0;
+	// Do it
+	InventoryLocation loc = co->getInventoryLocation();
+	if(get_server(L)->getInventory(loc) != NULL)
+		InvRef::create(L, loc);
+	else
+		lua_pushnil(L); // An object may have no inventory (nil)
+	return 1;
+}
+
+// get_wield_list(self)
+int ObjectRef::l_get_wield_list(lua_State *L)
+{
+	ObjectRef *ref = checkobject(L, 1);
+	ServerActiveObject *co = getobject(ref);
+	if(co == NULL) return 0;
+	// Do it
+	lua_pushstring(L, co->getWieldList().c_str());
+	return 1;
+}
+
+// get_wield_index(self)
+int ObjectRef::l_get_wield_index(lua_State *L)
+{
+	ObjectRef *ref = checkobject(L, 1);
+	ServerActiveObject *co = getobject(ref);
+	if(co == NULL) return 0;
+	// Do it
+	lua_pushinteger(L, co->getWieldIndex() + 1);
+	return 1;
+}
+
+// get_wielded_item(self)
+int ObjectRef::l_get_wielded_item(lua_State *L)
+{
+	ObjectRef *ref = checkobject(L, 1);
+	ServerActiveObject *co = getobject(ref);
+	if(co == NULL){
+		// Empty ItemStack
+		LuaItemStack::create(L, ItemStack());
+		return 1;
+	}
+	// Do it
+	LuaItemStack::create(L, co->getWieldedItem());
+	return 1;
+}
+
+// set_wielded_item(self, itemstack or itemstring or table or nil)
+int ObjectRef::l_set_wielded_item(lua_State *L)
+{
+	ObjectRef *ref = checkobject(L, 1);
+	ServerActiveObject *co = getobject(ref);
+	if(co == NULL) return 0;
+	// Do it
+	ItemStack item = read_item(L, 2);
+	bool success = co->setWieldedItem(item);
+	lua_pushboolean(L, success);
+	return 1;
+}
+
+// set_armor_groups(self, groups)
+int ObjectRef::l_set_armor_groups(lua_State *L)
+{
+	ObjectRef *ref = checkobject(L, 1);
+	ServerActiveObject *co = getobject(ref);
+	if(co == NULL) return 0;
+	// Do it
+	ItemGroupList groups;
+	read_groups(L, 2, groups);
+	co->setArmorGroups(groups);
+	return 0;
+}
+
+// set_animation(self, frame_range, frame_speed, frame_blend)
+int ObjectRef::l_set_animation(lua_State *L)
+{
+	ObjectRef *ref = checkobject(L, 1);
+	ServerActiveObject *co = getobject(ref);
+	if(co == NULL) return 0;
+	// Do it
+	v2f frames = v2f(1, 1);
+	if(!lua_isnil(L, 2))
+		frames = read_v2f(L, 2);
+	float frame_speed = 15;
+	if(!lua_isnil(L, 3))
+		frame_speed = lua_tonumber(L, 3);
+	float frame_blend = 0;
+	if(!lua_isnil(L, 4))
+		frame_blend = lua_tonumber(L, 4);
+	co->setAnimation(frames, frame_speed, frame_blend);
+	return 0;
+}
+
+// set_bone_position(self, std::string bone, v3f position, v3f rotation)
+int ObjectRef::l_set_bone_position(lua_State *L)
+{
+	ObjectRef *ref = checkobject(L, 1);
+	ServerActiveObject *co = getobject(ref);
+	if(co == NULL) return 0;
+	// Do it
+	std::string bone = "";
+	if(!lua_isnil(L, 2))
+		bone = lua_tostring(L, 2);
+	v3f position = v3f(0, 0, 0);
+	if(!lua_isnil(L, 3))
+		position = read_v3f(L, 3);
+	v3f rotation = v3f(0, 0, 0);
+	if(!lua_isnil(L, 4))
+		rotation = read_v3f(L, 4);
+	co->setBonePosition(bone, position, rotation);
+	return 0;
+}
+
+// set_attach(self, parent, bone, position, rotation)
+int ObjectRef::l_set_attach(lua_State *L)
+{
+	ObjectRef *ref = checkobject(L, 1);
+	ObjectRef *parent_ref = checkobject(L, 2);
+	ServerActiveObject *co = getobject(ref);
+	ServerActiveObject *parent = getobject(parent_ref);
+	if(co == NULL) return 0;
+	if(parent == NULL) return 0;
+	// Do it
+	std::string bone = "";
+	if(!lua_isnil(L, 3))
+		bone = lua_tostring(L, 3);
+	v3f position = v3f(0, 0, 0);
+	if(!lua_isnil(L, 4))
+		position = read_v3f(L, 4);
+	v3f rotation = v3f(0, 0, 0);
+	if(!lua_isnil(L, 5))
+		rotation = read_v3f(L, 5);
+	co->setAttachment(parent->getId(), bone, position, rotation);
+	return 0;
+}
+
+// set_detach(self)
+int ObjectRef::l_set_detach(lua_State *L)
+{
+	ObjectRef *ref = checkobject(L, 1);
+	ServerActiveObject *co = getobject(ref);
+	if(co == NULL) return 0;
+	// Do it
+	co->setAttachment(0, "", v3f(0,0,0), v3f(0,0,0));
+	return 0;
+}
+
+// set_properties(self, properties)
+int ObjectRef::l_set_properties(lua_State *L)
+{
+	ObjectRef *ref = checkobject(L, 1);
+	ServerActiveObject *co = getobject(ref);
+	if(co == NULL) return 0;
+	ObjectProperties *prop = co->accessObjectProperties();
+	if(!prop)
+		return 0;
+	read_object_properties(L, 2, prop);
+	co->notifyObjectPropertiesModified();
+	return 0;
+}
+
+/* LuaEntitySAO-only */
+
+// setvelocity(self, {x=num, y=num, z=num})
+int ObjectRef::l_setvelocity(lua_State *L)
+{
+	ObjectRef *ref = checkobject(L, 1);
+	LuaEntitySAO *co = getluaobject(ref);
+	if(co == NULL) return 0;
+	v3f pos = checkFloatPos(L, 2);
+	// Do it
+	co->setVelocity(pos);
+	return 0;
+}
+
+// getvelocity(self)
+int ObjectRef::l_getvelocity(lua_State *L)
+{
+	ObjectRef *ref = checkobject(L, 1);
+	LuaEntitySAO *co = getluaobject(ref);
+	if(co == NULL) return 0;
+	// Do it
+	v3f v = co->getVelocity();
+	pushFloatPos(L, v);
+	return 1;
+}
+
+// setacceleration(self, {x=num, y=num, z=num})
+int ObjectRef::l_setacceleration(lua_State *L)
+{
+	ObjectRef *ref = checkobject(L, 1);
+	LuaEntitySAO *co = getluaobject(ref);
+	if(co == NULL) return 0;
+	// pos
+	v3f pos = checkFloatPos(L, 2);
+	// Do it
+	co->setAcceleration(pos);
+	return 0;
+}
+
+// getacceleration(self)
+int ObjectRef::l_getacceleration(lua_State *L)
+{
+	ObjectRef *ref = checkobject(L, 1);
+	LuaEntitySAO *co = getluaobject(ref);
+	if(co == NULL) return 0;
+	// Do it
+	v3f v = co->getAcceleration();
+	pushFloatPos(L, v);
+	return 1;
+}
+
+// setyaw(self, radians)
+int ObjectRef::l_setyaw(lua_State *L)
+{
+	ObjectRef *ref = checkobject(L, 1);
+	LuaEntitySAO *co = getluaobject(ref);
+	if(co == NULL) return 0;
+	float yaw = luaL_checknumber(L, 2) * core::RADTODEG;
+	// Do it
+	co->setYaw(yaw);
+	return 0;
+}
+
+// getyaw(self)
+int ObjectRef::l_getyaw(lua_State *L)
+{
+	ObjectRef *ref = checkobject(L, 1);
+	LuaEntitySAO *co = getluaobject(ref);
+	if(co == NULL) return 0;
+	// Do it
+	float yaw = co->getYaw() * core::DEGTORAD;
+	lua_pushnumber(L, yaw);
+	return 1;
+}
+
+// settexturemod(self, mod)
+int ObjectRef::l_settexturemod(lua_State *L)
+{
+	ObjectRef *ref = checkobject(L, 1);
+	LuaEntitySAO *co = getluaobject(ref);
+	if(co == NULL) return 0;
+	// Do it
+	std::string mod = luaL_checkstring(L, 2);
+	co->setTextureMod(mod);
+	return 0;
+}
+
+// setsprite(self, p={x=0,y=0}, num_frames=1, framelength=0.2,
+//           select_horiz_by_yawpitch=false)
+int ObjectRef::l_setsprite(lua_State *L)
+{
+	ObjectRef *ref = checkobject(L, 1);
+	LuaEntitySAO *co = getluaobject(ref);
+	if(co == NULL) return 0;
+	// Do it
+	v2s16 p(0,0);
+	if(!lua_isnil(L, 2))
+		p = read_v2s16(L, 2);
+	int num_frames = 1;
+	if(!lua_isnil(L, 3))
+		num_frames = lua_tonumber(L, 3);
+	float framelength = 0.2;
+	if(!lua_isnil(L, 4))
+		framelength = lua_tonumber(L, 4);
+	bool select_horiz_by_yawpitch = false;
+	if(!lua_isnil(L, 5))
+		select_horiz_by_yawpitch = lua_toboolean(L, 5);
+	co->setSprite(p, num_frames, framelength, select_horiz_by_yawpitch);
+	return 0;
+}
+
+// DEPRECATED
+// get_entity_name(self)
+int ObjectRef::l_get_entity_name(lua_State *L)
+{
+	ObjectRef *ref = checkobject(L, 1);
+	LuaEntitySAO *co = getluaobject(ref);
+	if(co == NULL) return 0;
+	// Do it
+	std::string name = co->getName();
+	lua_pushstring(L, name.c_str());
+	return 1;
+}
+
+// get_luaentity(self)
+int ObjectRef::l_get_luaentity(lua_State *L)
+{
+	ObjectRef *ref = checkobject(L, 1);
+	LuaEntitySAO *co = getluaobject(ref);
+	if(co == NULL) return 0;
+	// Do it
+	luaentity_get(L, co->getId());
+	return 1;
+}
+
+/* Player-only */
+
+// is_player(self)
+int ObjectRef::l_is_player(lua_State *L)
+{
+	ObjectRef *ref = checkobject(L, 1);
+	Player *player = getplayer(ref);
+	lua_pushboolean(L, (player != NULL));
+	return 1;
+}
+
+// get_player_name(self)
+int ObjectRef::l_get_player_name(lua_State *L)
+{
+	ObjectRef *ref = checkobject(L, 1);
+	Player *player = getplayer(ref);
+	if(player == NULL){
+		lua_pushlstring(L, "", 0);
+		return 1;
+	}
+	// Do it
+	lua_pushstring(L, player->getName());
+	return 1;
+}
+
+// get_look_dir(self)
+int ObjectRef::l_get_look_dir(lua_State *L)
+{
+	ObjectRef *ref = checkobject(L, 1);
+	Player *player = getplayer(ref);
+	if(player == NULL) return 0;
+	// Do it
+	float pitch = player->getRadPitch();
+	float yaw = player->getRadYaw();
+	v3f v(cos(pitch)*cos(yaw), sin(pitch), cos(pitch)*sin(yaw));
+	push_v3f(L, v);
+	return 1;
+}
+
+// get_look_pitch(self)
+int ObjectRef::l_get_look_pitch(lua_State *L)
+{
+	ObjectRef *ref = checkobject(L, 1);
+	Player *player = getplayer(ref);
+	if(player == NULL) return 0;
+	// Do it
+	lua_pushnumber(L, player->getRadPitch());
+	return 1;
+}
+
+// get_look_yaw(self)
+int ObjectRef::l_get_look_yaw(lua_State *L)
+{
+	ObjectRef *ref = checkobject(L, 1);
+	Player *player = getplayer(ref);
+	if(player == NULL) return 0;
+	// Do it
+	lua_pushnumber(L, player->getRadYaw());
+	return 1;
+}
+
+// set_look_pitch(self, radians)
+int ObjectRef::l_set_look_pitch(lua_State *L)
+{
+	ObjectRef *ref = checkobject(L, 1);
+	PlayerSAO* co = getplayersao(ref);
+	if(co == NULL) return 0;
+	float pitch = luaL_checknumber(L, 2) * core::RADTODEG;
+	// Do it
+	co->setPitch(pitch);
+	return 1;
+}
+
+// set_look_yaw(self, radians)
+int ObjectRef::l_set_look_yaw(lua_State *L)
+{
+	ObjectRef *ref = checkobject(L, 1);
+	PlayerSAO* co = getplayersao(ref);
+	if(co == NULL) return 0;
+	float yaw = luaL_checknumber(L, 2) * core::RADTODEG;
+	// Do it
+	co->setYaw(yaw);
+	return 1;
+}
+
+// set_inventory_formspec(self, formspec)
+int ObjectRef::l_set_inventory_formspec(lua_State *L)
+{
+	ObjectRef *ref = checkobject(L, 1);
+	Player *player = getplayer(ref);
+	if(player == NULL) return 0;
+	std::string formspec = luaL_checkstring(L, 2);
+
+	player->inventory_formspec = formspec;
+	get_server(L)->reportInventoryFormspecModified(player->getName());
+	lua_pushboolean(L, true);
+	return 1;
+}
+
+// get_inventory_formspec(self) -> formspec
+int ObjectRef::l_get_inventory_formspec(lua_State *L)
+{
+	ObjectRef *ref = checkobject(L, 1);
+	Player *player = getplayer(ref);
+	if(player == NULL) return 0;
+
+	std::string formspec = player->inventory_formspec;
+	lua_pushlstring(L, formspec.c_str(), formspec.size());
+	return 1;
+}
+
+// get_player_control(self)
+int ObjectRef::l_get_player_control(lua_State *L)
+{
+	ObjectRef *ref = checkobject(L, 1);
+	Player *player = getplayer(ref);
+	if(player == NULL){
+		lua_pushlstring(L, "", 0);
+		return 1;
+	}
+	// Do it
+	PlayerControl control = player->getPlayerControl();
+	lua_newtable(L);
+	lua_pushboolean(L, control.up);
+	lua_setfield(L, -2, "up");
+	lua_pushboolean(L, control.down);
+	lua_setfield(L, -2, "down");
+	lua_pushboolean(L, control.left);
+	lua_setfield(L, -2, "left");
+	lua_pushboolean(L, control.right);
+	lua_setfield(L, -2, "right");
+	lua_pushboolean(L, control.jump);
+	lua_setfield(L, -2, "jump");
+	lua_pushboolean(L, control.aux1);
+	lua_setfield(L, -2, "aux1");
+	lua_pushboolean(L, control.sneak);
+	lua_setfield(L, -2, "sneak");
+	lua_pushboolean(L, control.LMB);
+	lua_setfield(L, -2, "LMB");
+	lua_pushboolean(L, control.RMB);
+	lua_setfield(L, -2, "RMB");
+	return 1;
+}
+
+// get_player_control_bits(self)
+int ObjectRef::l_get_player_control_bits(lua_State *L)
+{
+	ObjectRef *ref = checkobject(L, 1);
+	Player *player = getplayer(ref);
+	if(player == NULL){
+		lua_pushlstring(L, "", 0);
+		return 1;
+	}
+	// Do it
+	lua_pushnumber(L, player->keyPressed);
+	return 1;
+}
+
+
+ObjectRef::ObjectRef(ServerActiveObject *object):
+	m_object(object)
+{
+	//infostream<<"ObjectRef created for id="<<m_object->getId()<<std::endl;
+}
+
+ObjectRef::~ObjectRef()
+{
+	/*if(m_object)
+		infostream<<"ObjectRef destructing for id="
+				<<m_object->getId()<<std::endl;
+	else
+		infostream<<"ObjectRef destructing for id=unknown"<<std::endl;*/
+}
+
+// Creates an ObjectRef and leaves it on top of stack
+// Not callable from Lua; all references are created on the C side.
+void ObjectRef::create(lua_State *L, ServerActiveObject *object)
+{
+	ObjectRef *o = new ObjectRef(object);
+	//infostream<<"ObjectRef::create: o="<<o<<std::endl;
+	*(void **)(lua_newuserdata(L, sizeof(void *))) = o;
+	luaL_getmetatable(L, className);
+	lua_setmetatable(L, -2);
+}
+
+void ObjectRef::set_null(lua_State *L)
+{
+	ObjectRef *o = checkobject(L, -1);
+	o->m_object = NULL;
+}
+
+void ObjectRef::Register(lua_State *L)
+{
+	lua_newtable(L);
+	int methodtable = lua_gettop(L);
+	luaL_newmetatable(L, className);
+	int metatable = lua_gettop(L);
+
+	lua_pushliteral(L, "__metatable");
+	lua_pushvalue(L, methodtable);
+	lua_settable(L, metatable);  // hide metatable from Lua getmetatable()
+
+	lua_pushliteral(L, "__index");
+	lua_pushvalue(L, methodtable);
+	lua_settable(L, metatable);
+
+	lua_pushliteral(L, "__gc");
+	lua_pushcfunction(L, gc_object);
+	lua_settable(L, metatable);
+
+	lua_pop(L, 1);  // drop metatable
+
+	luaL_openlib(L, 0, methods, 0);  // fill methodtable
+	lua_pop(L, 1);  // drop methodtable
+
+	// Cannot be created from Lua
+	//lua_register(L, className, create_object);
+}
+
+const char ObjectRef::className[] = "ObjectRef";
+const luaL_reg ObjectRef::methods[] = {
+	// ServerActiveObject
+	luamethod(ObjectRef, remove),
+	luamethod(ObjectRef, getpos),
+	luamethod(ObjectRef, setpos),
+	luamethod(ObjectRef, moveto),
+	luamethod(ObjectRef, punch),
+	luamethod(ObjectRef, right_click),
+	luamethod(ObjectRef, set_hp),
+	luamethod(ObjectRef, get_hp),
+	luamethod(ObjectRef, get_inventory),
+	luamethod(ObjectRef, get_wield_list),
+	luamethod(ObjectRef, get_wield_index),
+	luamethod(ObjectRef, get_wielded_item),
+	luamethod(ObjectRef, set_wielded_item),
+	luamethod(ObjectRef, set_armor_groups),
+	luamethod(ObjectRef, set_animation),
+	luamethod(ObjectRef, set_bone_position),
+	luamethod(ObjectRef, set_attach),
+	luamethod(ObjectRef, set_detach),
+	luamethod(ObjectRef, set_properties),
+	// LuaEntitySAO-only
+	luamethod(ObjectRef, setvelocity),
+	luamethod(ObjectRef, getvelocity),
+	luamethod(ObjectRef, setacceleration),
+	luamethod(ObjectRef, getacceleration),
+	luamethod(ObjectRef, setyaw),
+	luamethod(ObjectRef, getyaw),
+	luamethod(ObjectRef, settexturemod),
+	luamethod(ObjectRef, setsprite),
+	luamethod(ObjectRef, get_entity_name),
+	luamethod(ObjectRef, get_luaentity),
+	// Player-only
+	luamethod(ObjectRef, is_player),
+	luamethod(ObjectRef, get_player_name),
+	luamethod(ObjectRef, get_look_dir),
+	luamethod(ObjectRef, get_look_pitch),
+	luamethod(ObjectRef, get_look_yaw),
+	luamethod(ObjectRef, set_look_yaw),
+	luamethod(ObjectRef, set_look_pitch),
+	luamethod(ObjectRef, set_inventory_formspec),
+	luamethod(ObjectRef, get_inventory_formspec),
+	luamethod(ObjectRef, get_player_control),
+	luamethod(ObjectRef, get_player_control_bits),
+	{0,0}
+};
+
+// Creates a new anonymous reference if cobj=NULL or id=0
+void objectref_get_or_create(lua_State *L,
+		ServerActiveObject *cobj)
+{
+	if(cobj == NULL || cobj->getId() == 0){
+		ObjectRef::create(L, cobj);
+	} else {
+		objectref_get(L, cobj->getId());
+	}
+}
+
+void objectref_get(lua_State *L, u16 id)
+{
+	// Get minetest.object_refs[i]
+	lua_getglobal(L, "minetest");
+	lua_getfield(L, -1, "object_refs");
+	luaL_checktype(L, -1, LUA_TTABLE);
+	lua_pushnumber(L, id);
+	lua_gettable(L, -2);
+	lua_remove(L, -2); // object_refs
+	lua_remove(L, -2); // minetest
+}
+
+/*
+	ObjectProperties
+*/
+
+void read_object_properties(lua_State *L, int index,
+		ObjectProperties *prop)
+{
+	if(index < 0)
+		index = lua_gettop(L) + 1 + index;
+	if(!lua_istable(L, index))
+		return;
+
+	prop->hp_max = getintfield_default(L, -1, "hp_max", 10);
+
+	getboolfield(L, -1, "physical", prop->physical);
+
+	getfloatfield(L, -1, "weight", prop->weight);
+
+	lua_getfield(L, -1, "collisionbox");
+	if(lua_istable(L, -1))
+		prop->collisionbox = read_aabb3f(L, -1, 1.0);
+	lua_pop(L, 1);
+
+	getstringfield(L, -1, "visual", prop->visual);
+
+	getstringfield(L, -1, "mesh", prop->mesh);
+
+	lua_getfield(L, -1, "visual_size");
+	if(lua_istable(L, -1))
+		prop->visual_size = read_v2f(L, -1);
+	lua_pop(L, 1);
+
+	lua_getfield(L, -1, "textures");
+	if(lua_istable(L, -1)){
+		prop->textures.clear();
+		int table = lua_gettop(L);
+		lua_pushnil(L);
+		while(lua_next(L, table) != 0){
+			// key at index -2 and value at index -1
+			if(lua_isstring(L, -1))
+				prop->textures.push_back(lua_tostring(L, -1));
+			else
+				prop->textures.push_back("");
+			// removes value, keeps key for next iteration
+			lua_pop(L, 1);
+		}
+	}
+	lua_pop(L, 1);
+
+	lua_getfield(L, -1, "colors");
+	if(lua_istable(L, -1)){
+		prop->colors.clear();
+		int table = lua_gettop(L);
+		lua_pushnil(L);
+		while(lua_next(L, table) != 0){
+			// key at index -2 and value at index -1
+			if(lua_isstring(L, -1))
+				prop->colors.push_back(readARGB8(L, -1));
+			else
+				prop->colors.push_back(video::SColor(255, 255, 255, 255));
+			// removes value, keeps key for next iteration
+			lua_pop(L, 1);
+		}
+	}
+	lua_pop(L, 1);
+
+	lua_getfield(L, -1, "spritediv");
+	if(lua_istable(L, -1))
+		prop->spritediv = read_v2s16(L, -1);
+	lua_pop(L, 1);
+
+	lua_getfield(L, -1, "initial_sprite_basepos");
+	if(lua_istable(L, -1))
+		prop->initial_sprite_basepos = read_v2s16(L, -1);
+	lua_pop(L, 1);
+
+	getboolfield(L, -1, "is_visible", prop->is_visible);
+	getboolfield(L, -1, "makes_footstep_sound", prop->makes_footstep_sound);
+	getfloatfield(L, -1, "automatic_rotate", prop->automatic_rotate);
+}
+
+/*
+	object_reference
+*/
+
+void scriptapi_add_object_reference(lua_State *L, ServerActiveObject *cobj)
+{
+	realitycheck(L);
+	assert(lua_checkstack(L, 20));
+	//infostream<<"scriptapi_add_object_reference: id="<<cobj->getId()<<std::endl;
+	StackUnroller stack_unroller(L);
+
+	// Create object on stack
+	ObjectRef::create(L, cobj); // Puts ObjectRef (as userdata) on stack
+	int object = lua_gettop(L);
+
+	// Get minetest.object_refs table
+	lua_getglobal(L, "minetest");
+	lua_getfield(L, -1, "object_refs");
+	luaL_checktype(L, -1, LUA_TTABLE);
+	int objectstable = lua_gettop(L);
+
+	// object_refs[id] = object
+	lua_pushnumber(L, cobj->getId()); // Push id
+	lua_pushvalue(L, object); // Copy object to top of stack
+	lua_settable(L, objectstable);
+}
+
+void scriptapi_rm_object_reference(lua_State *L, ServerActiveObject *cobj)
+{
+	realitycheck(L);
+	assert(lua_checkstack(L, 20));
+	//infostream<<"scriptapi_rm_object_reference: id="<<cobj->getId()<<std::endl;
+	StackUnroller stack_unroller(L);
+
+	// Get minetest.object_refs table
+	lua_getglobal(L, "minetest");
+	lua_getfield(L, -1, "object_refs");
+	luaL_checktype(L, -1, LUA_TTABLE);
+	int objectstable = lua_gettop(L);
+
+	// Get object_refs[id]
+	lua_pushnumber(L, cobj->getId()); // Push id
+	lua_gettable(L, objectstable);
+	// Set object reference to NULL
+	ObjectRef::set_null(L);
+	lua_pop(L, 1); // pop object
+
+	// Set object_refs[id] = nil
+	lua_pushnumber(L, cobj->getId()); // Push id
+	lua_pushnil(L);
+	lua_settable(L, objectstable);
+}
diff --git a/src/scriptapi_object.h b/src/scriptapi_object.h
new file mode 100644
index 0000000000000000000000000000000000000000..a37abbb781f0545cd1ae95c88d159b8a159fbe2f
--- /dev/null
+++ b/src/scriptapi_object.h
@@ -0,0 +1,220 @@
+/*
+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 LUA_OBJECT_H_
+#define LUA_OBJECT_H_
+
+extern "C" {
+#include <lua.h>
+#include <lauxlib.h>
+}
+
+#include "serverobject.h"
+#include "content_sao.h"
+#include "player.h"
+
+/*
+	ObjectRef
+*/
+
+class ObjectRef
+{
+private:
+	ServerActiveObject *m_object;
+
+	static const char className[];
+	static const luaL_reg methods[];
+public:
+	static ObjectRef *checkobject(lua_State *L, int narg);
+
+	static ServerActiveObject* getobject(ObjectRef *ref);
+private:
+	static LuaEntitySAO* getluaobject(ObjectRef *ref);
+
+	static PlayerSAO* getplayersao(ObjectRef *ref);
+
+	static Player* getplayer(ObjectRef *ref);
+
+	// Exported functions
+
+	// garbage collector
+	static int gc_object(lua_State *L);
+
+	// remove(self)
+	static int l_remove(lua_State *L);
+
+	// getpos(self)
+	// returns: {x=num, y=num, z=num}
+	static int l_getpos(lua_State *L);
+
+	// setpos(self, pos)
+	static int l_setpos(lua_State *L);
+
+	// moveto(self, pos, continuous=false)
+	static int l_moveto(lua_State *L);
+
+	// punch(self, puncher, time_from_last_punch, tool_capabilities, dir)
+	static int l_punch(lua_State *L);
+
+	// right_click(self, clicker); clicker = an another ObjectRef
+	static int l_right_click(lua_State *L);
+
+	// set_hp(self, hp)
+	// hp = number of hitpoints (2 * number of hearts)
+	// returns: nil
+	static int l_set_hp(lua_State *L);
+
+	// get_hp(self)
+	// returns: number of hitpoints (2 * number of hearts)
+	// 0 if not applicable to this type of object
+	static int l_get_hp(lua_State *L);
+
+	// get_inventory(self)
+	static int l_get_inventory(lua_State *L);
+
+	// get_wield_list(self)
+	static int l_get_wield_list(lua_State *L);
+
+	// get_wield_index(self)
+	static int l_get_wield_index(lua_State *L);
+
+	// get_wielded_item(self)
+	static int l_get_wielded_item(lua_State *L);
+
+	// set_wielded_item(self, itemstack or itemstring or table or nil)
+	static int l_set_wielded_item(lua_State *L);
+
+	// set_armor_groups(self, groups)
+	static int l_set_armor_groups(lua_State *L);
+
+	// set_animation(self, frame_range, frame_speed, frame_blend)
+	static int l_set_animation(lua_State *L);
+
+	// set_bone_position(self, std::string bone, v3f position, v3f rotation)
+	static int l_set_bone_position(lua_State *L);
+
+	// set_attach(self, parent, bone, position, rotation)
+	static int l_set_attach(lua_State *L);
+
+	// set_detach(self)
+	static int l_set_detach(lua_State *L);
+
+	// set_properties(self, properties)
+	static int l_set_properties(lua_State *L);
+
+	/* LuaEntitySAO-only */
+
+	// setvelocity(self, {x=num, y=num, z=num})
+	static int l_setvelocity(lua_State *L);
+
+	// getvelocity(self)
+	static int l_getvelocity(lua_State *L);
+
+	// setacceleration(self, {x=num, y=num, z=num})
+	static int l_setacceleration(lua_State *L);
+
+	// getacceleration(self)
+	static int l_getacceleration(lua_State *L);
+
+	// setyaw(self, radians)
+	static int l_setyaw(lua_State *L);
+
+	// getyaw(self)
+	static int l_getyaw(lua_State *L);
+
+	// settexturemod(self, mod)
+	static int l_settexturemod(lua_State *L);
+
+	// setsprite(self, p={x=0,y=0}, num_frames=1, framelength=0.2,
+	//           select_horiz_by_yawpitch=false)
+	static int l_setsprite(lua_State *L);
+
+	// DEPRECATED
+	// get_entity_name(self)
+	static int l_get_entity_name(lua_State *L);
+
+	// get_luaentity(self)
+	static int l_get_luaentity(lua_State *L);
+
+	/* Player-only */
+
+	// is_player(self)
+	static int l_is_player(lua_State *L);
+
+	// get_player_name(self)
+	static int l_get_player_name(lua_State *L);
+
+	// get_look_dir(self)
+	static int l_get_look_dir(lua_State *L);
+
+	// get_look_pitch(self)
+	static int l_get_look_pitch(lua_State *L);
+
+	// get_look_yaw(self)
+	static int l_get_look_yaw(lua_State *L);
+
+	// set_look_pitch(self, radians)
+	static int l_set_look_pitch(lua_State *L);
+
+	// set_look_yaw(self, radians)
+	static int l_set_look_yaw(lua_State *L);
+
+	// set_inventory_formspec(self, formspec)
+	static int l_set_inventory_formspec(lua_State *L);
+
+	// get_inventory_formspec(self) -> formspec
+	static int l_get_inventory_formspec(lua_State *L);
+
+	// get_player_control(self)
+	static int l_get_player_control(lua_State *L);
+
+	// get_player_control_bits(self)
+	static int l_get_player_control_bits(lua_State *L);
+
+public:
+	ObjectRef(ServerActiveObject *object);
+
+	~ObjectRef();
+
+	// Creates an ObjectRef and leaves it on top of stack
+	// Not callable from Lua; all references are created on the C side.
+	static void create(lua_State *L, ServerActiveObject *object);
+
+	static void set_null(lua_State *L);
+
+	static void Register(lua_State *L);
+};
+
+/*****************************************************************************/
+/* scriptapi internal                                                        */
+/*****************************************************************************/
+// Creates a new anonymous reference if cobj=NULL or id=0
+void objectref_get_or_create(lua_State *L,
+		ServerActiveObject *cobj);
+void objectref_get(lua_State *L, u16 id);
+void read_object_properties(lua_State *L, int index,
+		ObjectProperties *prop);
+
+/*****************************************************************************/
+/* Minetest interface                                                        */
+/*****************************************************************************/
+void scriptapi_add_object_reference(lua_State *L, ServerActiveObject *cobj);
+void scriptapi_rm_object_reference(lua_State *L, ServerActiveObject *cobj);
+
+#endif /* LUA_OBJECT_H_ */
diff --git a/src/scriptapi_particles.cpp b/src/scriptapi_particles.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..dc9b3776e82876c6f7bb6312ec6b14f382a2fd28
--- /dev/null
+++ b/src/scriptapi_particles.cpp
@@ -0,0 +1,143 @@
+/*
+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 "scriptapi.h"
+#include "scriptapi_particles.h"
+#include "server.h"
+#include "script.h"
+#include "scriptapi_types.h"
+#include "scriptapi_common.h"
+
+// add_particle(pos, velocity, acceleration, expirationtime,
+// 		size, collisiondetection, texture, player)
+// pos/velocity/acceleration = {x=num, y=num, z=num}
+// expirationtime = num (seconds)
+// size = num
+// texture = e.g."default_wood.png"
+int l_add_particle(lua_State *L)
+{
+	// Get server from registry
+	Server *server = get_server(L);
+	// Get parameters
+	v3f pos = check_v3f(L, 1);
+	v3f vel = check_v3f(L, 2);
+	v3f acc = check_v3f(L, 3);
+	float expirationtime = luaL_checknumber(L, 4);
+	float size = luaL_checknumber(L, 5);
+	bool collisiondetection = lua_toboolean(L, 6);
+	std::string texture = luaL_checkstring(L, 7);
+
+	if (lua_gettop(L) == 8) // only spawn for a single player
+	{
+		const char *playername = luaL_checkstring(L, 8);
+		server->spawnParticle(playername,
+			pos, vel, acc, expirationtime,
+			size, collisiondetection, texture);
+	}
+	else // spawn for all players
+	{
+		server->spawnParticleAll(pos, vel, acc,
+			expirationtime, size, collisiondetection, texture);
+	}
+	return 1;
+}
+
+// add_particlespawner(amount, time,
+//				minpos, maxpos,
+//				minvel, maxvel,
+//				minacc, maxacc,
+//				minexptime, maxexptime,
+//				minsize, maxsize,
+//				collisiondetection,
+//				texture,
+//				player)
+// minpos/maxpos/minvel/maxvel/minacc/maxacc = {x=num, y=num, z=num}
+// minexptime/maxexptime = num (seconds)
+// minsize/maxsize = num
+// collisiondetection = bool
+// texture = e.g."default_wood.png"
+int l_add_particlespawner(lua_State *L)
+{
+	// Get server from registry
+	Server *server = get_server(L);
+	// Get parameters
+	u16 amount = luaL_checknumber(L, 1);
+	float time = luaL_checknumber(L, 2);
+	v3f minpos = check_v3f(L, 3);
+	v3f maxpos = check_v3f(L, 4);
+	v3f minvel = check_v3f(L, 5);
+	v3f maxvel = check_v3f(L, 6);
+	v3f minacc = check_v3f(L, 7);
+	v3f maxacc = check_v3f(L, 8);
+	float minexptime = luaL_checknumber(L, 9);
+	float maxexptime = luaL_checknumber(L, 10);
+	float minsize = luaL_checknumber(L, 11);
+	float maxsize = luaL_checknumber(L, 12);
+	bool collisiondetection = lua_toboolean(L, 13);
+	std::string texture = luaL_checkstring(L, 14);
+
+	if (lua_gettop(L) == 15) // only spawn for a single player
+	{
+		const char *playername = luaL_checkstring(L, 15);
+		u32 id = server->addParticleSpawner(playername,
+							amount, time,
+							minpos, maxpos,
+							minvel, maxvel,
+							minacc, maxacc,
+							minexptime, maxexptime,
+							minsize, maxsize,
+							collisiondetection,
+							texture);
+		lua_pushnumber(L, id);
+	}
+	else // spawn for all players
+	{
+		u32 id = server->addParticleSpawnerAll(	amount, time,
+							minpos, maxpos,
+							minvel, maxvel,
+							minacc, maxacc,
+							minexptime, maxexptime,
+							minsize, maxsize,
+							collisiondetection,
+							texture);
+		lua_pushnumber(L, id);
+	}
+	return 1;
+}
+
+// delete_particlespawner(id, player)
+// player (string) is optional
+int l_delete_particlespawner(lua_State *L)
+{
+	// Get server from registry
+	Server *server = get_server(L);
+	// Get parameters
+	u32 id = luaL_checknumber(L, 1);
+
+	if (lua_gettop(L) == 2) // only delete for one player
+	{
+		const char *playername = luaL_checkstring(L, 2);
+		server->deleteParticleSpawner(playername, id);
+	}
+	else // delete for all players
+	{
+		server->deleteParticleSpawnerAll(id);
+	}
+	return 1;
+}
diff --git a/src/scriptapi_particles.h b/src/scriptapi_particles.h
new file mode 100644
index 0000000000000000000000000000000000000000..4b37d7ce19fcea9cca089dcc1c92a41b4a03883f
--- /dev/null
+++ b/src/scriptapi_particles.h
@@ -0,0 +1,32 @@
+/*
+Minetest-c55
+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 LUA_PARTICLES_H_
+#define LUA_PARTICLES_H_
+
+extern "C" {
+#include <lua.h>
+#include <lauxlib.h>
+}
+
+int l_add_particle(lua_State *L);
+int l_add_particlespawner(lua_State *L);
+int l_delete_particlespawner(lua_State *L);
+
+#endif
diff --git a/src/scriptapi_types.cpp b/src/scriptapi_types.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..3d06f1623e6776684eb18825e5f14201e241ce21
--- /dev/null
+++ b/src/scriptapi_types.cpp
@@ -0,0 +1,372 @@
+/*
+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 "scriptapi.h"
+#include "scriptapi_types.h"
+
+extern "C" {
+#include <lauxlib.h>
+}
+
+#include "util/numeric.h"
+#include "nodedef.h"
+
+/*
+	C struct <-> Lua table converter functions
+*/
+
+void push_v3f(lua_State *L, v3f p)
+{
+	lua_newtable(L);
+	lua_pushnumber(L, p.X);
+	lua_setfield(L, -2, "x");
+	lua_pushnumber(L, p.Y);
+	lua_setfield(L, -2, "y");
+	lua_pushnumber(L, p.Z);
+	lua_setfield(L, -2, "z");
+}
+
+v2s16 read_v2s16(lua_State *L, int index)
+{
+	v2s16 p;
+	luaL_checktype(L, index, LUA_TTABLE);
+	lua_getfield(L, index, "x");
+	p.X = lua_tonumber(L, -1);
+	lua_pop(L, 1);
+	lua_getfield(L, index, "y");
+	p.Y = lua_tonumber(L, -1);
+	lua_pop(L, 1);
+	return p;
+}
+
+v2f read_v2f(lua_State *L, int index)
+{
+	v2f p;
+	luaL_checktype(L, index, LUA_TTABLE);
+	lua_getfield(L, index, "x");
+	p.X = lua_tonumber(L, -1);
+	lua_pop(L, 1);
+	lua_getfield(L, index, "y");
+	p.Y = lua_tonumber(L, -1);
+	lua_pop(L, 1);
+	return p;
+}
+
+v3f read_v3f(lua_State *L, int index)
+{
+	v3f pos;
+	luaL_checktype(L, index, LUA_TTABLE);
+	lua_getfield(L, index, "x");
+	pos.X = lua_tonumber(L, -1);
+	lua_pop(L, 1);
+	lua_getfield(L, index, "y");
+	pos.Y = lua_tonumber(L, -1);
+	lua_pop(L, 1);
+	lua_getfield(L, index, "z");
+	pos.Z = lua_tonumber(L, -1);
+	lua_pop(L, 1);
+	return pos;
+}
+
+v3f check_v3f(lua_State *L, int index)
+{
+	v3f pos;
+	luaL_checktype(L, index, LUA_TTABLE);
+	lua_getfield(L, index, "x");
+	pos.X = luaL_checknumber(L, -1);
+	lua_pop(L, 1);
+	lua_getfield(L, index, "y");
+	pos.Y = luaL_checknumber(L, -1);
+	lua_pop(L, 1);
+	lua_getfield(L, index, "z");
+	pos.Z = luaL_checknumber(L, -1);
+	lua_pop(L, 1);
+	return pos;
+}
+
+void pushFloatPos(lua_State *L, v3f p)
+{
+	p /= BS;
+	push_v3f(L, p);
+}
+
+v3f checkFloatPos(lua_State *L, int index)
+{
+	return check_v3f(L, index) * BS;
+}
+
+void push_v3s16(lua_State *L, v3s16 p)
+{
+	lua_newtable(L);
+	lua_pushnumber(L, p.X);
+	lua_setfield(L, -2, "x");
+	lua_pushnumber(L, p.Y);
+	lua_setfield(L, -2, "y");
+	lua_pushnumber(L, p.Z);
+	lua_setfield(L, -2, "z");
+}
+
+v3s16 read_v3s16(lua_State *L, int index)
+{
+	// Correct rounding at <0
+	v3f pf = read_v3f(L, index);
+	return floatToInt(pf, 1.0);
+}
+
+v3s16 check_v3s16(lua_State *L, int index)
+{
+	// Correct rounding at <0
+	v3f pf = check_v3f(L, index);
+	return floatToInt(pf, 1.0);
+}
+
+video::SColor readARGB8(lua_State *L, int index)
+{
+	video::SColor color;
+	luaL_checktype(L, index, LUA_TTABLE);
+	lua_getfield(L, index, "a");
+	if(lua_isnumber(L, -1))
+		color.setAlpha(lua_tonumber(L, -1));
+	lua_pop(L, 1);
+	lua_getfield(L, index, "r");
+	color.setRed(lua_tonumber(L, -1));
+	lua_pop(L, 1);
+	lua_getfield(L, index, "g");
+	color.setGreen(lua_tonumber(L, -1));
+	lua_pop(L, 1);
+	lua_getfield(L, index, "b");
+	color.setBlue(lua_tonumber(L, -1));
+	lua_pop(L, 1);
+	return color;
+}
+
+aabb3f read_aabb3f(lua_State *L, int index, f32 scale)
+{
+	aabb3f box;
+	if(lua_istable(L, index)){
+		lua_rawgeti(L, index, 1);
+		box.MinEdge.X = lua_tonumber(L, -1) * scale;
+		lua_pop(L, 1);
+		lua_rawgeti(L, index, 2);
+		box.MinEdge.Y = lua_tonumber(L, -1) * scale;
+		lua_pop(L, 1);
+		lua_rawgeti(L, index, 3);
+		box.MinEdge.Z = lua_tonumber(L, -1) * scale;
+		lua_pop(L, 1);
+		lua_rawgeti(L, index, 4);
+		box.MaxEdge.X = lua_tonumber(L, -1) * scale;
+		lua_pop(L, 1);
+		lua_rawgeti(L, index, 5);
+		box.MaxEdge.Y = lua_tonumber(L, -1) * scale;
+		lua_pop(L, 1);
+		lua_rawgeti(L, index, 6);
+		box.MaxEdge.Z = lua_tonumber(L, -1) * scale;
+		lua_pop(L, 1);
+	}
+	return box;
+}
+
+std::vector<aabb3f> read_aabb3f_vector(lua_State *L, int index, f32 scale)
+{
+	std::vector<aabb3f> boxes;
+	if(lua_istable(L, index)){
+		int n = lua_objlen(L, index);
+		// Check if it's a single box or a list of boxes
+		bool possibly_single_box = (n == 6);
+		for(int i = 1; i <= n && possibly_single_box; i++){
+			lua_rawgeti(L, index, i);
+			if(!lua_isnumber(L, -1))
+				possibly_single_box = false;
+			lua_pop(L, 1);
+		}
+		if(possibly_single_box){
+			// Read a single box
+			boxes.push_back(read_aabb3f(L, index, scale));
+		} else {
+			// Read a list of boxes
+			for(int i = 1; i <= n; i++){
+				lua_rawgeti(L, index, i);
+				boxes.push_back(read_aabb3f(L, -1, scale));
+				lua_pop(L, 1);
+			}
+		}
+	}
+	return boxes;
+}
+
+/*
+	Table field getters
+*/
+
+bool getstringfield(lua_State *L, int table,
+		const char *fieldname, std::string &result)
+{
+	lua_getfield(L, table, fieldname);
+	bool got = false;
+	if(lua_isstring(L, -1)){
+		size_t len = 0;
+		const char *ptr = lua_tolstring(L, -1, &len);
+		result.assign(ptr, len);
+		got = true;
+	}
+	lua_pop(L, 1);
+	return got;
+}
+
+bool getintfield(lua_State *L, int table,
+		const char *fieldname, int &result)
+{
+	lua_getfield(L, table, fieldname);
+	bool got = false;
+	if(lua_isnumber(L, -1)){
+		result = lua_tonumber(L, -1);
+		got = true;
+	}
+	lua_pop(L, 1);
+	return got;
+}
+
+bool getfloatfield(lua_State *L, int table,
+		const char *fieldname, float &result)
+{
+	lua_getfield(L, table, fieldname);
+	bool got = false;
+	if(lua_isnumber(L, -1)){
+		result = lua_tonumber(L, -1);
+		got = true;
+	}
+	lua_pop(L, 1);
+	return got;
+}
+
+bool getboolfield(lua_State *L, int table,
+		const char *fieldname, bool &result)
+{
+	lua_getfield(L, table, fieldname);
+	bool got = false;
+	if(lua_isboolean(L, -1)){
+		result = lua_toboolean(L, -1);
+		got = true;
+	}
+	lua_pop(L, 1);
+	return got;
+}
+
+std::string checkstringfield(lua_State *L, int table,
+		const char *fieldname)
+{
+	lua_getfield(L, table, fieldname);
+	std::string s = luaL_checkstring(L, -1);
+	lua_pop(L, 1);
+	return s;
+}
+
+std::string getstringfield_default(lua_State *L, int table,
+		const char *fieldname, const std::string &default_)
+{
+	std::string result = default_;
+	getstringfield(L, table, fieldname, result);
+	return result;
+}
+
+int getintfield_default(lua_State *L, int table,
+		const char *fieldname, int default_)
+{
+	int result = default_;
+	getintfield(L, table, fieldname, result);
+	return result;
+}
+
+float getfloatfield_default(lua_State *L, int table,
+		const char *fieldname, float default_)
+{
+	float result = default_;
+	getfloatfield(L, table, fieldname, result);
+	return result;
+}
+
+bool getboolfield_default(lua_State *L, int table,
+		const char *fieldname, bool default_)
+{
+	bool result = default_;
+	getboolfield(L, table, fieldname, result);
+	return result;
+}
+
+void setintfield(lua_State *L, int table,
+		const char *fieldname, int value)
+{
+	lua_pushinteger(L, value);
+	if(table < 0)
+		table -= 1;
+	lua_setfield(L, table, fieldname);
+}
+
+void setfloatfield(lua_State *L, int table,
+		const char *fieldname, float value)
+{
+	lua_pushnumber(L, value);
+	if(table < 0)
+		table -= 1;
+	lua_setfield(L, table, fieldname);
+}
+
+void setboolfield(lua_State *L, int table,
+		const char *fieldname, bool value)
+{
+	lua_pushboolean(L, value);
+	if(table < 0)
+		table -= 1;
+	lua_setfield(L, table, fieldname);
+}
+
+
+/* minetest specific types */
+MapNode readnode(lua_State *L, int index, INodeDefManager *ndef)
+{
+	lua_getfield(L, index, "name");
+	const char *name = luaL_checkstring(L, -1);
+	lua_pop(L, 1);
+	u8 param1;
+	lua_getfield(L, index, "param1");
+	if(lua_isnil(L, -1))
+		param1 = 0;
+	else
+		param1 = lua_tonumber(L, -1);
+	lua_pop(L, 1);
+	u8 param2;
+	lua_getfield(L, index, "param2");
+	if(lua_isnil(L, -1))
+		param2 = 0;
+	else
+		param2 = lua_tonumber(L, -1);
+	lua_pop(L, 1);
+	return MapNode(ndef, name, param1, param2);
+}
+
+void pushnode(lua_State *L, const MapNode &n, INodeDefManager *ndef)
+{
+	lua_newtable(L);
+	lua_pushstring(L, ndef->get(n).name.c_str());
+	lua_setfield(L, -2, "name");
+	lua_pushnumber(L, n.getParam1());
+	lua_setfield(L, -2, "param1");
+	lua_pushnumber(L, n.getParam2());
+	lua_setfield(L, -2, "param2");
+}
diff --git a/src/scriptapi_types.h b/src/scriptapi_types.h
new file mode 100644
index 0000000000000000000000000000000000000000..e3a611a9d8d4cd0277fe6f97b3a8568e49cbd0d6
--- /dev/null
+++ b/src/scriptapi_types.h
@@ -0,0 +1,87 @@
+/*
+Minetest-c55
+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 LUA_TYPES_H_
+#define LUA_TYPES_H_
+
+#include <iostream>
+#include <map>
+#include <vector>
+
+#include "irrlichttypes_bloated.h"
+#include "porting.h"
+#include "map.h"
+
+extern "C" {
+#include <lua.h>
+}
+
+std::string        getstringfield_default        (lua_State *L, int table,
+                             const char *fieldname, const std::string &default_);
+bool               getboolfield_default(lua_State *L, int table,
+                             const char *fieldname, bool default_);
+float              getfloatfield_default(lua_State *L, int table,
+                             const char *fieldname, float default_);
+int                getintfield_default           (lua_State *L, int table,
+                             const char *fieldname, int default_);
+
+bool               getstringfield(lua_State *L, int table,
+                             const char *fieldname, std::string &result);
+bool               getintfield(lua_State *L, int table,
+                             const char *fieldname, int &result);
+void               read_groups                   (lua_State *L, int index,
+                             std::map<std::string, int> &result);
+bool               getboolfield(lua_State *L, int table,
+                             const char *fieldname, bool &result);
+bool               getfloatfield(lua_State *L, int table,
+                             const char *fieldname, float &result);
+
+std::string        checkstringfield(lua_State *L, int table,
+                             const char *fieldname);
+
+void               setintfield(lua_State *L, int table,
+                             const char *fieldname, int value);
+void               setfloatfield(lua_State *L, int table,
+                             const char *fieldname, float value);
+void               setboolfield(lua_State *L, int table,
+                             const char *fieldname, bool value);
+
+
+v3f           checkFloatPos                 (lua_State *L, int index);
+v3f           check_v3f                     (lua_State *L, int index);
+v3s16         check_v3s16                   (lua_State *L, int index);
+
+v3f           read_v3f                      (lua_State *L, int index);
+v2f           read_v2f                      (lua_State *L, int index);
+v2s16         read_v2s16                    (lua_State *L, int index);
+video::SColor readARGB8                     (lua_State *L, int index);
+aabb3f        read_aabb3f                   (lua_State *L, int index, f32 scale);
+v3s16         read_v3s16                    (lua_State *L, int index);
+std::vector<aabb3f>
+              read_aabb3f_vector            (lua_State *L, int index, f32 scale);
+
+void          push_v3s16                    (lua_State *L, v3s16 p);
+void          pushFloatPos                  (lua_State *L, v3f p);
+void          push_v3f                      (lua_State *L, v3f p);
+
+
+MapNode readnode(lua_State *L, int index, INodeDefManager *ndef);
+void pushnode(lua_State *L, const MapNode &n, INodeDefManager *ndef);
+
+#endif /* LUA_TYPES_H_ */
diff --git a/src/server.cpp b/src/server.cpp
index 41a7a4289095abdce8fcfb2eec191900c6078b94..c4dd0ab0fabd1652ba6a23930305167d967f1eb4 100644
--- a/src/server.cpp
+++ b/src/server.cpp
@@ -20,6 +20,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include "server.h"
 #include <iostream>
 #include <queue>
+#include <algorithm>
 #include "clientserver.h"
 #include "map.h"
 #include "jmutexautolock.h"
@@ -58,6 +59,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include "util/mathconstants.h"
 #include "rollback.h"
 #include "util/serialize.h"
+#include "defaultsettings.h"
 
 void * ServerThread::Thread()
 {
@@ -126,7 +128,7 @@ v3f ServerSoundParams::getPos(ServerEnvironment *env, bool *pos_exists) const
 }
 
 void RemoteClient::GetNextBlocks(Server *server, float dtime,
-		core::array<PrioritySortedBlockTransfer> &dest)
+		std::vector<PrioritySortedBlockTransfer> &dest)
 {
 	DSTACK(__FUNCTION_NAME);
 
@@ -274,11 +276,11 @@ void RemoteClient::GetNextBlocks(Server *server, float dtime,
 			Get the border/face dot coordinates of a "d-radiused"
 			box
 		*/
-		core::list<v3s16> list;
+		std::list<v3s16> list;
 		getFacePositions(list, d);
 
-		core::list<v3s16>::Iterator li;
-		for(li=list.begin(); li!=list.end(); li++)
+		std::list<v3s16>::iterator li;
+		for(li=list.begin(); li!=list.end(); ++li)
 		{
 			v3s16 p = *li + center;
 
@@ -305,7 +307,7 @@ void RemoteClient::GetNextBlocks(Server *server, float dtime,
 			}
 
 			// Don't send blocks that are currently being transferred
-			if(m_blocks_sending.find(p) != NULL)
+			if(m_blocks_sending.find(p) != m_blocks_sending.end())
 				continue;
 
 			/*
@@ -382,7 +384,7 @@ void RemoteClient::GetNextBlocks(Server *server, float dtime,
 				Don't send already sent blocks
 			*/
 			{
-				if(m_blocks_sent.find(p) != NULL)
+				if(m_blocks_sent.find(p) != m_blocks_sent.end())
 				{
 					continue;
 				}
@@ -554,21 +556,21 @@ void RemoteClient::GetNextBlocks(Server *server, float dtime,
 
 void RemoteClient::GotBlock(v3s16 p)
 {
-	if(m_blocks_sending.find(p) != NULL)
-		m_blocks_sending.remove(p);
+	if(m_blocks_sending.find(p) != m_blocks_sending.end())
+		m_blocks_sending.erase(p);
 	else
 	{
 		/*infostream<<"RemoteClient::GotBlock(): Didn't find in"
 				" m_blocks_sending"<<std::endl;*/
 		m_excess_gotblocks++;
 	}
-	m_blocks_sent.insert(p, true);
+	m_blocks_sent.insert(p);
 }
 
 void RemoteClient::SentBlock(v3s16 p)
 {
-	if(m_blocks_sending.find(p) == NULL)
-		m_blocks_sending.insert(p, 0.0);
+	if(m_blocks_sending.find(p) == m_blocks_sending.end())
+		m_blocks_sending[p] = 0.0;
 	else
 		infostream<<"RemoteClient::SentBlock(): Sent block"
 				" already in m_blocks_sending"<<std::endl;
@@ -578,26 +580,26 @@ void RemoteClient::SetBlockNotSent(v3s16 p)
 {
 	m_nearest_unsent_d = 0;
 
-	if(m_blocks_sending.find(p) != NULL)
-		m_blocks_sending.remove(p);
-	if(m_blocks_sent.find(p) != NULL)
-		m_blocks_sent.remove(p);
+	if(m_blocks_sending.find(p) != m_blocks_sending.end())
+		m_blocks_sending.erase(p);
+	if(m_blocks_sent.find(p) != m_blocks_sent.end())
+		m_blocks_sent.erase(p);
 }
 
-void RemoteClient::SetBlocksNotSent(core::map<v3s16, MapBlock*> &blocks)
+void RemoteClient::SetBlocksNotSent(std::map<v3s16, MapBlock*> &blocks)
 {
 	m_nearest_unsent_d = 0;
 
-	for(core::map<v3s16, MapBlock*>::Iterator
-			i = blocks.getIterator();
-			i.atEnd()==false; i++)
+	for(std::map<v3s16, MapBlock*>::iterator
+			i = blocks.begin();
+			i != blocks.end(); ++i)
 	{
-		v3s16 p = i.getNode()->getKey();
+		v3s16 p = i->first;
 
-		if(m_blocks_sending.find(p) != NULL)
-			m_blocks_sending.remove(p);
-		if(m_blocks_sent.find(p) != NULL)
-			m_blocks_sent.remove(p);
+		if(m_blocks_sending.find(p) != m_blocks_sending.end())
+			m_blocks_sending.erase(p);
+		if(m_blocks_sent.find(p) != m_blocks_sent.end())
+			m_blocks_sent.erase(p);
 	}
 }
 
@@ -651,7 +653,6 @@ Server::Server(
 	m_craftdef(createCraftDefManager()),
 	m_event(new EventManager()),
 	m_thread(this),
-	//m_emergethread(this),
 	m_time_of_day_send_timer(0),
 	m_uptime(0),
 	m_shutdown_requested(false),
@@ -687,9 +688,19 @@ Server::Server(
 	infostream<<"- config: "<<m_path_config<<std::endl;
 	infostream<<"- game:   "<<m_gamespec.path<<std::endl;
 
+	// Initialize default settings and override defaults with those provided
+	// by the game
+	set_default_settings(g_settings);
+	Settings gamedefaults;
+	getGameMinetestConfig(gamespec.path, gamedefaults);
+	override_default_settings(g_settings, &gamedefaults);
+
 	// Create biome definition manager
 	m_biomedef = new BiomeDefManager(this);
-
+	
+	// Create emerge manager
+	m_emerge = new EmergeManager(this, m_biomedef);
+	
 	// Create rollback manager
 	std::string rollback_path = m_path_world+DIR_DELIM+"rollback.txt";
 	m_rollback = createRollbackManager(rollback_path, this);
@@ -736,10 +747,10 @@ Server::Server(
 	}
 	// complain about mods declared to be loaded, but not found
 	for(std::vector<ModSpec>::iterator it = m_mods.begin();
-		it != m_mods.end(); ++it)
+			it != m_mods.end(); ++it)
 		load_mod_names.erase((*it).name);
 	for(std::list<ModSpec>::iterator it = unsatisfied_mods.begin();
-		it != unsatisfied_mods.end(); ++it)
+			it != unsatisfied_mods.end(); ++it)
 		load_mod_names.erase((*it).name);
 	if(!load_mod_names.empty())
 	{		
@@ -805,9 +816,6 @@ Server::Server(
 	// Add default biomes after nodedef had its aliases added
 	m_biomedef->addDefaultBiomes();
 
-	// Create emerge manager
-	m_emerge = new EmergeManager(this, m_biomedef);
-
 	// Initialize Environment
 	ServerMap *servermap = new ServerMap(path_world, this, m_emerge);
 	m_env = new ServerEnvironment(servermap, m_lua, this, this);
@@ -854,13 +862,13 @@ Server::~Server()
 		/*
 			Send the message to clients
 		*/
-		for(core::map<u16, RemoteClient*>::Iterator
-			i = m_clients.getIterator();
-			i.atEnd() == false; i++)
+		for(std::map<u16, RemoteClient*>::iterator
+			i = m_clients.begin();
+			i != m_clients.end(); ++i)
 		{
 			// Get client and check that it is valid
-			RemoteClient *client = i.getNode()->getValue();
-			assert(client->peer_id == i.getNode()->getKey());
+			RemoteClient *client = i->second;
+			assert(client->peer_id == i->first);
 			if(client->serialization_version == SER_FMT_VER_INVALID)
 				continue;
 
@@ -909,13 +917,13 @@ Server::~Server()
 	{
 		JMutexAutoLock clientslock(m_con_mutex);
 
-		for(core::map<u16, RemoteClient*>::Iterator
-			i = m_clients.getIterator();
-			i.atEnd() == false; i++)
+		for(std::map<u16, RemoteClient*>::iterator
+			i = m_clients.begin();
+			i != m_clients.end(); ++i)
 		{
 
 			// Delete client
-			delete i.getNode()->getValue();
+			delete i->second;
 		}
 	}
 
@@ -1073,11 +1081,11 @@ void Server::AsyncRunStep()
 			//JMutexAutoLock envlock(m_env_mutex);
 			JMutexAutoLock conlock(m_con_mutex);
 
-			for(core::map<u16, RemoteClient*>::Iterator
-				i = m_clients.getIterator();
-				i.atEnd() == false; i++)
+			for(std::map<u16, RemoteClient*>::iterator
+				i = m_clients.begin();
+				i != m_clients.end(); ++i)
 			{
-				RemoteClient *client = i.getNode()->getValue();
+				RemoteClient *client = i->second;
 				SharedBuffer<u8> data = makePacket_TOCLIENT_TIME_OF_DAY(
 						m_env->getTimeOfDay(), g_settings->getFloat("time_speed"));
 				// Send as reliable
@@ -1117,11 +1125,11 @@ void Server::AsyncRunStep()
 
 		ScopeProfiler sp(g_profiler, "Server: handle players");
 
-		for(core::map<u16, RemoteClient*>::Iterator
-			i = m_clients.getIterator();
-			i.atEnd() == false; i++)
+		for(std::map<u16, RemoteClient*>::iterator
+			i = m_clients.begin();
+			i != m_clients.end(); ++i)
 		{
-			RemoteClient *client = i.getNode()->getValue();
+			RemoteClient *client = i->second;
 			PlayerSAO *playersao = getPlayerSAO(client->peer_id);
 			if(playersao == NULL)
 				continue;
@@ -1161,7 +1169,7 @@ void Server::AsyncRunStep()
 
 		ScopeProfiler sp(g_profiler, "Server: liquid transform");
 
-		core::map<v3s16, MapBlock*> modified_blocks;
+		std::map<v3s16, MapBlock*> modified_blocks;
 		m_env->getMap().transformLiquids(modified_blocks);
 #if 0
 		/*
@@ -1186,11 +1194,11 @@ void Server::AsyncRunStep()
 
 		JMutexAutoLock lock2(m_con_mutex);
 
-		for(core::map<u16, RemoteClient*>::Iterator
-				i = m_clients.getIterator();
-				i.atEnd() == false; i++)
+		for(std::map<u16, RemoteClient*>::iterator
+				i = m_clients.begin();
+				i != m_clients.end(); ++i)
 		{
-			RemoteClient *client = i.getNode()->getValue();
+			RemoteClient *client = i->second;
 
 			if(modified_blocks.size() > 0)
 			{
@@ -1212,12 +1220,12 @@ void Server::AsyncRunStep()
 			m_clients_number = 0;
 			if(m_clients.size() != 0)
 				infostream<<"Players:"<<std::endl;
-			for(core::map<u16, RemoteClient*>::Iterator
-				i = m_clients.getIterator();
-				i.atEnd() == false; i++)
+			for(std::map<u16, RemoteClient*>::iterator
+				i = m_clients.begin();
+				i != m_clients.end(); ++i)
 			{
 				//u16 peer_id = i.getNode()->getKey();
-				RemoteClient *client = i.getNode()->getValue();
+				RemoteClient *client = i->second;
 				Player *player = m_env->getPlayer(client->peer_id);
 				if(player==NULL)
 					continue;
@@ -1235,7 +1243,7 @@ void Server::AsyncRunStep()
 		float &counter = m_masterserver_timer;
 		if((!counter || counter >= 300.0) && g_settings->getBool("server_announce") == true)
 		{
-			ServerList::sendAnnounce(!counter ? "start" : "update", m_clients_number);
+			ServerList::sendAnnounce(!counter ? "start" : "update", m_clients_number, m_uptime.get(), m_gamespec.id);
 			counter = 0.01;
 		}
 		counter += dtime;
@@ -1259,11 +1267,11 @@ void Server::AsyncRunStep()
 		s16 radius = g_settings->getS16("active_object_send_range_blocks");
 		radius *= MAP_BLOCKSIZE;
 
-		for(core::map<u16, RemoteClient*>::Iterator
-			i = m_clients.getIterator();
-			i.atEnd() == false; i++)
+		for(std::map<u16, RemoteClient*>::iterator
+			i = m_clients.begin();
+			i != m_clients.end(); ++i)
 		{
-			RemoteClient *client = i.getNode()->getValue();
+			RemoteClient *client = i->second;
 
 			// If definitions and textures have not been sent, don't
 			// send objects either
@@ -1281,8 +1289,8 @@ void Server::AsyncRunStep()
 			}
 			v3s16 pos = floatToInt(player->getPosition(), BS);
 
-			core::map<u16, bool> removed_objects;
-			core::map<u16, bool> added_objects;
+			std::set<u16> removed_objects;
+			std::set<u16> added_objects;
 			m_env->getRemovedActiveObjects(pos, radius,
 					client->m_known_objects, removed_objects);
 			m_env->getAddedActiveObjects(pos, radius,
@@ -1302,20 +1310,20 @@ void Server::AsyncRunStep()
 			// Handle removed objects
 			writeU16((u8*)buf, removed_objects.size());
 			data_buffer.append(buf, 2);
-			for(core::map<u16, bool>::Iterator
-					i = removed_objects.getIterator();
-					i.atEnd()==false; i++)
+			for(std::set<u16>::iterator
+					i = removed_objects.begin();
+					i != removed_objects.end(); ++i)
 			{
 				// Get object
-				u16 id = i.getNode()->getKey();
+				u16 id = *i;
 				ServerActiveObject* obj = m_env->getActiveObject(id);
 
 				// Add to data buffer for sending
-				writeU16((u8*)buf, i.getNode()->getKey());
+				writeU16((u8*)buf, id);
 				data_buffer.append(buf, 2);
 
 				// Remove from known objects
-				client->m_known_objects.remove(i.getNode()->getKey());
+				client->m_known_objects.erase(id);
 
 				if(obj && obj->m_known_by_count > 0)
 					obj->m_known_by_count--;
@@ -1324,12 +1332,12 @@ void Server::AsyncRunStep()
 			// Handle added objects
 			writeU16((u8*)buf, added_objects.size());
 			data_buffer.append(buf, 2);
-			for(core::map<u16, bool>::Iterator
-					i = added_objects.getIterator();
-					i.atEnd()==false; i++)
+			for(std::set<u16>::iterator
+					i = added_objects.begin();
+					i != added_objects.end(); ++i)
 			{
 				// Get object
-				u16 id = i.getNode()->getKey();
+				u16 id = *i;
 				ServerActiveObject* obj = m_env->getActiveObject(id);
 
 				// Get object type
@@ -1353,7 +1361,7 @@ void Server::AsyncRunStep()
 					data_buffer.append(serializeLongString(""));
 
 				// Add to known objects
-				client->m_known_objects.insert(i.getNode()->getKey(), false);
+				client->m_known_objects.insert(id);
 
 				if(obj)
 					obj->m_known_by_count++;
@@ -1412,7 +1420,7 @@ void Server::AsyncRunStep()
 
 		// Key = object id
 		// Value = data sent by object
-		core::map<u16, core::list<ActiveObjectMessage>* > buffered_messages;
+		std::map<u16, std::list<ActiveObjectMessage>* > buffered_messages;
 
 		// Get active object messages from environment
 		for(;;)
@@ -1421,43 +1429,43 @@ void Server::AsyncRunStep()
 			if(aom.id == 0)
 				break;
 
-			core::list<ActiveObjectMessage>* message_list = NULL;
-			core::map<u16, core::list<ActiveObjectMessage>* >::Node *n;
+			std::list<ActiveObjectMessage>* message_list = NULL;
+			std::map<u16, std::list<ActiveObjectMessage>* >::iterator n;
 			n = buffered_messages.find(aom.id);
-			if(n == NULL)
+			if(n == buffered_messages.end())
 			{
-				message_list = new core::list<ActiveObjectMessage>;
-				buffered_messages.insert(aom.id, message_list);
+				message_list = new std::list<ActiveObjectMessage>;
+				buffered_messages[aom.id] = message_list;
 			}
 			else
 			{
-				message_list = n->getValue();
+				message_list = n->second;
 			}
 			message_list->push_back(aom);
 		}
 
 		// Route data to every client
-		for(core::map<u16, RemoteClient*>::Iterator
-			i = m_clients.getIterator();
-			i.atEnd()==false; i++)
+		for(std::map<u16, RemoteClient*>::iterator
+			i = m_clients.begin();
+			i != m_clients.end(); ++i)
 		{
-			RemoteClient *client = i.getNode()->getValue();
+			RemoteClient *client = i->second;
 			std::string reliable_data;
 			std::string unreliable_data;
 			// Go through all objects in message buffer
-			for(core::map<u16, core::list<ActiveObjectMessage>* >::Iterator
-					j = buffered_messages.getIterator();
-					j.atEnd()==false; j++)
+			for(std::map<u16, std::list<ActiveObjectMessage>* >::iterator
+					j = buffered_messages.begin();
+					j != buffered_messages.end(); ++j)
 			{
 				// If object is not known by client, skip it
-				u16 id = j.getNode()->getKey();
-				if(client->m_known_objects.find(id) == NULL)
+				u16 id = j->first;
+				if(client->m_known_objects.find(id) == client->m_known_objects.end())
 					continue;
 				// Get message list of object
-				core::list<ActiveObjectMessage>* list = j.getNode()->getValue();
+				std::list<ActiveObjectMessage>* list = j->second;
 				// Go through every message
-				for(core::list<ActiveObjectMessage>::Iterator
-						k = list->begin(); k != list->end(); k++)
+				for(std::list<ActiveObjectMessage>::iterator
+						k = list->begin(); k != list->end(); ++k)
 				{
 					// Compose the full new data with header
 					ActiveObjectMessage aom = *k;
@@ -1508,11 +1516,11 @@ void Server::AsyncRunStep()
 		}
 
 		// Clear buffered_messages
-		for(core::map<u16, core::list<ActiveObjectMessage>* >::Iterator
-				i = buffered_messages.getIterator();
-				i.atEnd()==false; i++)
+		for(std::map<u16, std::list<ActiveObjectMessage>* >::iterator
+				i = buffered_messages.begin();
+				i != buffered_messages.end(); ++i)
 		{
-			delete i.getNode()->getValue();
+			delete i->second;
 		}
 	}
 
@@ -1546,7 +1554,7 @@ void Server::AsyncRunStep()
 			// Players far away from the change are stored here.
 			// Instead of sending the changes, MapBlocks are set not sent
 			// for them.
-			core::list<u16> far_players;
+			std::list<u16> far_players;
 
 			if(event->type == MEET_ADDNODE)
 			{
@@ -1580,12 +1588,11 @@ void Server::AsyncRunStep()
 			{
 				infostream<<"Server: MEET_OTHER"<<std::endl;
 				prof.add("MEET_OTHER", 1);
-				for(core::map<v3s16, bool>::Iterator
-						i = event->modified_blocks.getIterator();
-						i.atEnd()==false; i++)
+				for(std::set<v3s16>::iterator
+						i = event->modified_blocks.begin();
+						i != event->modified_blocks.end(); ++i)
 				{
-					v3s16 p = i.getNode()->getKey();
-					setBlockNotSent(p);
+					setBlockNotSent(*i);
 				}
 			}
 			else
@@ -1601,19 +1608,18 @@ void Server::AsyncRunStep()
 			if(far_players.size() > 0)
 			{
 				// Convert list format to that wanted by SetBlocksNotSent
-				core::map<v3s16, MapBlock*> modified_blocks2;
-				for(core::map<v3s16, bool>::Iterator
-						i = event->modified_blocks.getIterator();
-						i.atEnd()==false; i++)
+				std::map<v3s16, MapBlock*> modified_blocks2;
+				for(std::set<v3s16>::iterator
+						i = event->modified_blocks.begin();
+						i != event->modified_blocks.end(); ++i)
 				{
-					v3s16 p = i.getNode()->getKey();
-					modified_blocks2.insert(p,
-							m_env->getMap().getBlockNoCreateNoEx(p));
+					modified_blocks2[*i] =
+							m_env->getMap().getBlockNoCreateNoEx(*i);
 				}
 				// Set blocks not sent
-				for(core::list<u16>::Iterator
+				for(std::list<u16>::iterator
 						i = far_players.begin();
-						i != far_players.end(); i++)
+						i != far_players.end(); ++i)
 				{
 					u16 peer_id = *i;
 					RemoteClient *client = getClient(peer_id);
@@ -1792,7 +1798,7 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
 		u8 client_max = data[2];
 		u8 our_max = SER_FMT_VER_HIGHEST;
 		// Use the highest version supported by both
-		u8 deployed = core::min_(client_max, our_max);
+		u8 deployed = std::min(client_max, our_max);
 		// If it's lower than the lowest supported, give up.
 		if(deployed < SER_FMT_VER_LOWEST)
 			deployed = SER_FMT_VER_INVALID;
@@ -2074,7 +2080,7 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
 		SendMovement(m_con, peer_id);
 
 		// Send item definitions
-		SendItemDef(m_con, peer_id, m_itemdef);
+		SendItemDef(m_con, peer_id, m_itemdef, client->net_proto_version);
 
 		// Send node definitions
 		SendNodeDef(m_con, peer_id, m_nodedef, client->net_proto_version);
@@ -2143,12 +2149,12 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
 		*/
 		{
 			std::ostringstream os(std::ios_base::binary);
-			for(core::map<u16, RemoteClient*>::Iterator
-				i = m_clients.getIterator();
-				i.atEnd() == false; i++)
+			for(std::map<u16, RemoteClient*>::iterator
+				i = m_clients.begin();
+				i != m_clients.end(); ++i)
 			{
-				RemoteClient *client = i.getNode()->getValue();
-				assert(client->peer_id == i.getNode()->getKey());
+				RemoteClient *client = i->second;
+				assert(client->peer_id == i->first);
 				if(client->serialization_version == SER_FMT_VER_INVALID)
 					continue;
 				// Get player
@@ -2520,13 +2526,13 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
 			/*
 				Send the message to clients
 			*/
-			for(core::map<u16, RemoteClient*>::Iterator
-				i = m_clients.getIterator();
-				i.atEnd() == false; i++)
+			for(std::map<u16, RemoteClient*>::iterator
+				i = m_clients.begin();
+				i != m_clients.end(); ++i)
 			{
 				// Get client and check that it is valid
-				RemoteClient *client = i.getNode()->getValue();
-				assert(client->peer_id == i.getNode()->getKey());
+				RemoteClient *client = i->second;
+				assert(client->peer_id == i->first);
 				if(client->serialization_version == SER_FMT_VER_INVALID)
 					continue;
 
@@ -2651,7 +2657,7 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
 		std::string datastring((char*)&data[2], datasize-2);
 		std::istringstream is(datastring, std::ios_base::binary);
 
-		core::list<MediaRequest> tosend;
+		std::list<MediaRequest> tosend;
 		u16 numfiles = readU16(is);
 
 		infostream<<"Sending "<<numfiles<<" files to "
@@ -3189,19 +3195,19 @@ void Server::setInventoryModified(const InventoryLocation &loc)
 	}
 }
 
-core::list<PlayerInfo> Server::getPlayerInfo()
+std::list<PlayerInfo> Server::getPlayerInfo()
 {
 	DSTACK(__FUNCTION_NAME);
 	JMutexAutoLock envlock(m_env_mutex);
 	JMutexAutoLock conlock(m_con_mutex);
 
-	core::list<PlayerInfo> list;
+	std::list<PlayerInfo> list;
 
-	core::list<Player*> players = m_env->getPlayers();
+	std::list<Player*> players = m_env->getPlayers();
 
-	core::list<Player*>::Iterator i;
+	std::list<Player*>::iterator i;
 	for(i = players.begin();
-			i != players.end(); i++)
+			i != players.end(); ++i)
 	{
 		PlayerInfo info;
 
@@ -3336,7 +3342,7 @@ void Server::SendDeathscreen(con::Connection &con, u16 peer_id,
 }
 
 void Server::SendItemDef(con::Connection &con, u16 peer_id,
-		IItemDefManager *itemdef)
+		IItemDefManager *itemdef, u16 protocol_version)
 {
 	DSTACK(__FUNCTION_NAME);
 	std::ostringstream os(std::ios_base::binary);
@@ -3348,7 +3354,7 @@ void Server::SendItemDef(con::Connection &con, u16 peer_id,
 	*/
 	writeU16(os, TOCLIENT_ITEMDEF);
 	std::ostringstream tmp_os(std::ios::binary);
-	itemdef->serialize(tmp_os);
+	itemdef->serialize(tmp_os, protocol_version);
 	std::ostringstream tmp_os2(std::ios::binary);
 	compressZlib(tmp_os.str(), tmp_os2);
 	os<<serializeLongString(tmp_os2.str());
@@ -3468,15 +3474,141 @@ void Server::SendShowFormspecMessage(u16 peer_id, const std::string formspec, co
 	m_con.Send(peer_id, 0, data, true);
 }
 
+// Spawns a particle on peer with peer_id
+void Server::SendSpawnParticle(u16 peer_id, v3f pos, v3f velocity, v3f acceleration, float expirationtime, float size, bool collisiondetection, std::string texture)
+{
+	DSTACK(__FUNCTION_NAME);
+
+	std::ostringstream os(std::ios_base::binary);
+	writeU16(os, TOCLIENT_SPAWN_PARTICLE);
+	writeV3F1000(os, pos);
+	writeV3F1000(os, velocity);
+	writeV3F1000(os, acceleration);
+	writeF1000(os, expirationtime);
+	writeF1000(os, size);
+	writeU8(os,  collisiondetection);
+	os<<serializeLongString(texture);
+
+	// Make data buffer
+	std::string s = os.str();
+	SharedBuffer<u8> data((u8*)s.c_str(), s.size());
+	// Send as reliable
+	m_con.Send(peer_id, 0, data, true);
+}
+
+// Spawns a particle on all peers
+void Server::SendSpawnParticleAll(v3f pos, v3f velocity, v3f acceleration, float expirationtime, float size, bool collisiondetection, std::string texture)
+{
+	for(std::map<u16, RemoteClient*>::iterator
+		i = m_clients.begin();
+		i != m_clients.end(); i++)
+	{
+		// Get client and check that it is valid
+		RemoteClient *client = i->second;
+		assert(client->peer_id == i->first);
+		if(client->serialization_version == SER_FMT_VER_INVALID)
+			continue;
+
+		SendSpawnParticle(client->peer_id, pos, velocity, acceleration,
+			expirationtime, size, collisiondetection, texture);
+	}
+}
+
+// Adds a ParticleSpawner on peer with peer_id
+void Server::SendAddParticleSpawner(u16 peer_id, u16 amount, float spawntime, v3f minpos, v3f maxpos,
+	v3f minvel, v3f maxvel, v3f minacc, v3f maxacc, float minexptime, float maxexptime,
+	float minsize, float maxsize, bool collisiondetection, std::string texture, u32 id)
+{
+	DSTACK(__FUNCTION_NAME);
+
+	std::ostringstream os(std::ios_base::binary);
+	writeU16(os, TOCLIENT_ADD_PARTICLESPAWNER);
+
+	writeU16(os, amount);
+	writeF1000(os, spawntime);
+	writeV3F1000(os, minpos);
+	writeV3F1000(os, maxpos);
+	writeV3F1000(os, minvel);
+	writeV3F1000(os, maxvel);
+	writeV3F1000(os, minacc);
+	writeV3F1000(os, maxacc);
+	writeF1000(os, minexptime);
+	writeF1000(os, maxexptime);
+	writeF1000(os, minsize);
+	writeF1000(os, maxsize);
+	writeU8(os,  collisiondetection);
+	os<<serializeLongString(texture);
+	writeU32(os, id);
+
+	// Make data buffer
+	std::string s = os.str();
+	SharedBuffer<u8> data((u8*)s.c_str(), s.size());
+	// Send as reliable
+	m_con.Send(peer_id, 0, data, true);
+}
+
+// Adds a ParticleSpawner on all peers
+void Server::SendAddParticleSpawnerAll(u16 amount, float spawntime, v3f minpos, v3f maxpos,
+	v3f minvel, v3f maxvel, v3f minacc, v3f maxacc, float minexptime, float maxexptime,
+	float minsize, float maxsize, bool collisiondetection, std::string texture, u32 id)
+{
+	for(std::map<u16, RemoteClient*>::iterator
+		i = m_clients.begin();
+		i != m_clients.end(); i++)
+	{
+		// Get client and check that it is valid
+		RemoteClient *client = i->second;
+		assert(client->peer_id == i->first);
+		if(client->serialization_version == SER_FMT_VER_INVALID)
+			continue;
+
+		SendAddParticleSpawner(client->peer_id, amount, spawntime,
+			minpos, maxpos, minvel, maxvel, minacc, maxacc,
+			minexptime, maxexptime, minsize, maxsize, collisiondetection, texture, id);
+	}
+}
+
+void Server::SendDeleteParticleSpawner(u16 peer_id, u32 id)
+{
+	DSTACK(__FUNCTION_NAME);
+
+	std::ostringstream os(std::ios_base::binary);
+	writeU16(os, TOCLIENT_DELETE_PARTICLESPAWNER);
+
+	writeU16(os, id);
+
+	// Make data buffer
+	std::string s = os.str();
+	SharedBuffer<u8> data((u8*)s.c_str(), s.size());
+	// Send as reliable
+	m_con.Send(peer_id, 0, data, true);
+}
+
+void Server::SendDeleteParticleSpawnerAll(u32 id)
+{
+	for(std::map<u16, RemoteClient*>::iterator
+		i = m_clients.begin();
+		i != m_clients.end(); i++)
+	{
+		// Get client and check that it is valid
+		RemoteClient *client = i->second;
+		assert(client->peer_id == i->first);
+		if(client->serialization_version == SER_FMT_VER_INVALID)
+			continue;
+
+		SendDeleteParticleSpawner(client->peer_id, id);
+	}
+}
+
 void Server::BroadcastChatMessage(const std::wstring &message)
 {
-	for(core::map<u16, RemoteClient*>::Iterator
-		i = m_clients.getIterator();
-		i.atEnd() == false; i++)
+	for(std::map<u16, RemoteClient*>::iterator
+		i = m_clients.begin();
+		i != m_clients.end(); ++i)
 	{
 		// Get client and check that it is valid
-		RemoteClient *client = i.getNode()->getValue();
-		assert(client->peer_id == i.getNode()->getKey());
+		RemoteClient *client = i->second;
+		assert(client->peer_id == i->first);
 		if(client->serialization_version == SER_FMT_VER_INVALID)
 			continue;
 
@@ -3595,10 +3727,10 @@ s32 Server::playSound(const SimpleSoundSpec &spec,
 	}
 	else
 	{
-		for(core::map<u16, RemoteClient*>::Iterator
-				i = m_clients.getIterator(); i.atEnd() == false; i++)
+		for(std::map<u16, RemoteClient*>::iterator
+				i = m_clients.begin(); i != m_clients.end(); ++i)
 		{
-			RemoteClient *client = i.getNode()->getValue();
+			RemoteClient *client = i->second;
 			Player *player = m_env->getPlayer(client->peer_id);
 			if(!player)
 				continue;
@@ -3668,7 +3800,7 @@ void Server::stopSound(s32 handle)
 }
 
 void Server::sendRemoveNode(v3s16 p, u16 ignore_id,
-	core::list<u16> *far_players, float far_d_nodes)
+	std::list<u16> *far_players, float far_d_nodes)
 {
 	float maxd = far_d_nodes*BS;
 	v3f p_f = intToFloat(p, BS);
@@ -3681,13 +3813,13 @@ void Server::sendRemoveNode(v3s16 p, u16 ignore_id,
 	writeS16(&reply[4], p.Y);
 	writeS16(&reply[6], p.Z);
 
-	for(core::map<u16, RemoteClient*>::Iterator
-		i = m_clients.getIterator();
-		i.atEnd() == false; i++)
+	for(std::map<u16, RemoteClient*>::iterator
+		i = m_clients.begin();
+		i != m_clients.end(); ++i)
 	{
 		// Get client and check that it is valid
-		RemoteClient *client = i.getNode()->getValue();
-		assert(client->peer_id == i.getNode()->getKey());
+		RemoteClient *client = i->second;
+		assert(client->peer_id == i->first);
 		if(client->serialization_version == SER_FMT_VER_INVALID)
 			continue;
 
@@ -3717,18 +3849,18 @@ void Server::sendRemoveNode(v3s16 p, u16 ignore_id,
 }
 
 void Server::sendAddNode(v3s16 p, MapNode n, u16 ignore_id,
-		core::list<u16> *far_players, float far_d_nodes)
+		std::list<u16> *far_players, float far_d_nodes)
 {
 	float maxd = far_d_nodes*BS;
 	v3f p_f = intToFloat(p, BS);
 
-	for(core::map<u16, RemoteClient*>::Iterator
-		i = m_clients.getIterator();
-		i.atEnd() == false; i++)
+	for(std::map<u16, RemoteClient*>::iterator
+		i = m_clients.begin();
+		i != m_clients.end(); ++i)
 	{
 		// Get client and check that it is valid
-		RemoteClient *client = i.getNode()->getValue();
-		assert(client->peer_id == i.getNode()->getKey());
+		RemoteClient *client = i->second;
+		assert(client->peer_id == i->first);
 		if(client->serialization_version == SER_FMT_VER_INVALID)
 			continue;
 
@@ -3768,11 +3900,11 @@ void Server::sendAddNode(v3s16 p, MapNode n, u16 ignore_id,
 
 void Server::setBlockNotSent(v3s16 p)
 {
-	for(core::map<u16, RemoteClient*>::Iterator
-		i = m_clients.getIterator();
-		i.atEnd()==false; i++)
+	for(std::map<u16, RemoteClient*>::iterator
+		i = m_clients.begin();
+		i != m_clients.end(); ++i)
 	{
-		RemoteClient *client = i.getNode()->getValue();
+		RemoteClient *client = i->second;
 		client->SetBlockNotSent(p);
 	}
 }
@@ -3839,19 +3971,19 @@ void Server::SendBlocks(float dtime)
 
 	ScopeProfiler sp(g_profiler, "Server: sel and send blocks to clients");
 
-	core::array<PrioritySortedBlockTransfer> queue;
+	std::vector<PrioritySortedBlockTransfer> queue;
 
 	s32 total_sending = 0;
 
 	{
 		ScopeProfiler sp(g_profiler, "Server: selecting blocks for sending");
 
-		for(core::map<u16, RemoteClient*>::Iterator
-			i = m_clients.getIterator();
-			i.atEnd() == false; i++)
+		for(std::map<u16, RemoteClient*>::iterator
+			i = m_clients.begin();
+			i != m_clients.end(); ++i)
 		{
-			RemoteClient *client = i.getNode()->getValue();
-			assert(client->peer_id == i.getNode()->getKey());
+			RemoteClient *client = i->second;
+			assert(client->peer_id == i->first);
 
 			// If definitions and textures have not been sent, don't
 			// send MapBlocks either
@@ -3870,7 +4002,7 @@ void Server::SendBlocks(float dtime)
 	// Sort.
 	// Lowest priority number comes first.
 	// Lowest is most important.
-	queue.sort();
+	std::sort(queue.begin(), queue.end());
 
 	for(u32 i=0; i<queue.size(); i++)
 	{
@@ -4017,7 +4149,7 @@ void Server::sendMediaAnnouncement(u16 peer_id)
 	verbosestream<<"Server: Announcing files to id("<<peer_id<<")"
 			<<std::endl;
 
-	core::list<SendableMediaAnnouncement> file_announcements;
+	std::list<SendableMediaAnnouncement> file_announcements;
 
 	for(std::map<std::string, MediaInfo>::iterator i = m_media.begin();
 			i != m_media.end(); i++){
@@ -4043,9 +4175,9 @@ void Server::sendMediaAnnouncement(u16 peer_id)
 	writeU16(os, TOCLIENT_ANNOUNCE_MEDIA);
 	writeU16(os, file_announcements.size());
 
-	for(core::list<SendableMediaAnnouncement>::Iterator
+	for(std::list<SendableMediaAnnouncement>::iterator
 			j = file_announcements.begin();
-			j != file_announcements.end(); j++){
+			j != file_announcements.end(); ++j){
 		os<<serializeString(j->name);
 		os<<serializeString(j->sha1_digest);
 	}
@@ -4074,7 +4206,7 @@ struct SendableMedia
 };
 
 void Server::sendRequestedMedia(u16 peer_id,
-		const core::list<MediaRequest> &tosend)
+		const std::list<MediaRequest> &tosend)
 {
 	DSTACK(__FUNCTION_NAME);
 
@@ -4086,13 +4218,13 @@ void Server::sendRequestedMedia(u16 peer_id,
 	// Put 5kB in one bunch (this is not accurate)
 	u32 bytes_per_bunch = 5000;
 
-	core::array< core::list<SendableMedia> > file_bunches;
-	file_bunches.push_back(core::list<SendableMedia>());
+	std::vector< std::list<SendableMedia> > file_bunches;
+	file_bunches.push_back(std::list<SendableMedia>());
 
 	u32 file_size_bunch_total = 0;
 
-	for(core::list<MediaRequest>::ConstIterator i = tosend.begin();
-			i != tosend.end(); i++)
+	for(std::list<MediaRequest>::const_iterator i = tosend.begin();
+			i != tosend.end(); ++i)
 	{
 		if(m_media.find(i->name) == m_media.end()){
 			errorstream<<"Server::sendRequestedMedia(): Client asked for "
@@ -4138,7 +4270,7 @@ void Server::sendRequestedMedia(u16 peer_id,
 
 		// Start next bunch if got enough data
 		if(file_size_bunch_total >= bytes_per_bunch){
-			file_bunches.push_back(core::list<SendableMedia>());
+			file_bunches.push_back(std::list<SendableMedia>());
 			file_size_bunch_total = 0;
 		}
 
@@ -4169,9 +4301,9 @@ void Server::sendRequestedMedia(u16 peer_id,
 		writeU16(os, i);
 		writeU32(os, file_bunches[i].size());
 
-		for(core::list<SendableMedia>::Iterator
+		for(std::list<SendableMedia>::iterator
 				j = file_bunches[i].begin();
-				j != file_bunches[i].end(); j++){
+				j != file_bunches[i].end(); ++j){
 			os<<serializeString(j->name);
 			os<<serializeLongString(j->data);
 		}
@@ -4212,10 +4344,10 @@ void Server::sendDetachedInventoryToAll(const std::string &name)
 {
 	DSTACK(__FUNCTION_NAME);
 
-	for(core::map<u16, RemoteClient*>::Iterator
-			i = m_clients.getIterator();
-			i.atEnd() == false; i++){
-		RemoteClient *client = i.getNode()->getValue();
+	for(std::map<u16, RemoteClient*>::iterator
+			i = m_clients.begin();
+			i != m_clients.end(); ++i){
+		RemoteClient *client = i->second;
 		sendDetachedInventory(name, client->peer_id);
 	}
 }
@@ -4299,11 +4431,11 @@ RemoteClient* Server::getClient(u16 peer_id)
 {
 	DSTACK(__FUNCTION_NAME);
 	//JMutexAutoLock lock(m_con_mutex);
-	core::map<u16, RemoteClient*>::Node *n;
+	std::map<u16, RemoteClient*>::iterator n;
 	n = m_clients.find(peer_id);
 	// A client should exist for all peers
-	assert(n != NULL);
-	return n->getValue();
+	assert(n != m_clients.end());
+	return n->second;
 }
 
 std::wstring Server::getStatusString()
@@ -4315,15 +4447,15 @@ std::wstring Server::getStatusString()
 	// Uptime
 	os<<L", uptime="<<m_uptime.get();
 	// Information about clients
-	core::map<u16, RemoteClient*>::Iterator i;
+	std::map<u16, RemoteClient*>::iterator i;
 	bool first;
 	os<<L", clients={";
-	for(i = m_clients.getIterator(), first = true;
-		i.atEnd() == false; i++)
+	for(i = m_clients.begin(), first = true;
+		i != m_clients.end(); ++i)
 	{
 		// Get client and check that it is valid
-		RemoteClient *client = i.getNode()->getValue();
-		assert(client->peer_id == i.getNode()->getKey());
+		RemoteClient *client = i->second;
+		assert(client->peer_id == i->first);
 		if(client->serialization_version == SER_FMT_VER_INVALID)
 			continue;
 		// Get player
@@ -4363,10 +4495,10 @@ bool Server::checkPriv(const std::string &name, const std::string &priv)
 void Server::reportPrivsModified(const std::string &name)
 {
 	if(name == ""){
-		for(core::map<u16, RemoteClient*>::Iterator
-				i = m_clients.getIterator();
-				i.atEnd() == false; i++){
-			RemoteClient *client = i.getNode()->getValue();
+		for(std::map<u16, RemoteClient*>::iterator
+				i = m_clients.begin();
+				i != m_clients.end(); ++i){
+			RemoteClient *client = i->second;
 			Player *player = m_env->getPlayer(client->peer_id);
 			reportPrivsModified(player->getName());
 		}
@@ -4426,6 +4558,111 @@ void Server::notifyPlayers(const std::wstring msg)
 	BroadcastChatMessage(msg);
 }
 
+void Server::spawnParticle(const char *playername, v3f pos,
+		v3f velocity, v3f acceleration,
+		float expirationtime, float size, bool
+		collisiondetection, std::string texture)
+{
+	Player *player = m_env->getPlayer(playername);
+	if(!player)
+		return;
+	SendSpawnParticle(player->peer_id, pos, velocity, acceleration,
+			expirationtime, size, collisiondetection, texture);
+}
+
+void Server::spawnParticleAll(v3f pos, v3f velocity, v3f acceleration,
+		float expirationtime, float size,
+		bool collisiondetection, std::string texture)
+{
+	SendSpawnParticleAll(pos, velocity, acceleration,
+			expirationtime, size, collisiondetection, texture);
+}
+
+u32 Server::addParticleSpawner(const char *playername,
+		u16 amount, float spawntime,
+		v3f minpos, v3f maxpos,
+		v3f minvel, v3f maxvel,
+		v3f minacc, v3f maxacc,
+		float minexptime, float maxexptime,
+		float minsize, float maxsize,
+		bool collisiondetection, std::string texture)
+{
+	Player *player = m_env->getPlayer(playername);
+	if(!player)
+		return -1;
+
+	u32 id = 0;
+	for(;;) // look for unused particlespawner id
+	{
+		id++;
+		if (std::find(m_particlespawner_ids.begin(),
+				m_particlespawner_ids.end(), id)
+				== m_particlespawner_ids.end())
+		{
+			m_particlespawner_ids.push_back(id);
+			break;
+		}
+	}
+
+	SendAddParticleSpawner(player->peer_id, amount, spawntime,
+		minpos, maxpos, minvel, maxvel, minacc, maxacc,
+		minexptime, maxexptime, minsize, maxsize,
+		collisiondetection, texture, id);
+
+	return id;
+}
+
+u32 Server::addParticleSpawnerAll(u16 amount, float spawntime,
+		v3f minpos, v3f maxpos,
+		v3f minvel, v3f maxvel,
+		v3f minacc, v3f maxacc,
+		float minexptime, float maxexptime,
+		float minsize, float maxsize,
+		bool collisiondetection, std::string texture)
+{
+	u32 id = 0;
+	for(;;) // look for unused particlespawner id
+	{
+		id++;
+		if (std::find(m_particlespawner_ids.begin(),
+				m_particlespawner_ids.end(), id)
+				== m_particlespawner_ids.end())
+		{
+			m_particlespawner_ids.push_back(id);
+			break;
+		}
+	}
+
+	SendAddParticleSpawnerAll(amount, spawntime,
+		minpos, maxpos, minvel, maxvel, minacc, maxacc,
+		minexptime, maxexptime, minsize, maxsize,
+		collisiondetection, texture, id);
+
+	return id;
+}
+
+void Server::deleteParticleSpawner(const char *playername, u32 id)
+{
+	Player *player = m_env->getPlayer(playername);
+	if(!player)
+		return;
+
+	m_particlespawner_ids.erase(
+			std::remove(m_particlespawner_ids.begin(),
+			m_particlespawner_ids.end(), id),
+			m_particlespawner_ids.end());
+	SendDeleteParticleSpawner(player->peer_id, id);
+}
+
+void Server::deleteParticleSpawnerAll(u32 id)
+{
+	m_particlespawner_ids.erase(
+			std::remove(m_particlespawner_ids.begin(),
+			m_particlespawner_ids.end(), id),
+			m_particlespawner_ids.end());
+	SendDeleteParticleSpawnerAll(id);
+}
+
 void Server::queueBlockEmerge(v3s16 blockpos, bool allow_generate)
 {
 	m_emerge->enqueueBlockEmerge(PEER_ID_INEXISTENT, blockpos, allow_generate);
@@ -4579,11 +4816,11 @@ const ModSpec* Server::getModSpec(const std::string &modname)
 	}
 	return NULL;
 }
-void Server::getModNames(core::list<std::string> &modlist)
+void Server::getModNames(std::list<std::string> &modlist)
 {
 	for(std::vector<ModSpec>::iterator i = m_mods.begin(); i != m_mods.end(); i++)
 	{
-		modlist.push_back((*i).name);
+		modlist.push_back(i->name);
 	}
 }
 std::string Server::getBuiltinLuaPath()
@@ -4733,15 +4970,15 @@ void Server::handlePeerChange(PeerChange &c)
 		*/
 
 		// Error check
-		core::map<u16, RemoteClient*>::Node *n;
+		std::map<u16, RemoteClient*>::iterator n;
 		n = m_clients.find(c.peer_id);
 		// The client shouldn't already exist
-		assert(n == NULL);
+		assert(n == m_clients.end());
 
 		// Create client
 		RemoteClient *client = new RemoteClient();
 		client->peer_id = c.peer_id;
-		m_clients.insert(client->peer_id, client);
+		m_clients[client->peer_id] = client;
 
 	} // PEER_ADDED
 	else if(c.type == PEER_REMOVED)
@@ -4751,22 +4988,22 @@ void Server::handlePeerChange(PeerChange &c)
 		*/
 
 		// Error check
-		core::map<u16, RemoteClient*>::Node *n;
+		std::map<u16, RemoteClient*>::iterator n;
 		n = m_clients.find(c.peer_id);
 		// The client should exist
-		assert(n != NULL);
+		assert(n != m_clients.end());
 
 		/*
 			Mark objects to be not known by the client
 		*/
-		RemoteClient *client = n->getValue();
+		RemoteClient *client = n->second;
 		// Handle objects
-		for(core::map<u16, bool>::Iterator
-				i = client->m_known_objects.getIterator();
-				i.atEnd()==false; i++)
+		for(std::set<u16>::iterator
+				i = client->m_known_objects.begin();
+				i != client->m_known_objects.end(); ++i)
 		{
 			// Get object
-			u16 id = i.getNode()->getKey();
+			u16 id = *i;
 			ServerActiveObject* obj = m_env->getActiveObject(id);
 
 			if(obj && obj->m_known_by_count > 0)
@@ -4824,12 +5061,12 @@ void Server::handlePeerChange(PeerChange &c)
 			if(player != NULL)
 			{
 				std::ostringstream os(std::ios_base::binary);
-				for(core::map<u16, RemoteClient*>::Iterator
-					i = m_clients.getIterator();
-					i.atEnd() == false; i++)
+				for(std::map<u16, RemoteClient*>::iterator
+					i = m_clients.begin();
+					i != m_clients.end(); ++i)
 				{
-					RemoteClient *client = i.getNode()->getValue();
-					assert(client->peer_id == i.getNode()->getKey());
+					RemoteClient *client = i->second;
+					assert(client->peer_id == i->first);
 					if(client->serialization_version == SER_FMT_VER_INVALID)
 						continue;
 					// Get player
@@ -4849,7 +5086,7 @@ void Server::handlePeerChange(PeerChange &c)
 
 		// Delete client
 		delete m_clients[c.peer_id];
-		m_clients.remove(c.peer_id);
+		m_clients.erase(c.peer_id);
 
 		// Send player info to all remaining clients
 		//SendPlayerInfos();
diff --git a/src/server.h b/src/server.h
index d7700791c99ca8e2ab32a98eb25e70e689b351ad..04e693fc884994fd1938a80080bfb5a4cabc3444 100644
--- a/src/server.h
+++ b/src/server.h
@@ -38,6 +38,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include "util/string.h"
 #include "rollback_interface.h" // Needed for rollbackRevertActions()
 #include <list> // Needed for rollbackRevertActions()
+#include <algorithm>
 
 #define PP(x) "("<<(x).X<<","<<(x).Y<<","<<(x).Z<<")"
 
@@ -166,7 +167,7 @@ struct PrioritySortedBlockTransfer
 		pos = a_pos;
 		peer_id = a_peer_id;
 	}
-	bool operator < (PrioritySortedBlockTransfer &other)
+	bool operator < (const PrioritySortedBlockTransfer &other) const
 	{
 		return priority < other.priority;
 	}
@@ -271,14 +272,14 @@ class RemoteClient
 		dtime is used for resetting send radius at slow interval
 	*/
 	void GetNextBlocks(Server *server, float dtime,
-			core::array<PrioritySortedBlockTransfer> &dest);
+			std::vector<PrioritySortedBlockTransfer> &dest);
 
 	void GotBlock(v3s16 p);
 
 	void SentBlock(v3s16 p);
 
 	void SetBlockNotSent(v3s16 p);
-	void SetBlocksNotSent(core::map<v3s16, MapBlock*> &blocks);
+	void SetBlocksNotSent(std::map<v3s16, MapBlock*> &blocks);
 
 	s32 SendingCount()
 	{
@@ -314,7 +315,7 @@ class RemoteClient
 		List of active objects that the client knows of.
 		Value is dummy.
 	*/
-	core::map<u16, bool> m_known_objects;
+	std::set<u16> m_known_objects;
 
 private:
 	/*
@@ -326,7 +327,7 @@ class RemoteClient
 		Key is position, value is dummy.
 		No MapBlock* is stored here because the blocks can get deleted.
 	*/
-	core::map<v3s16, bool> m_blocks_sent;
+	std::set<v3s16> m_blocks_sent;
 	s16 m_nearest_unsent_d;
 	v3s16 m_last_center;
 	float m_nearest_unsent_reset_timer;
@@ -339,7 +340,7 @@ class RemoteClient
 		Block is removed when GOTBLOCKS is received.
 		Value is time from sending. (not used at the moment)
 	*/
-	core::map<v3s16, float> m_blocks_sending;
+	std::map<v3s16, float> m_blocks_sending;
 
 	/*
 		Count of excess GotBlocks().
@@ -381,7 +382,7 @@ class Server : public con::PeerHandler, public MapEventReceiver,
 	void Receive();
 	void ProcessData(u8 *data, u32 datasize, u16 peer_id);
 
-	core::list<PlayerInfo> getPlayerInfo();
+	std::list<PlayerInfo> getPlayerInfo();
 
 	// Environment must be locked when called
 	void setTimeOfDay(u32 time)
@@ -455,6 +456,35 @@ class Server : public con::PeerHandler, public MapEventReceiver,
 	// Envlock and conlock should be locked when calling this
 	void notifyPlayer(const char *name, const std::wstring msg);
 	void notifyPlayers(const std::wstring msg);
+	void spawnParticle(const char *playername,
+		v3f pos, v3f velocity, v3f acceleration,
+		float expirationtime, float size,
+		bool collisiondetection, std::string texture);
+
+	void spawnParticleAll(v3f pos, v3f velocity, v3f acceleration,
+		float expirationtime, float size,
+		bool collisiondetection, std::string texture);
+
+	u32 addParticleSpawner(const char *playername,
+		u16 amount, float spawntime,
+		v3f minpos, v3f maxpos,
+		v3f minvel, v3f maxvel,
+		v3f minacc, v3f maxacc,
+		float minexptime, float maxexptime,
+		float minsize, float maxsize,
+		bool collisiondetection, std::string texture);
+
+	u32 addParticleSpawnerAll(u16 amount, float spawntime,
+		v3f minpos, v3f maxpos,
+		v3f minvel, v3f maxvel,
+		v3f minacc, v3f maxacc,
+		float minexptime, float maxexptime,
+		float minsize, float maxsize,
+		bool collisiondetection, std::string texture);
+
+	void deleteParticleSpawner(const char *playername, u32 id);
+	void deleteParticleSpawnerAll(u32 id);
+
 
 	void queueBlockEmerge(v3s16 blockpos, bool allow_generate);
 
@@ -494,7 +524,7 @@ class Server : public con::PeerHandler, public MapEventReceiver,
 	IWritableCraftDefManager* getWritableCraftDefManager();
 
 	const ModSpec* getModSpec(const std::string &modname);
-	void getModNames(core::list<std::string> &modlist);
+	void getModNames(std::list<std::string> &modlist);
 	std::string getBuiltinLuaPath();
 
 	std::string getWorldPath(){ return m_path_world; }
@@ -526,7 +556,7 @@ class Server : public con::PeerHandler, public MapEventReceiver,
 	static void SendDeathscreen(con::Connection &con, u16 peer_id,
 			bool set_camera_point_target, v3f camera_point_target);
 	static void SendItemDef(con::Connection &con, u16 peer_id,
-			IItemDefManager *itemdef);
+			IItemDefManager *itemdef, u16 protocol_version);
 	static void SendNodeDef(con::Connection &con, u16 peer_id,
 			INodeDefManager *nodedef, u16 protocol_version);
 
@@ -553,9 +583,9 @@ class Server : public con::PeerHandler, public MapEventReceiver,
 	*/
 	// Envlock and conlock should be locked when calling these
 	void sendRemoveNode(v3s16 p, u16 ignore_id=0,
-			core::list<u16> *far_players=NULL, float far_d_nodes=100);
+			std::list<u16> *far_players=NULL, float far_d_nodes=100);
 	void sendAddNode(v3s16 p, MapNode n, u16 ignore_id=0,
-			core::list<u16> *far_players=NULL, float far_d_nodes=100);
+			std::list<u16> *far_players=NULL, float far_d_nodes=100);
 	void setBlockNotSent(v3s16 p);
 
 	// Environment and Connection must be locked when called
@@ -567,12 +597,47 @@ class Server : public con::PeerHandler, public MapEventReceiver,
 	void fillMediaCache();
 	void sendMediaAnnouncement(u16 peer_id);
 	void sendRequestedMedia(u16 peer_id,
-			const core::list<MediaRequest> &tosend);
+			const std::list<MediaRequest> &tosend);
 
 	void sendDetachedInventory(const std::string &name, u16 peer_id);
 	void sendDetachedInventoryToAll(const std::string &name);
 	void sendDetachedInventories(u16 peer_id);
 
+	// Adds a ParticleSpawner on peer with peer_id
+	void SendAddParticleSpawner(u16 peer_id, u16 amount, float spawntime,
+		v3f minpos, v3f maxpos,
+		v3f minvel, v3f maxvel,
+		v3f minacc, v3f maxacc,
+		float minexptime, float maxexptime,
+		float minsize, float maxsize,
+		bool collisiondetection, std::string texture, u32 id);
+
+	// Adds a ParticleSpawner on all peers
+	void SendAddParticleSpawnerAll(u16 amount, float spawntime,
+		v3f minpos, v3f maxpos,
+		v3f minvel, v3f maxvel,
+		v3f minacc, v3f maxacc,
+		float minexptime, float maxexptime,
+		float minsize, float maxsize,
+		bool collisiondetection, std::string texture, u32 id);
+
+	// Deletes ParticleSpawner on a single client
+	void SendDeleteParticleSpawner(u16 peer_id, u32 id);
+
+	// Deletes ParticleSpawner on all clients
+	void SendDeleteParticleSpawnerAll(u32 id);
+
+	// Spawns particle on single client
+	void SendSpawnParticle(u16 peer_id,
+		v3f pos, v3f velocity, v3f acceleration,
+		float expirationtime, float size,
+		bool collisiondetection, std::string texture);
+
+	// Spawns particle on all clients
+	void SendSpawnParticleAll(v3f pos, v3f velocity, v3f acceleration,
+		float expirationtime, float size,
+		bool collisiondetection, std::string texture);
+
 	/*
 		Something random
 	*/
@@ -655,7 +720,7 @@ class Server : public con::PeerHandler, public MapEventReceiver,
 	con::Connection m_con;
 	JMutex m_con_mutex;
 	// Connected clients (behind the con mutex)
-	core::map<u16, RemoteClient*> m_clients;
+	std::map<u16, RemoteClient*> m_clients;
 	u16 m_clients_number; //for announcing masterserver
 
 	// Bann checking
@@ -735,7 +800,7 @@ class Server : public con::PeerHandler, public MapEventReceiver,
 	*/
 
 	// Mod parent directory paths
-	core::list<std::string> m_modspaths;
+	std::list<std::string> m_modspaths;
 
 	bool m_shutdown_requested;
 
@@ -789,6 +854,11 @@ class Server : public con::PeerHandler, public MapEventReceiver,
 	*/
 	// key = name
 	std::map<std::string, Inventory*> m_detached_inventories;
+
+	/*
+		Particles
+	*/
+	std::vector<u32> m_particlespawner_ids;
 };
 
 /*
diff --git a/src/serverlist.cpp b/src/serverlist.cpp
index d37b5d6371fed45689699d5384e83c601913eef8..93f9d2435dea91020c4ccb3bbd12305aa720db77 100644
--- a/src/serverlist.cpp
+++ b/src/serverlist.cpp
@@ -232,7 +232,7 @@ static size_t ServerAnnounceCallback(void *contents, size_t size, size_t nmemb,
     //((std::string*)userp)->append((char*)contents, size * nmemb);
     //return size * nmemb;
 }
-void sendAnnounce(std::string action, u16 clients) {
+void sendAnnounce(std::string action, u16 clients, double uptime, std::string gameid) {
 	Json::Value server;
 	if (action.size())
 		server["action"]	= action;
@@ -250,6 +250,9 @@ void sendAnnounce(std::string action, u16 clients) {
 		server["pvp"]		= g_settings->getBool("enable_pvp");
 		server["clients"]	= clients;
 		server["clients_max"]	= g_settings->get("max_users");
+		if (uptime >=1) server["uptime"] = (int)uptime;
+		if (gameid!="") server["gameid"] = gameid;
+		
 	}
 	if(server["action"] == "start")
 		actionstream << "announcing to " << g_settings->get("serverlist_url") << std::endl;
@@ -259,6 +262,7 @@ void sendAnnounce(std::string action, u16 clients) {
 	if (curl)
 	{
 		CURLcode res;
+		curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1);
 		curl_easy_setopt(curl, CURLOPT_URL, (g_settings->get("serverlist_url")+std::string("/announce?json=")+curl_easy_escape(curl, writer.write( server ).c_str(), 0)).c_str());
 		//curl_easy_setopt(curl, CURLOPT_USERAGENT, "minetest");
 		curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, ServerList::ServerAnnounceCallback);
diff --git a/src/serverlist.h b/src/serverlist.h
index e81e64c5b3acb8d9eb17b4fadc1c280961a9cce2..d01415c504c845496fdbb58214cd1ff559c5477d 100644
--- a/src/serverlist.h
+++ b/src/serverlist.h
@@ -39,7 +39,7 @@ namespace ServerList
 	std::vector<ServerListSpec> deSerializeJson(std::string liststring);
 	std::string serializeJson(std::vector<ServerListSpec>);
 	#if USE_CURL
-	void sendAnnounce(std::string action = "", u16 clients = 0);
+	void sendAnnounce(std::string action = "", u16 clients = 0, double uptime = 0, std::string gameid = "");
 	#endif
 } //ServerList namespace
 
diff --git a/src/serverobject.cpp b/src/serverobject.cpp
index beb17d31f90dacfefe97d15668b7d98c2d69041b..95735de17ce4a2f36aacba35ab40179acabfe12e 100644
--- a/src/serverobject.cpp
+++ b/src/serverobject.cpp
@@ -43,9 +43,9 @@ ServerActiveObject* ServerActiveObject::create(u8 type,
 		const std::string &data)
 {
 	// Find factory function
-	core::map<u16, Factory>::Node *n;
+	std::map<u16, Factory>::iterator n;
 	n = m_types.find(type);
-	if(n == NULL)
+	if(n == m_types.end())
 	{
 		// If factory is not found, just return.
 		dstream<<"WARNING: ServerActiveObject: No factory for type="
@@ -53,18 +53,18 @@ ServerActiveObject* ServerActiveObject::create(u8 type,
 		return NULL;
 	}
 
-	Factory f = n->getValue();
+	Factory f = n->second;
 	ServerActiveObject *object = (*f)(env, pos, data);
 	return object;
 }
 
 void ServerActiveObject::registerType(u16 type, Factory f)
 {
-	core::map<u16, Factory>::Node *n;
+	std::map<u16, Factory>::iterator n;
 	n = m_types.find(type);
-	if(n)
+	if(n != m_types.end())
 		return;
-	m_types.insert(type, f);
+	m_types[type] = f;
 }
 
 float ServerActiveObject::getMinimumSavedMovement()
diff --git a/src/serverobject.h b/src/serverobject.h
index 6525270f624eec1436577d8030e7122bb0e4f1c8..7a5b47bd16f671ecf95e43d38c75ee789f482c4a 100644
--- a/src/serverobject.h
+++ b/src/serverobject.h
@@ -235,7 +235,7 @@ class ServerActiveObject : public ActiveObject
 
 private:
 	// Used for creating objects based on type
-	static core::map<u16, Factory> m_types;
+	static std::map<u16, Factory> m_types;
 };
 
 #endif
diff --git a/src/settings.h b/src/settings.h
index 7ac308cc0ee468f2027afd2c56afac205d74e641..1b7e3cb095bbba716ba13988adea002103f7ceb9 100644
--- a/src/settings.h
+++ b/src/settings.h
@@ -33,6 +33,9 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include "log.h"
 #include "util/string.h"
 #include "porting.h"
+#include <list>
+#include <map>
+#include <set>
 
 enum ValueType
 {
@@ -63,12 +66,12 @@ class Settings
 	{
 		JMutexAutoLock lock(m_mutex);
 
-		for(core::map<std::string, std::string>::Iterator
-				i = m_settings.getIterator();
-				i.atEnd() == false; i++)
+		for(std::map<std::string, std::string>::iterator
+				i = m_settings.begin();
+				i != m_settings.end(); ++i)
 		{
-			std::string name = i.getNode()->getKey();
-			std::string value = i.getNode()->getValue();
+			std::string name = i->first;
+			std::string value = i->second;
 			os<<name<<" = "<<value<<"\n";
 		}
 	}
@@ -76,12 +79,11 @@ class Settings
 	// return all keys used 
 	std::vector<std::string> getNames(){
 		std::vector<std::string> names;
-		for(core::map<std::string, std::string>::Iterator
-				i = m_settings.getIterator();
-				i.atEnd() == false; i++)
+		for(std::map<std::string, std::string>::iterator
+				i = m_settings.begin();
+				i != m_settings.end(); ++i)
 		{
-			std::string name = i.getNode()->getKey();
-			names.push_back(name);
+			names.push_back(i->first);
 		}
 		return names;  
 	}
@@ -89,7 +91,7 @@ class Settings
 	// remove a setting
 	bool remove(const std::string& name)
 	{
-		return m_settings.remove(name);
+		return m_settings.erase(name);
 	}
 
 
@@ -188,8 +190,8 @@ class Settings
 		Returns false on EOF
 	*/
 	bool getUpdatedConfigObject(std::istream &is,
-			core::list<std::string> &dst,
-			core::map<std::string, bool> &updated,
+			std::list<std::string> &dst,
+			std::set<std::string> &updated,
 			bool &value_changed)
 	{
 		JMutexAutoLock lock(m_mutex);
@@ -228,7 +230,7 @@ class Settings
 		std::string value = sf.next("\n");
 		value = trim(value);
 
-		if(m_settings.find(name))
+		if(m_settings.find(name) != m_settings.end())
 		{
 			std::string newvalue = m_settings[name];
 
@@ -242,7 +244,7 @@ class Settings
 
 			dst.push_back(name + " = " + newvalue + line_end);
 
-			updated[name] = true;
+			updated.insert(name);
 		}
 		else //file contains a setting which is not in m_settings
 			value_changed=true;
@@ -260,8 +262,8 @@ class Settings
 		infostream<<"Updating configuration file: \""
 				<<filename<<"\""<<std::endl;
 
-		core::list<std::string> objects;
-		core::map<std::string, bool> updated;
+		std::list<std::string> objects;
+		std::set<std::string> updated;
 		bool something_actually_changed = false;
 
 		// Read and modify stuff
@@ -286,11 +288,11 @@ class Settings
 		// If something not yet determined to have been changed, check if
 		// any new stuff was added
 		if(!something_actually_changed){
-			for(core::map<std::string, std::string>::Iterator
-					i = m_settings.getIterator();
-					i.atEnd() == false; i++)
+			for(std::map<std::string, std::string>::iterator
+					i = m_settings.begin();
+					i != m_settings.end(); ++i)
 			{
-				if(updated.find(i.getNode()->getKey()))
+				if(updated.find(i->first) != updated.end())
 					continue;
 				something_actually_changed = true;
 				break;
@@ -318,9 +320,9 @@ class Settings
 			/*
 				Write updated stuff
 			*/
-			for(core::list<std::string>::Iterator
+			for(std::list<std::string>::iterator
 					i = objects.begin();
-					i != objects.end(); i++)
+					i != objects.end(); ++i)
 			{
 				os<<(*i);
 			}
@@ -328,14 +330,14 @@ class Settings
 			/*
 				Write stuff that was not already in the file
 			*/
-			for(core::map<std::string, std::string>::Iterator
-					i = m_settings.getIterator();
-					i.atEnd() == false; i++)
+			for(std::map<std::string, std::string>::iterator
+					i = m_settings.begin();
+					i != m_settings.end(); ++i)
 			{
-				if(updated.find(i.getNode()->getKey()))
+				if(updated.find(i->first) != updated.end())
 					continue;
-				std::string name = i.getNode()->getKey();
-				std::string value = i.getNode()->getValue();
+				std::string name = i->first;
+				std::string value = i->second;
 				infostream<<"Adding \""<<name<<"\" = \""<<value<<"\""
 						<<std::endl;
 				os<<name<<" = "<<value<<"\n";
@@ -351,7 +353,7 @@ class Settings
 		returns true on success
 	*/
 	bool parseCommandLine(int argc, char *argv[],
-			core::map<std::string, ValueSpec> &allowed_options)
+			std::map<std::string, ValueSpec> &allowed_options)
 	{
 		int nonopt_index = 0;
 		int i=1;
@@ -379,16 +381,16 @@ class Settings
 
 			std::string name = argname.substr(2);
 
-			core::map<std::string, ValueSpec>::Node *n;
+			std::map<std::string, ValueSpec>::iterator n;
 			n = allowed_options.find(name);
-			if(n == NULL)
+			if(n == allowed_options.end())
 			{
 				errorstream<<"Unknown command-line parameter \""
 						<<argname<<"\""<<std::endl;
 				return false;
 			}
 
-			ValueType type = n->getValue().type;
+			ValueType type = n->second.type;
 
 			std::string value = "";
 
@@ -444,25 +446,25 @@ class Settings
 	{
 		JMutexAutoLock lock(m_mutex);
 
-		return (m_settings.find(name) || m_defaults.find(name));
+		return (m_settings.find(name) != m_settings.end() || m_defaults.find(name) != m_defaults.end());
 	}
 
 	std::string get(std::string name)
 	{
 		JMutexAutoLock lock(m_mutex);
 
-		core::map<std::string, std::string>::Node *n;
+		std::map<std::string, std::string>::iterator n;
 		n = m_settings.find(name);
-		if(n == NULL)
+		if(n == m_settings.end())
 		{
 			n = m_defaults.find(name);
-			if(n == NULL)
+			if(n == m_defaults.end())
 			{
-				throw SettingNotFoundException("Setting not found");
+				throw SettingNotFoundException(("Setting [" + name + "] not found ").c_str());
 			}
 		}
 
-		return n->getValue();
+		return n->second;
 	}
 
 	bool getBool(std::string name)
@@ -919,19 +921,8 @@ class Settings
 		if(&other == this)
 			return;
 
-		for(core::map<std::string, std::string>::Iterator
-				i = other.m_settings.getIterator();
-				i.atEnd() == false; i++)
-		{
-			m_settings[i.getNode()->getKey()] = i.getNode()->getValue();
-		}
-
-		for(core::map<std::string, std::string>::Iterator
-				i = other.m_defaults.getIterator();
-				i.atEnd() == false; i++)
-		{
-			m_defaults[i.getNode()->getKey()] = i.getNode()->getValue();
-		}
+		m_settings.insert(other.m_settings.begin(), other.m_settings.end());
+		m_defaults.insert(other.m_defaults.begin(), other.m_defaults.end());
 
 		return;
 	}
@@ -944,21 +935,7 @@ class Settings
 		if(&other == this)
 			return *this;
 
-		for(core::map<std::string, std::string>::Iterator
-				i = other.m_settings.getIterator();
-				i.atEnd() == false; i++)
-		{
-			m_settings.insert(i.getNode()->getKey(),
-					i.getNode()->getValue());
-		}
-
-		for(core::map<std::string, std::string>::Iterator
-				i = other.m_defaults.getIterator();
-				i.atEnd() == false; i++)
-		{
-			m_defaults.insert(i.getNode()->getKey(),
-					i.getNode()->getValue());
-		}
+		update(other);
 
 		return *this;
 
@@ -979,8 +956,8 @@ class Settings
 	}
 
 private:
-	core::map<std::string, std::string> m_settings;
-	core::map<std::string, std::string> m_defaults;
+	std::map<std::string, std::string> m_settings;
+	std::map<std::string, std::string> m_defaults;
 	// All methods that access m_settings/m_defaults directly should lock this.
 	JMutex m_mutex;
 };
diff --git a/src/shader.cpp b/src/shader.cpp
index 7e3d16e8bc9e8fdb4cc865e90256780958fc3696..a224c82bb271eda6053f0e57f382c618b4ff9772 100644
--- a/src/shader.cpp
+++ b/src/shader.cpp
@@ -125,10 +125,10 @@ class SourceShaderCache
 			const std::string &filename)
 	{
 		std::string combined = name_of_shader + DIR_DELIM + filename;
-		core::map<std::string, std::string>::Node *n;
+		std::map<std::string, std::string>::iterator n;
 		n = m_programs.find(combined);
-		if(n)
-			return n->getValue();
+		if(n != m_programs.end())
+			return n->second;
 		return "";
 	}
 	// Primarily fetches from cache, secondarily tries to read from filesystem
@@ -136,10 +136,10 @@ class SourceShaderCache
 			const std::string &filename)
 	{
 		std::string combined = name_of_shader + DIR_DELIM + filename;
-		core::map<std::string, std::string>::Node *n;
+		std::map<std::string, std::string>::iterator n;
 		n = m_programs.find(combined);
-		if(n)
-			return n->getValue();
+		if(n != m_programs.end())
+			return n->second;
 		std::string path = getShaderPath(name_of_shader, filename);
 		if(path == ""){
 			infostream<<"SourceShaderCache::getOrLoad(): No path found for \""
@@ -156,7 +156,7 @@ class SourceShaderCache
 		return "";
 	}
 private:
-	core::map<std::string, std::string> m_programs;
+	std::map<std::string, std::string> m_programs;
 	std::string readFile(const std::string &path)
 	{
 		std::ifstream is(path.c_str(), std::ios::binary);
@@ -332,9 +332,9 @@ class ShaderSource : public IWritableShaderSource, public IShaderConstantSetterR
 
 	// A shader id is index in this array.
 	// The first position contains a dummy shader.
-	core::array<ShaderInfo> m_shaderinfo_cache;
+	std::vector<ShaderInfo> m_shaderinfo_cache;
 	// Maps a shader name to an index in the former.
-	core::map<std::string, u32> m_name_to_id;
+	std::map<std::string, u32> m_name_to_id;
 	// The two former containers are behind this mutex
 	JMutex m_shaderinfo_cache_mutex;
 
@@ -343,7 +343,7 @@ class ShaderSource : public IWritableShaderSource, public IShaderConstantSetterR
 
 	// Global constant setters
 	// TODO: Delete these in the destructor
-	core::array<IShaderConstantSetter*> m_global_setters;
+	std::vector<IShaderConstantSetter*> m_global_setters;
 };
 
 IWritableShaderSource* createShaderSource(IrrlichtDevice *device)
@@ -399,10 +399,10 @@ u32 ShaderSource::getShaderId(const std::string &name)
 			See if shader already exists
 		*/
 		JMutexAutoLock lock(m_shaderinfo_cache_mutex);
-		core::map<std::string, u32>::Node *n;
+		std::map<std::string, u32>::iterator n;
 		n = m_name_to_id.find(name);
-		if(n != NULL)
-			return n->getValue();
+		if(n != m_name_to_id.end())
+			return n->second;
 	}
 
 	/*
@@ -471,12 +471,12 @@ u32 ShaderSource::getShaderIdDirect(const std::string &name)
 	{
 		JMutexAutoLock lock(m_shaderinfo_cache_mutex);
 
-		core::map<std::string, u32>::Node *n;
+		std::map<std::string, u32>::iterator n;
 		n = m_name_to_id.find(name);
-		if(n != NULL){
+		if(n != m_name_to_id.end()){
 			/*infostream<<"getShaderIdDirect(): \""<<name
 					<<"\" found in cache"<<std::endl;*/
-			return n->getValue();
+			return n->second;
 		}
 	}
 
@@ -494,7 +494,7 @@ u32 ShaderSource::getShaderIdDirect(const std::string &name)
 
 	u32 id = m_shaderinfo_cache.size();
 	m_shaderinfo_cache.push_back(info);
-	m_name_to_id.insert(name, id);
+	m_name_to_id[name] = id;
 
 	/*infostream<<"getShaderIdDirect(): "
 			<<"Returning id="<<id<<" for name \""<<name<<"\""<<std::endl;*/
@@ -531,7 +531,7 @@ void ShaderSource::processQueue()
 	/*
 		Fetch shaders
 	*/
-	if(m_get_shader_queue.size() > 0){
+	if(!m_get_shader_queue.empty()){
 		GetRequest<std::string, u32, u8, u8>
 				request = m_get_shader_queue.pop();
 
diff --git a/src/staticobject.cpp b/src/staticobject.cpp
index 48fadaf069cfc568ec27b0270c7ae02c4562289a..973257fcfa1482853ebec4051224780a37da38e1 100644
--- a/src/staticobject.cpp
+++ b/src/staticobject.cpp
@@ -58,18 +58,18 @@ void StaticObjectList::serialize(std::ostream &os)
 	u16 count = m_stored.size() + m_active.size();
 	writeU16((u8*)buf, count);
 	os.write(buf, 2);
-	for(core::list<StaticObject>::Iterator
+	for(std::list<StaticObject>::iterator
 			i = m_stored.begin();
-			i != m_stored.end(); i++)
+			i != m_stored.end(); ++i)
 	{
 		StaticObject &s_obj = *i;
 		s_obj.serialize(os);
 	}
-	for(core::map<u16, StaticObject>::Iterator
-			i = m_active.getIterator();
-			i.atEnd()==false; i++)
+	for(std::map<u16, StaticObject>::iterator
+			i = m_active.begin();
+			i != m_active.end(); ++i)
 	{
-		StaticObject s_obj = i.getNode()->getValue();
+		StaticObject s_obj = i->second;
 		s_obj.serialize(os);
 	}
 }
diff --git a/src/staticobject.h b/src/staticobject.h
index c8427fe47bd74dc2995c840f43b20202ec4f86e8..640747e96dbe3152823d02cb0d6a16444a6582d7 100644
--- a/src/staticobject.h
+++ b/src/staticobject.h
@@ -23,6 +23,8 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include "irrlichttypes_bloated.h"
 #include <string>
 #include <sstream>
+#include <list>
+#include <map>
 #include "debug.h"
 
 struct StaticObject
@@ -62,27 +64,27 @@ class StaticObjectList
 		}
 		else
 		{
-			if(m_active.find(id) != NULL)
+			if(m_active.find(id) != m_active.end())
 			{
 				dstream<<"ERROR: StaticObjectList::insert(): "
 						<<"id already exists"<<std::endl;
 				assert(0);
 				return;
 			}
-			m_active.insert(id, obj);
+			m_active[id] = obj;
 		}
 	}
 
 	void remove(u16 id)
 	{
 		assert(id != 0);
-		if(m_active.find(id) == NULL)
+		if(m_active.find(id) == m_active.end())
 		{
 			dstream<<"WARNING: StaticObjectList::remove(): id="<<id
 					<<" not found"<<std::endl;
 			return;
 		}
-		m_active.remove(id);
+		m_active.erase(id);
 	}
 
 	void serialize(std::ostream &os);
@@ -93,8 +95,8 @@ class StaticObjectList
 		from m_stored and inserted to m_active.
 		The caller directly manipulates these containers.
 	*/
-	core::list<StaticObject> m_stored;
-	core::map<u16, StaticObject> m_active;
+	std::list<StaticObject> m_stored;
+	std::map<u16, StaticObject> m_active;
 
 private:
 };
diff --git a/src/strfnd.h b/src/strfnd.h
index d28aa73b24ab7cb4253b0f0e3288c91c2fab018c..4a72edf3c50f474aa38e841ba15973a29c2772ff 100644
--- a/src/strfnd.h
+++ b/src/strfnd.h
@@ -65,6 +65,25 @@ class Strfnd{
 		//std::cout<<"palautus=\""<<palautus<<"\""<<std::endl;
         return palautus;
     }
+    
+    // Returns substr of tek up to the next occurence of plop that isn't escaped with '\'
+    std::string next_esc(std::string plop) {
+		size_t n, realp;
+		
+    	if (p >= tek.size())
+    		return "";
+		
+		realp = p;
+		do {
+			n = tek.find(plop, p);
+			if (n == std::string::npos || plop == "")
+				n = tek.length();
+			p = n + plop.length();
+		} while (n > 0 && tek[n - 1] == '\\');
+		
+		return tek.substr(realp, n - realp);
+    }
+    
 	void skip_over(std::string chars){
 		while(p < tek.size()){
 			bool is = false;
@@ -128,6 +147,24 @@ class WStrfnd{
 		//std::cout<<"palautus=\""<<palautus<<"\""<<std::endl;
         return palautus;
     }
+    
+    std::wstring next_esc(std::wstring plop) {
+		size_t n, realp;
+		
+    	if (p >= tek.size())
+    		return L"";
+		
+		realp = p;
+		do {
+			n = tek.find(plop, p);
+			if (n == std::wstring::npos || plop == L"")
+				n = tek.length();
+			p = n + plop.length();
+		} while (n > 0 && tek[n - 1] == '\\');
+		
+		return tek.substr(realp, n - realp);
+    }
+    
     bool atend(){
         if(p>=tek.size()) return true;
         return false;
diff --git a/src/subgame.cpp b/src/subgame.cpp
index 3c8bf53c53989338d0aea89126e62281d44610a3..19ad4e636be379fdd38171d175f72f42b49b455e 100644
--- a/src/subgame.cpp
+++ b/src/subgame.cpp
@@ -24,12 +24,22 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include "log.h"
 #include "util/string.h"
 
-std::string getGameName(const std::string &game_path)
+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;
-	bool succeeded = conf.readConfigFile(conf_path.c_str());
-	if(!succeeded)
+	if(!getGameConfig(game_path, conf))
 		return "";
 	if(!conf.exists("name"))
 		return "";
@@ -117,6 +127,11 @@ std::set<std::string> getAvailableGameIds()
 		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 != "")
diff --git a/src/subgame.h b/src/subgame.h
index 8561c1a52ce322effffba0130557baf266257169..b120b75425482d8d5ee1e43dd54611734a63942b 100644
--- a/src/subgame.h
+++ b/src/subgame.h
@@ -24,6 +24,8 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include <set>
 #include <vector>
 
+class Settings;
+
 #define WORLDNAME_BLACKLISTED_CHARS "/\\"
 
 struct SubgameSpec
@@ -52,6 +54,11 @@ struct SubgameSpec
 	}
 };
 
+// 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);
diff --git a/src/test.cpp b/src/test.cpp
index d868681183b216d4852bee25e8fb39f07c16eeca..d18bd8b931d5064569dc075bf5eb241980714079 100644
--- a/src/test.cpp
+++ b/src/test.cpp
@@ -42,6 +42,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include "util/serialize.h"
 #include "noise.h" // PseudoRandom used for random data for compression
 #include "clientserver.h" // LATEST_PROTOCOL_VERSION
+#include <algorithm>
 
 /*
 	Asserts that the exception occurs
@@ -508,26 +509,26 @@ struct TestVoxelManipulator: public TestBase
 		// An area that is 1 bigger in x+ and z-
 		VoxelArea d(v3s16(-2,-2,-3), v3s16(3,2,2));
 		
-		core::list<VoxelArea> aa;
+		std::list<VoxelArea> aa;
 		d.diff(c, aa);
 		
 		// Correct results
-		core::array<VoxelArea> results;
+		std::vector<VoxelArea> results;
 		results.push_back(VoxelArea(v3s16(-2,-2,-3),v3s16(3,2,-3)));
 		results.push_back(VoxelArea(v3s16(3,-2,-2),v3s16(3,2,2)));
 
 		UASSERT(aa.size() == results.size());
 		
 		infostream<<"Result of diff:"<<std::endl;
-		for(core::list<VoxelArea>::Iterator
-				i = aa.begin(); i != aa.end(); i++)
+		for(std::list<VoxelArea>::const_iterator
+				i = aa.begin(); i != aa.end(); ++i)
 		{
 			i->print(infostream);
 			infostream<<std::endl;
 			
-			s32 j = results.linear_search(*i);
-			UASSERT(j != -1);
-			results.erase(j, 1);
+			std::vector<VoxelArea>::iterator j = std::find(results.begin(), results.end(), *i);
+			UASSERT(j != results.end());
+			results.erase(j);
 		}
 
 
@@ -582,7 +583,7 @@ struct TestVoxelAlgorithms: public TestBase
 			}
 			VoxelArea a(v3s16(0,0,0), v3s16(2,2,2));
 			{
-				core::map<v3s16, bool> light_sources;
+				std::set<v3s16> light_sources;
 				voxalgo::setLight(v, a, 0, ndef);
 				voxalgo::SunlightPropagateResult res = voxalgo::propagateSunlight(
 						v, a, true, light_sources, ndef);
@@ -593,7 +594,7 @@ struct TestVoxelAlgorithms: public TestBase
 			}
 			v.setNodeNoRef(v3s16(0,0,0), MapNode(CONTENT_STONE));
 			{
-				core::map<v3s16, bool> light_sources;
+				std::set<v3s16> light_sources;
 				voxalgo::setLight(v, a, 0, ndef);
 				voxalgo::SunlightPropagateResult res = voxalgo::propagateSunlight(
 						v, a, true, light_sources, ndef);
@@ -602,7 +603,7 @@ struct TestVoxelAlgorithms: public TestBase
 						== LIGHT_SUN);
 			}
 			{
-				core::map<v3s16, bool> light_sources;
+				std::set<v3s16> light_sources;
 				voxalgo::setLight(v, a, 0, ndef);
 				voxalgo::SunlightPropagateResult res = voxalgo::propagateSunlight(
 						v, a, false, light_sources, ndef);
@@ -612,7 +613,7 @@ struct TestVoxelAlgorithms: public TestBase
 			}
 			v.setNodeNoRef(v3s16(1,3,2), MapNode(CONTENT_STONE));
 			{
-				core::map<v3s16, bool> light_sources;
+				std::set<v3s16> light_sources;
 				voxalgo::setLight(v, a, 0, ndef);
 				voxalgo::SunlightPropagateResult res = voxalgo::propagateSunlight(
 						v, a, true, light_sources, ndef);
@@ -621,7 +622,7 @@ struct TestVoxelAlgorithms: public TestBase
 						== 0);
 			}
 			{
-				core::map<v3s16, bool> light_sources;
+				std::set<v3s16> light_sources;
 				voxalgo::setLight(v, a, 0, ndef);
 				voxalgo::SunlightPropagateResult res = voxalgo::propagateSunlight(
 						v, a, false, light_sources, ndef);
@@ -635,14 +636,14 @@ struct TestVoxelAlgorithms: public TestBase
 				v.setNodeNoRef(v3s16(1,-1,2), n);
 			}
 			{
-				core::map<v3s16, bool> light_sources;
+				std::set<v3s16> light_sources;
 				voxalgo::setLight(v, a, 0, ndef);
 				voxalgo::SunlightPropagateResult res = voxalgo::propagateSunlight(
 						v, a, true, light_sources, ndef);
 				UASSERT(res.bottom_sunlight_valid == true);
 			}
 			{
-				core::map<v3s16, bool> light_sources;
+				std::set<v3s16> light_sources;
 				voxalgo::setLight(v, a, 0, ndef);
 				voxalgo::SunlightPropagateResult res = voxalgo::propagateSunlight(
 						v, a, false, light_sources, ndef);
@@ -654,14 +655,14 @@ struct TestVoxelAlgorithms: public TestBase
 				v.setNodeNoRef(v3s16(1,-1,2), n);
 			}
 			{
-				core::map<v3s16, bool> light_sources;
+				std::set<v3s16> light_sources;
 				voxalgo::setLight(v, a, 0, ndef);
 				voxalgo::SunlightPropagateResult res = voxalgo::propagateSunlight(
 						v, a, true, light_sources, ndef);
 				UASSERT(res.bottom_sunlight_valid == false);
 			}
 			{
-				core::map<v3s16, bool> light_sources;
+				std::set<v3s16> light_sources;
 				voxalgo::setLight(v, a, 0, ndef);
 				voxalgo::SunlightPropagateResult res = voxalgo::propagateSunlight(
 						v, a, false, light_sources, ndef);
@@ -669,7 +670,7 @@ struct TestVoxelAlgorithms: public TestBase
 			}
 			v.setNodeNoRef(v3s16(1,3,2), MapNode(CONTENT_IGNORE));
 			{
-				core::map<v3s16, bool> light_sources;
+				std::set<v3s16> light_sources;
 				voxalgo::setLight(v, a, 0, ndef);
 				voxalgo::SunlightPropagateResult res = voxalgo::propagateSunlight(
 						v, a, true, light_sources, ndef);
@@ -697,16 +698,16 @@ struct TestVoxelAlgorithms: public TestBase
 				v.setNode(v3s16(1,1,2), n);
 			}
 			{
-				core::map<v3s16, bool> light_sources;
-				core::map<v3s16, u8> unlight_from;
+				std::set<v3s16> light_sources;
+				std::map<v3s16, u8> unlight_from;
 				voxalgo::clearLightAndCollectSources(v, a, LIGHTBANK_DAY,
 						ndef, light_sources, unlight_from);
 				//v.print(dstream, ndef, VOXELPRINT_LIGHT_DAY);
 				UASSERT(v.getNode(v3s16(0,1,1)).getLight(LIGHTBANK_DAY, ndef)
 						== 0);
-				UASSERT(light_sources.find(v3s16(1,1,1)) != NULL);
+				UASSERT(light_sources.find(v3s16(1,1,1)) != light_sources.end());
 				UASSERT(light_sources.size() == 1);
-				UASSERT(unlight_from.find(v3s16(1,1,2)) != NULL);
+				UASSERT(unlight_from.find(v3s16(1,1,2)) != unlight_from.end());
 				UASSERT(unlight_from.size() == 1);
 			}
 		}
diff --git a/src/tile.cpp b/src/tile.cpp
index 7286293d894affba1fb2f986fa215c0210758c5d..aea9665f5eb69ca3568c12447538a81e7a1b8771 100644
--- a/src/tile.cpp
+++ b/src/tile.cpp
@@ -206,10 +206,10 @@ class SourceImageCache
 	{
 		assert(img);
 		// Remove old image
-		core::map<std::string, video::IImage*>::Node *n;
+		std::map<std::string, video::IImage*>::iterator n;
 		n = m_images.find(name);
-		if(n){
-			video::IImage *oldimg = n->getValue();
+		if(n != m_images.end()){
+			video::IImage *oldimg = n->second;
 			if(oldimg)
 				oldimg->drop();
 		}
@@ -229,20 +229,20 @@ class SourceImageCache
 	}
 	video::IImage* get(const std::string &name)
 	{
-		core::map<std::string, video::IImage*>::Node *n;
+		std::map<std::string, video::IImage*>::iterator n;
 		n = m_images.find(name);
-		if(n)
-			return n->getValue();
+		if(n != m_images.end())
+			return n->second;
 		return NULL;
 	}
 	// Primarily fetches from cache, secondarily tries to read from filesystem
 	video::IImage* getOrLoad(const std::string &name, IrrlichtDevice *device)
 	{
-		core::map<std::string, video::IImage*>::Node *n;
+		std::map<std::string, video::IImage*>::iterator n;
 		n = m_images.find(name);
-		if(n){
-			n->getValue()->grab(); // Grab for caller
-			return n->getValue();
+		if(n != m_images.end()){
+			n->second->grab(); // Grab for caller
+			return n->second;
 		}
 		video::IVideoDriver* driver = device->getVideoDriver();
 		std::string path = getTexturePath(name.c_str());
@@ -263,7 +263,7 @@ class SourceImageCache
 		return img;
 	}
 private:
-	core::map<std::string, video::IImage*> m_images;
+	std::map<std::string, video::IImage*> m_images;
 };
 
 /*
@@ -417,9 +417,9 @@ class TextureSource : public IWritableTextureSource
 
 	// A texture id is index in this array.
 	// The first position contains a NULL texture.
-	core::array<SourceAtlasPointer> m_atlaspointer_cache;
+	std::vector<SourceAtlasPointer> m_atlaspointer_cache;
 	// Maps a texture name to an index in the former.
-	core::map<std::string, u32> m_name_to_id;
+	std::map<std::string, u32> m_name_to_id;
 	// The two former containers are behind this mutex
 	JMutex m_atlaspointer_cache_mutex;
 	
@@ -465,11 +465,11 @@ u32 TextureSource::getTextureId(const std::string &name)
 			See if texture already exists
 		*/
 		JMutexAutoLock lock(m_atlaspointer_cache_mutex);
-		core::map<std::string, u32>::Node *n;
+		std::map<std::string, u32>::iterator n;
 		n = m_name_to_id.find(name);
-		if(n != NULL)
+		if(n != m_name_to_id.end())
 		{
-			return n->getValue();
+			return n->second;
 		}
 	}
 	
@@ -579,13 +579,13 @@ u32 TextureSource::getTextureIdDirect(const std::string &name)
 	{
 		JMutexAutoLock lock(m_atlaspointer_cache_mutex);
 
-		core::map<std::string, u32>::Node *n;
+		std::map<std::string, u32>::iterator n;
 		n = m_name_to_id.find(name);
-		if(n != NULL)
+		if(n != m_name_to_id.end())
 		{
 			/*infostream<<"getTextureIdDirect(): \""<<name
 					<<"\" found in cache"<<std::endl;*/
-			return n->getValue();
+			return n->second;
 		}
 	}
 
@@ -724,7 +724,7 @@ u32 TextureSource::getTextureIdDirect(const std::string &name)
 		baseimg_dim = baseimg->getDimension();
 	SourceAtlasPointer nap(name, ap, baseimg, v2s32(0,0), baseimg_dim);
 	m_atlaspointer_cache.push_back(nap);
-	m_name_to_id.insert(name, id);
+	m_name_to_id[name] = id;
 
 	/*infostream<<"getTextureIdDirect(): "
 			<<"Returning id="<<id<<" for name \""<<name<<"\""<<std::endl;*/
@@ -769,7 +769,7 @@ void TextureSource::processQueue()
 	/*
 		Fetch textures
 	*/
-	if(m_get_texture_queue.size() > 0)
+	if(!m_get_texture_queue.empty())
 	{
 		GetRequest<std::string, u32, u8, u8>
 				request = m_get_texture_queue.pop();
@@ -872,7 +872,7 @@ void TextureSource::buildMainAtlas(class IGameDef *gamedef)
 		main content features
 	*/
 
-	core::map<std::string, bool> sourcelist;
+	std::set<std::string> sourcelist;
 
 	for(u16 j=0; j<MAX_CONTENT+1; j++)
 	{
@@ -882,16 +882,16 @@ void TextureSource::buildMainAtlas(class IGameDef *gamedef)
 		for(u32 i=0; i<6; i++)
 		{
 			std::string name = f.tiledef[i].name;
-			sourcelist[name] = true;
+			sourcelist.insert(name);
 		}
 	}
 	
 	infostream<<"Creating texture atlas out of textures: ";
-	for(core::map<std::string, bool>::Iterator
-			i = sourcelist.getIterator();
-			i.atEnd() == false; i++)
+	for(std::set<std::string>::iterator
+			i = sourcelist.begin();
+			i != sourcelist.end(); ++i)
 	{
-		std::string name = i.getNode()->getKey();
+		std::string name = *i;
 		infostream<<"\""<<name<<"\" ";
 	}
 	infostream<<std::endl;
@@ -910,11 +910,11 @@ void TextureSource::buildMainAtlas(class IGameDef *gamedef)
 	pos_in_atlas.X = column_padding;
 	pos_in_atlas.Y = padding;
 
-	for(core::map<std::string, bool>::Iterator
-			i = sourcelist.getIterator();
-			i.atEnd() == false; i++)
+	for(std::set<std::string>::iterator
+			i = sourcelist.begin();
+			i != sourcelist.end(); ++i)
 	{
-		std::string name = i.getNode()->getKey();
+		std::string name = *i;
 
 		// Generate image by name
 		video::IImage *img2 = generate_image_from_scratch(name, m_device,
@@ -1026,11 +1026,11 @@ void TextureSource::buildMainAtlas(class IGameDef *gamedef)
 		bool reuse_old_id = false;
 		u32 id = m_atlaspointer_cache.size();
 		// Check old id without fetching a texture
-		core::map<std::string, u32>::Node *n;
+		std::map<std::string, u32>::iterator n;
 		n = m_name_to_id.find(name);
 		// If it exists, we will replace the old definition
-		if(n){
-			id = n->getValue();
+		if(n != m_name_to_id.end()){
+			id = n->second;
 			reuse_old_id = true;
 			/*infostream<<"TextureSource::buildMainAtlas(): "
 					<<"Replacing old AtlasPointer"<<std::endl;*/
@@ -1066,12 +1066,12 @@ void TextureSource::buildMainAtlas(class IGameDef *gamedef)
 	/*
 		Second pass: set texture pointer in generated AtlasPointers
 	*/
-	for(core::map<std::string, bool>::Iterator
-			i = sourcelist.getIterator();
-			i.atEnd() == false; i++)
+	for(std::set<std::string>::iterator
+			i = sourcelist.begin();
+			i != sourcelist.end(); ++i)
 	{
-		std::string name = i.getNode()->getKey();
-		if(m_name_to_id.find(name) == NULL)
+		std::string name = *i;
+		if(m_name_to_id.find(name) == m_name_to_id.end())
 			continue;
 		u32 id = m_name_to_id[name];
 		//infostream<<"id of name "<<name<<" is "<<id<<std::endl;
diff --git a/src/tile.h b/src/tile.h
index 07e5bcb5693eb802f69a24dd1c1c6eaf2125a543..c5c7f930346eef67859078355a41c8ae5e5b9c26 100644
--- a/src/tile.h
+++ b/src/tile.h
@@ -205,7 +205,8 @@ struct TileSpec
 			texture == other.texture &&
 			alpha == other.alpha &&
 			material_type == other.material_type &&
-			material_flags == other.material_flags
+			material_flags == other.material_flags &&
+			rotation == other.rotation
 		);
 	}
 
@@ -264,6 +265,7 @@ struct TileSpec
 	// Animation parameters
 	u8 animation_frame_count;
 	u16 animation_frame_length_ms;
+	u8 rotation;
 };
 
 #endif
diff --git a/src/tool.cpp b/src/tool.cpp
index 04f19749c9332c4c059b49d28b31a3817c70444a..4d809e2c44ffc75e3e1e374a439004b19a2d8462 100644
--- a/src/tool.cpp
+++ b/src/tool.cpp
@@ -24,9 +24,12 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include "util/serialize.h"
 #include "util/numeric.h"
 
-void ToolCapabilities::serialize(std::ostream &os) const
+void ToolCapabilities::serialize(std::ostream &os, u16 protocol_version) const
 {
-	writeU8(os, 1); // version
+	if(protocol_version <= 17)
+		writeU8(os, 1); // version
+	else
+		writeU8(os, 2); // version
 	writeF1000(os, full_punch_interval);
 	writeS16(os, max_drop_level);
 	writeU32(os, groupcaps.size());
@@ -44,12 +47,20 @@ void ToolCapabilities::serialize(std::ostream &os) const
 			writeF1000(os, i->second);
 		}
 	}
+	if(protocol_version > 17){
+		writeU32(os, damageGroups.size());
+		for(std::map<std::string, s16>::const_iterator
+				i = damageGroups.begin(); i != damageGroups.end(); i++){
+			os<<serializeString(i->first);
+			writeS16(os, i->second);
+		}
+	}
 }
 
 void ToolCapabilities::deSerialize(std::istream &is)
 {
 	int version = readU8(is);
-	if(version != 1) throw SerializationError(
+	if(version != 1 && version != 2) throw SerializationError(
 			"unsupported ToolCapabilities version");
 	full_punch_interval = readF1000(is);
 	max_drop_level = readS16(is);
@@ -68,6 +79,15 @@ void ToolCapabilities::deSerialize(std::istream &is)
 		}
 		groupcaps[name] = cap;
 	}
+	if(version == 2)
+	{
+		u32 damage_groups_size = readU32(is);
+		for(u32 i=0; i<damage_groups_size; i++){
+			std::string name = deSerializeString(is);
+			s16 rating = readS16(is);
+			damageGroups[name] = rating;
+		}
+	}
 }
 
 DigParams getDigParams(const ItemGroupList &groups,
@@ -136,28 +156,26 @@ DigParams getDigParams(const ItemGroupList &groups,
 	return getDigParams(groups, tp, 1000000);
 }
 
-HitParams getHitParams(const ItemGroupList &groups,
+HitParams getHitParams(const ItemGroupList &armor_groups,
 		const ToolCapabilities *tp, float time_from_last_punch)
 {
-	DigParams digprop = getDigParams(groups, tp,
-			time_from_last_punch);
-	
-	if(time_from_last_punch > tp->full_punch_interval)
-		time_from_last_punch = tp->full_punch_interval;
-	// Damage in hp is equivalent to nodes dug in time_from_last_punch
-	s16 hp = 0;
-	if(digprop.diggable)
-		hp = time_from_last_punch / digprop.time;
-	// Wear is the same as for digging a single node
-	s16 wear = (float)digprop.wear;
-
-	return HitParams(hp, wear, digprop.main_group);
+	s16 damage = 0;
+	float full_punch_interval = tp->full_punch_interval;
+
+	for(std::map<std::string, s16>::const_iterator
+			i = tp->damageGroups.begin(); i != tp->damageGroups.end(); i++){
+		s16 armor = itemgroup_get(armor_groups, i->first);
+		damage += i->second * rangelim(time_from_last_punch * full_punch_interval, 0.0, 1.0)
+				* armor / 100.0;
+	}
+
+	return HitParams(damage, 0);
 }
 
-HitParams getHitParams(const ItemGroupList &groups,
+HitParams getHitParams(const ItemGroupList &armor_groups,
 		const ToolCapabilities *tp)
 {
-	return getHitParams(groups, tp, 1000000);
+	return getHitParams(armor_groups, tp, 1000000);
 }
 
 PunchDamageResult getPunchDamage(
@@ -187,7 +205,6 @@ PunchDamageResult getPunchDamage(
 		result.did_punch = true;
 		result.wear = hitparams.wear;
 		result.damage = hitparams.hp;
-		result.main_group = hitparams.main_group;
 	}
 
 	return result;
diff --git a/src/tool.h b/src/tool.h
index e812a9e368c88cdd6889281c4da770cc0c41009e..509561a1645466f9381833714a65621466c3f035 100644
--- a/src/tool.h
+++ b/src/tool.h
@@ -52,6 +52,7 @@ struct ToolGroupCap
 
 // CLANG SUCKS DONKEY BALLS
 typedef std::map<std::string, struct ToolGroupCap> ToolGCMap;
+typedef std::map<std::string, s16> DamageGroup;
 
 struct ToolCapabilities
 {
@@ -59,19 +60,22 @@ struct ToolCapabilities
 	int max_drop_level;
 	// CLANG SUCKS DONKEY BALLS
 	ToolGCMap groupcaps;
+	DamageGroup damageGroups;
 
 	ToolCapabilities(
 			float full_punch_interval_=1.4,
 			int max_drop_level_=1,
 			// CLANG SUCKS DONKEY BALLS
-			ToolGCMap groupcaps_=ToolGCMap()
+			ToolGCMap groupcaps_=ToolGCMap(),
+			DamageGroup damageGroups_=DamageGroup()
 	):
 		full_punch_interval(full_punch_interval_),
 		max_drop_level(max_drop_level_),
-		groupcaps(groupcaps_)
+		groupcaps(groupcaps_),
+		damageGroups(damageGroups_)
 	{}
 
-	void serialize(std::ostream &os) const;
+	void serialize(std::ostream &os, u16 version) const;
 	void deSerialize(std::istream &is);
 };
 
@@ -103,19 +107,17 @@ struct HitParams
 {
 	s16 hp;
 	s16 wear;
-	std::string main_group;
 
-	HitParams(s16 hp_=0, s16 wear_=0, std::string main_group_=""):
+	HitParams(s16 hp_=0, s16 wear_=0):
 		hp(hp_),
-		wear(wear_),
-		main_group(main_group_)
+		wear(wear_)
 	{}
 };
 
-HitParams getHitParams(const ItemGroupList &groups,
+HitParams getHitParams(const ItemGroupList &armor_groups,
 		const ToolCapabilities *tp, float time_from_last_punch);
 
-HitParams getHitParams(const ItemGroupList &groups,
+HitParams getHitParams(const ItemGroupList &armor_groups,
 		const ToolCapabilities *tp);
 
 struct PunchDamageResult
@@ -123,7 +125,6 @@ struct PunchDamageResult
 	bool did_punch;
 	int damage;
 	int wear;
-	std::string main_group;
 
 	PunchDamageResult():
 		did_punch(false),
diff --git a/src/treegen.cpp b/src/treegen.cpp
index 7b61523294170935284be1c95c46cd6ef04779cf..808cf916a386ada660edd03dff69297445cea4a2 100644
--- a/src/treegen.cpp
+++ b/src/treegen.cpp
@@ -28,15 +28,16 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 
 namespace treegen
 {
+
 void make_tree(ManualMapVoxelManipulator &vmanip, v3s16 p0,
-		bool is_apple_tree, INodeDefManager *ndef,int seed)
+		bool is_apple_tree, INodeDefManager *ndef, int seed)
 {
 	MapNode treenode(ndef->getId("mapgen_tree"));
 	MapNode leavesnode(ndef->getId("mapgen_leaves"));
 	MapNode applenode(ndef->getId("mapgen_apple"));
 
-	PseudoRandom ps(seed);
-	s16 trunk_h = ps.range(4, 5);
+	PseudoRandom pr(seed);
+	s16 trunk_h = pr.range(4, 5);
 	v3s16 p1 = p0;
 	for(s16 ii=0; ii<trunk_h; ii++)
 	{
@@ -72,9 +73,9 @@ void make_tree(ManualMapVoxelManipulator &vmanip, v3s16 p0,
 		s16 d = 1;
 
 		v3s16 p(
-			ps.range(leaves_a.MinEdge.X, leaves_a.MaxEdge.X-d),
-			ps.range(leaves_a.MinEdge.Y, leaves_a.MaxEdge.Y-d),
-			ps.range(leaves_a.MinEdge.Z, leaves_a.MaxEdge.Z-d)
+			pr.range(leaves_a.MinEdge.X, leaves_a.MaxEdge.X-d),
+			pr.range(leaves_a.MinEdge.Y, leaves_a.MaxEdge.Y-d),
+			pr.range(leaves_a.MinEdge.Z, leaves_a.MaxEdge.Z-d)
 		);
 
 		for(s16 z=0; z<=d; z++)
@@ -100,7 +101,7 @@ void make_tree(ManualMapVoxelManipulator &vmanip, v3s16 p0,
 			continue;
 		u32 i = leaves_a.index(x,y,z);
 		if(leaves_d[i] == 1) {
-			bool is_apple = ps.range(0,99) < 10;
+			bool is_apple = pr.range(0,99) < 10;
 			if(is_apple_tree && is_apple) {
 				vmanip.m_data[vi] = applenode;
 			} else {
@@ -111,10 +112,10 @@ void make_tree(ManualMapVoxelManipulator &vmanip, v3s16 p0,
 }
 
 // L-System tree LUA spawner
-void spawn_ltree (ServerEnvironment *env, v3s16 p0, INodeDefManager *ndef, TreeDef tree_definition)
+void spawn_ltree(ServerEnvironment *env, v3s16 p0, INodeDefManager *ndef, TreeDef tree_definition)
 {
 	ServerMap *map = &env->getServerMap();
-	core::map<v3s16, MapBlock*> modified_blocks;
+	std::map<v3s16, MapBlock*> modified_blocks;
 	ManualMapVoxelManipulator vmanip(map);
 	v3s16 tree_blockp = getNodeBlockPos(p0);
 	vmanip.initialEmerge(tree_blockp - v3s16(1,1,1), tree_blockp + v3s16(1,3,1));
@@ -122,23 +123,17 @@ void spawn_ltree (ServerEnvironment *env, v3s16 p0, INodeDefManager *ndef, TreeD
 	vmanip.blitBackAll(&modified_blocks);
 
 	// update lighting
-	core::map<v3s16, MapBlock*> lighting_modified_blocks;
-	for(core::map<v3s16, MapBlock*>::Iterator
-		i = modified_blocks.getIterator();
-		i.atEnd() == false; i++)
-	{
-		lighting_modified_blocks.insert(i.getNode()->getKey(), i.getNode()->getValue());
-	}
+	std::map<v3s16, MapBlock*> lighting_modified_blocks;
+	lighting_modified_blocks.insert(modified_blocks.begin(), modified_blocks.end());
 	map->updateLighting(lighting_modified_blocks, modified_blocks);
 	// Send a MEET_OTHER event
 	MapEditEvent event;
 	event.type = MEET_OTHER;
-	for(core::map<v3s16, MapBlock*>::Iterator
-		i = modified_blocks.getIterator();
-		i.atEnd() == false; i++)
+	for(std::map<v3s16, MapBlock*>::iterator
+		i = modified_blocks.begin();
+		i != modified_blocks.end(); ++i)
 	{
-		v3s16 p = i.getNode()->getKey();
-		event.modified_blocks.insert(p, true);
+		event.modified_blocks.insert(i->first);
 	}
 	map->dispatchEvent(&event);
 }
@@ -512,33 +507,48 @@ v3f transposeMatrix(irr::core::matrix4 M, v3f v)
 	return translated;
 }
 
-#if 0
-static void make_jungletree(VoxelManipulator &vmanip, v3s16 p0,
-		INodeDefManager *ndef)
+void make_jungletree(VoxelManipulator &vmanip, v3s16 p0,
+		INodeDefManager *ndef, int seed)
 {
-	MapNode treenode(ndef->getId("mapgen_jungletree"));
-	MapNode leavesnode(ndef->getId("mapgen_leaves"));
+	content_t c_tree   = ndef->getId("mapgen_jungletree");
+	content_t c_leaves = ndef->getId("mapgen_jungleleaves");
+	if (c_tree == CONTENT_IGNORE)
+		c_tree = ndef->getId("mapgen_tree");
+	if (c_leaves == CONTENT_IGNORE)
+		c_leaves = ndef->getId("mapgen_leaves");
+
+	MapNode treenode(c_tree);
+	MapNode leavesnode(c_leaves);
 
+	PseudoRandom pr(seed);
 	for(s16 x=-1; x<=1; x++)
 	for(s16 z=-1; z<=1; z++)
 	{
-		if(myrand_range(0, 2) == 0)
+		if(pr.range(0, 2) == 0)
 			continue;
 		v3s16 p1 = p0 + v3s16(x,0,z);
 		v3s16 p2 = p0 + v3s16(x,-1,z);
-		if(vmanip.m_area.contains(p2)
-				&& vmanip.m_data[vmanip.m_area.index(p2)] == CONTENT_AIR)
-			vmanip.m_data[vmanip.m_area.index(p2)] = treenode;
-		else if(vmanip.m_area.contains(p1))
-			vmanip.m_data[vmanip.m_area.index(p1)] = treenode;
+		u32 vi1 = vmanip.m_area.index(p1);
+		u32 vi2 = vmanip.m_area.index(p2);
+		
+		if (vmanip.m_area.contains(p2) &&
+			vmanip.m_data[vi2].getContent() == CONTENT_AIR)
+			vmanip.m_data[vi2] = treenode;
+		else if (vmanip.m_area.contains(p1) &&
+				vmanip.m_data[vi1].getContent() == CONTENT_AIR)
+			vmanip.m_data[vi1] = treenode;
 	}
+	vmanip.m_data[vmanip.m_area.index(p0)] = treenode;
 
-	s16 trunk_h = myrand_range(8, 12);
+	s16 trunk_h = pr.range(8, 12);
 	v3s16 p1 = p0;
-	for(s16 ii=0; ii<trunk_h; ii++)
+	for (s16 ii=0; ii<trunk_h; ii++)
 	{
-		if(vmanip.m_area.contains(p1))
-			vmanip.m_data[vmanip.m_area.index(p1)] = treenode;
+		if (vmanip.m_area.contains(p1)) {
+			u32 vi = vmanip.m_area.index(p1);
+			if (vmanip.m_data[vi].getContent() == CONTENT_AIR)
+				vmanip.m_data[vi] = treenode;
+		}
 		p1.Y++;
 	}
 
@@ -568,9 +578,9 @@ static void make_jungletree(VoxelManipulator &vmanip, v3s16 p0,
 		s16 d = 1;
 
 		v3s16 p(
-			myrand_range(leaves_a.MinEdge.X, leaves_a.MaxEdge.X-d),
-			myrand_range(leaves_a.MinEdge.Y, leaves_a.MaxEdge.Y-d),
-			myrand_range(leaves_a.MinEdge.Z, leaves_a.MaxEdge.Z-d)
+			pr.range(leaves_a.MinEdge.X, leaves_a.MaxEdge.X-d),
+			pr.range(leaves_a.MinEdge.Y, leaves_a.MaxEdge.Y-d),
+			pr.range(leaves_a.MinEdge.Z, leaves_a.MaxEdge.Z-d)
 		);
 
 		for(s16 z=0; z<=d; z++)
@@ -591,14 +601,13 @@ static void make_jungletree(VoxelManipulator &vmanip, v3s16 p0,
 		if(vmanip.m_area.contains(p) == false)
 			continue;
 		u32 vi = vmanip.m_area.index(p);
-		if(vmanip.m_data[vi].getContent() != CONTENT_AIR
-				&& vmanip.m_data[vi].getContent() != CONTENT_IGNORE)
+		if (vmanip.m_data[vi].getContent() != CONTENT_AIR &&
+			vmanip.m_data[vi].getContent() != CONTENT_IGNORE)
 			continue;
 		u32 i = leaves_a.index(x,y,z);
 		if(leaves_d[i] == 1)
 			vmanip.m_data[vi] = leavesnode;
 	}
 }
-#endif
 
 }; // namespace treegen
diff --git a/src/treegen.h b/src/treegen.h
index ca4d3e23d3249fd296401b4d591ba186469516ba..16c85cf0a301c079cce25c3e396fa2b37eefcb8a 100644
--- a/src/treegen.h
+++ b/src/treegen.h
@@ -27,33 +27,36 @@ class ManualMapVoxelManipulator;
 class INodeDefManager;
 
 
-namespace treegen
-{
+namespace treegen {
 
-struct TreeDef
-{
-std::string initial_axiom;
-std::string rules_a;
-std::string rules_b;
-std::string rules_c;
-std::string rules_d;
-MapNode trunknode;
-MapNode leavesnode;
-MapNode leaves2node;
-int leaves2_chance;
-int angle;
-int iterations;
-int iterations_random_level;
-std::string trunk_type;
-bool thin_branches;
-MapNode fruitnode;
-int fruit_chance;
-int seed;
-};
+	struct TreeDef {
+		std::string initial_axiom;
+		std::string rules_a;
+		std::string rules_b;
+		std::string rules_c;
+		std::string rules_d;
+
+		MapNode trunknode;
+		MapNode leavesnode;
+		MapNode leaves2node;
+
+		int leaves2_chance;
+		int angle;
+		int iterations;
+		int iterations_random_level;
+		std::string trunk_type;
+		bool thin_branches;
+		MapNode fruitnode;
+		int fruit_chance;
+		int seed;
+	};
 
 	// Add default tree
 	void make_tree(ManualMapVoxelManipulator &vmanip, v3s16 p0,
-		bool is_apple_tree, INodeDefManager *ndef,int seed);
+		bool is_apple_tree, INodeDefManager *ndef, int seed);
+	// Add jungle tree
+	void make_jungletree(VoxelManipulator &vmanip, v3s16 p0,
+		INodeDefManager *ndef, int seed);
 
 	// Add L-Systems tree (used by engine)
 	void make_ltree(ManualMapVoxelManipulator &vmanip, v3s16 p0, INodeDefManager *ndef,
@@ -73,7 +76,7 @@ int seed;
 		PseudoRandom ps, TreeDef &tree_definition);
 	void tree_fruit_placement(ManualMapVoxelManipulator &vmanip, v3f p0,
 		TreeDef &tree_definition);
-	irr::core::matrix4 setRotationAxisRadians(irr::core::matrix4 M, double angle,v3f axis);
+	irr::core::matrix4 setRotationAxisRadians(irr::core::matrix4 M, double angle, v3f axis);
 
 	v3f transposeMatrix(irr::core::matrix4 M ,v3f v);
 
diff --git a/src/util/container.h b/src/util/container.h
index 775372649bdc4ee14d2067255976dbe032a862d4..9bb388f0e69bac2558e1d9bd2bedb164c26db38d 100644
--- a/src/util/container.h
+++ b/src/util/container.h
@@ -24,6 +24,8 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include <jmutex.h>
 #include <jmutexautolock.h>
 #include "../porting.h" // For sleep_ms
+#include <list>
+#include <vector>
 
 /*
 	Queue with unique values with fast checking of value existence
@@ -43,11 +45,11 @@ class UniqueQueue
 	bool push_back(Value value)
 	{
 		// Check if already exists
-		if(m_map.find(value) != NULL)
+		if(m_map.find(value) != m_map.end())
 			return false;
 
 		// Add
-		m_map.insert(value, 0);
+		m_map[value] = 0;
 		m_list.push_back(value);
 		
 		return true;
@@ -55,22 +57,21 @@ class UniqueQueue
 
 	Value pop_front()
 	{
-		typename core::list<Value>::Iterator i = m_list.begin();
+		typename std::list<Value>::iterator i = m_list.begin();
 		Value value = *i;
-		m_map.remove(value);
+		m_map.erase(value);
 		m_list.erase(i);
 		return value;
 	}
 
 	u32 size()
 	{
-		assert(m_list.size() == m_map.size());
-		return m_list.size();
+		return m_map.size();
 	}
 
 private:
-	core::map<Value, u8> m_map;
-	core::list<Value> m_list;
+	std::map<Value, u8> m_map;
+	std::list<Value> m_list;
 };
 
 #if 1
@@ -95,31 +96,31 @@ class MutexedMap
 	{
 		JMutexAutoLock lock(m_mutex);
 
-		typename core::map<Key, Value>::Node *n;
+		typename std::map<Key, Value>::iterator n;
 		n = m_values.find(name);
 
-		if(n == NULL)
+		if(n == m_values.end())
 			return false;
 		
 		if(result != NULL)
-			*result = n->getValue();
+			*result = n->second;
 			
 		return true;
 	}
 
-	core::list<Value> getValues()
+	std::list<Value> getValues()
 	{
-		core::list<Value> result;
-		for(typename core::map<Key, Value>::Iterator
-				i = m_values.getIterator();
-				i.atEnd() == false; i++){
-			result.push_back(i.getNode()->getValue());
+		std::list<Value> result;
+		for(typename std::map<Key, Value>::iterator
+				i = m_values.begin();
+				i != m_values.end(); ++i){
+			result.push_back(i->second);
 		}
 		return result;
 	}
 
 private:
-	core::map<Key, Value> m_values;
+	std::map<Key, Value> m_values;
 	JMutex m_mutex;
 };
 #endif
@@ -163,10 +164,10 @@ class MutexedIdGenerator
 	u32 getId(const T &value)
 	{
 		JMutexAutoLock lock(m_mutex);
-		typename core::map<T, u32>::Node *n;
+		typename std::map<T, u32>::iterator n;
 		n = m_value_to_id.find(value);
-		if(n != NULL)
-			return n->getValue();
+		if(n != m_value_to_id.end())
+			return n->second;
 		m_id_to_value.push_back(value);
 		u32 new_id = m_id_to_value.size();
 		m_value_to_id.insert(value, new_id);
@@ -176,8 +177,8 @@ class MutexedIdGenerator
 private:
 	JMutex m_mutex;
 	// Values are stored here at id-1 position (id 1 = [0])
-	core::array<T> m_id_to_value;
-	core::map<T, u32> m_value_to_id;
+	std::vector<T> m_id_to_value;
+	std::map<T, u32> m_value_to_id;
 };
 
 /*
@@ -187,39 +188,52 @@ template<typename T>
 class Queue
 {
 public:
+	Queue():
+		m_list_size(0)
+	{}
+
 	void push_back(T t)
 	{
 		m_list.push_back(t);
+		++m_list_size;
 	}
 	
 	T pop_front()
 	{
-		if(m_list.size() == 0)
+		if(m_list.empty())
 			throw ItemNotFoundException("Queue: queue is empty");
 
-		typename core::list<T>::Iterator begin = m_list.begin();
+		typename std::list<T>::iterator begin = m_list.begin();
 		T t = *begin;
 		m_list.erase(begin);
+		--m_list_size;
 		return t;
 	}
 	T pop_back()
 	{
-		if(m_list.size() == 0)
+		if(m_list.empty())
 			throw ItemNotFoundException("Queue: queue is empty");
 
-		typename core::list<T>::Iterator last = m_list.getLast();
+		typename std::list<T>::iterator last = m_list.back();
 		T t = *last;
 		m_list.erase(last);
+		--m_list_size;
 		return t;
 	}
 
 	u32 size()
 	{
-		return m_list.size();
+		return m_list_size;
+	}
+
+	bool empty()
+	{
+		return m_list.empty();
 	}
 
 protected:
-	core::list<T> m_list;
+	std::list<T> m_list;
+	u32 m_list_size;
 };
 
 /*
@@ -234,10 +248,10 @@ class MutexedQueue
 	{
 		m_mutex.Init();
 	}
-	u32 size()
+	bool empty()
 	{
 		JMutexAutoLock lock(m_mutex);
-		return m_list.size();
+		return m_list.empty();
 	}
 	void push_back(T t)
 	{
@@ -253,9 +267,9 @@ class MutexedQueue
 			{
 				JMutexAutoLock lock(m_mutex);
 
-				if(m_list.size() > 0)
+				if(!m_list.empty())
 				{
-					typename core::list<T>::Iterator begin = m_list.begin();
+					typename std::list<T>::iterator begin = m_list.begin();
 					T t = *begin;
 					m_list.erase(begin);
 					return t;
@@ -279,9 +293,9 @@ class MutexedQueue
 			{
 				JMutexAutoLock lock(m_mutex);
 
-				if(m_list.size() > 0)
+				if(!m_list.empty())
 				{
-					typename core::list<T>::Iterator last = m_list.getLast();
+					typename std::list<T>::iterator last = m_list.back();
 					T t = *last;
 					m_list.erase(last);
 					return t;
@@ -302,14 +316,14 @@ class MutexedQueue
 		return m_mutex;
 	}
 
-	core::list<T> & getList()
+	std::list<T> & getList()
 	{
 		return m_list;
 	}
 
 protected:
 	JMutex m_mutex;
-	core::list<T> m_list;
+	std::list<T> m_list;
 };
 
 #endif
diff --git a/src/util/numeric.cpp b/src/util/numeric.cpp
index a79454628af5f989cf816a2fbc858d6bbd825591..ed83df7d7f768be0f4635820d8d0985cc409f4bd 100644
--- a/src/util/numeric.cpp
+++ b/src/util/numeric.cpp
@@ -24,7 +24,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include <iostream>
 
 // Calculate the borders of a "d-radius" cube
-void getFacePositions(core::list<v3s16> &list, u16 d)
+void getFacePositions(std::list<v3s16> &list, u16 d)
 {
 	if(d == 0)
 	{
diff --git a/src/util/numeric.h b/src/util/numeric.h
index 450a98e40b0335368a0900e95be60e2058b0d61c..e66af237648c8f5ac142a580c57a177e9483aae7 100644
--- a/src/util/numeric.h
+++ b/src/util/numeric.h
@@ -25,9 +25,10 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include "../irr_v3d.h"
 #include "../irr_aabb3d.h"
 #include <irrList.h>
+#include <list>
 
 // Calculate the borders of a "d-radius" cube
-void getFacePositions(core::list<v3s16> &list, u16 d);
+void getFacePositions(std::list<v3s16> &list, u16 d);
 
 class IndentationRaiser
 {
diff --git a/src/util/string.h b/src/util/string.h
index 2f0264bd45db471e372ff507fed5d684091f1a6a..6c48adeb3d70b0368f4f62b5a7a13d4ebcd03081 100644
--- a/src/util/string.h
+++ b/src/util/string.h
@@ -286,6 +286,22 @@ inline std::string wrap_rows(const std::string &from, u32 rowlen)
 	return to;
 }
 
+/*
+	Removes all \\ from a string that had been escaped (FormSpec strings)
+*/
+inline std::string unescape_string(std::string &s)
+{
+	std::string res;
+	
+	for (size_t i = 0; i <= s.length(); i++) {
+		if (s[i] == '\\')
+			i++;
+		res += s[i];
+	}
+	
+	return res;
+}
+
 std::string translatePassword(std::string playername, std::wstring password);
 size_t curl_write_data(char *ptr, size_t size, size_t nmemb, void *userdata);
 u32 readFlagString(std::string str, FlagDesc *flagdesc);
diff --git a/src/util/thread.h b/src/util/thread.h
index 949bb42048dd548552347552ff601a50178880c7..6b2cf5b6c1318d5422cfdd416ac06ecc47cdc209 100644
--- a/src/util/thread.h
+++ b/src/util/thread.h
@@ -120,7 +120,7 @@ class GetResult
 public:
 	Key key;
 	T item;
-	core::list<CallerInfo<Caller, CallerData> > callers;
+	std::list<CallerInfo<Caller, CallerData> > callers;
 };
 
 template<typename Key, typename T, typename Caller, typename CallerData>
@@ -152,16 +152,16 @@ class GetRequest
 	
 	Key key;
 	ResultQueue<Key, T, Caller, CallerData> *dest;
-	core::list<CallerInfo<Caller, CallerData> > callers;
+	std::list<CallerInfo<Caller, CallerData> > callers;
 };
 
 template<typename Key, typename T, typename Caller, typename CallerData>
 class RequestQueue
 {
 public:
-	u32 size()
+	bool empty()
 	{
-		return m_queue.size();
+		return m_queue.empty();
 	}
 
 	void add(Key key, Caller caller, CallerData callerdata,
@@ -172,17 +172,17 @@ class RequestQueue
 		/*
 			If the caller is already on the list, only update CallerData
 		*/
-		for(typename core::list< GetRequest<Key, T, Caller, CallerData> >::Iterator
+		for(typename std::list< GetRequest<Key, T, Caller, CallerData> >::iterator
 				i = m_queue.getList().begin();
-				i != m_queue.getList().end(); i++)
+				i != m_queue.getList().end(); ++i)
 		{
 			GetRequest<Key, T, Caller, CallerData> &request = *i;
 
 			if(request.key == key)
 			{
-				for(typename core::list< CallerInfo<Caller, CallerData> >::Iterator
+				for(typename std::list< CallerInfo<Caller, CallerData> >::iterator
 						i = request.callers.begin();
-						i != request.callers.end(); i++)
+						i != request.callers.end(); ++i)
 				{
 					CallerInfo<Caller, CallerData> &ca = *i;
 					if(ca.caller == caller)
diff --git a/src/util/timetaker.cpp b/src/util/timetaker.cpp
index 910fea822f89fe5ffd8338038d695e392f16f739..720a9e1a976e4a78ff1919caa08a74156f2ed776 100644
--- a/src/util/timetaker.cpp
+++ b/src/util/timetaker.cpp
@@ -23,19 +23,20 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include "../log.h"
 #include <ostream>
 
-TimeTaker::TimeTaker(const char *name, u32 *result)
+TimeTaker::TimeTaker(const char *name, u32 *result, TimePrecision prec)
 {
 	m_name = name;
 	m_result = result;
 	m_running = true;
-	m_time1 = getTimeMs();
+	m_precision = prec;
+	m_time1 = getTime(prec);
 }
 
 u32 TimeTaker::stop(bool quiet)
 {
 	if(m_running)
 	{
-		u32 time2 = getTimeMs();
+		u32 time2 = getTime(m_precision);
 		u32 dtime = time2 - m_time1;
 		if(m_result != NULL)
 		{
@@ -52,9 +53,9 @@ u32 TimeTaker::stop(bool quiet)
 	return 0;
 }
 
-u32 TimeTaker::getTime()
+u32 TimeTaker::getTimerTime()
 {
-	u32 time2 = getTimeMs();
+	u32 time2 = getTime(m_precision);
 	u32 dtime = time2 - m_time1;
 	return dtime;
 }
diff --git a/src/util/timetaker.h b/src/util/timetaker.h
index 0b9d9ca04d2347d33bd4772147b93ad8a7e56821..5512c205fe611ab72263966bc1e3f5efb2c52fcf 100644
--- a/src/util/timetaker.h
+++ b/src/util/timetaker.h
@@ -21,6 +21,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #define UTIL_TIMETAKER_HEADER
 
 #include "../irrlichttypes.h"
+#include "../gettime.h"
 
 /*
 	TimeTaker
@@ -29,7 +30,8 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 class TimeTaker
 {
 public:
-	TimeTaker(const char *name, u32 *result=NULL);
+	TimeTaker(const char *name, u32 *result=NULL,
+		TimePrecision=PRECISION_MILLI);
 
 	~TimeTaker()
 	{
@@ -38,12 +40,13 @@ class TimeTaker
 
 	u32 stop(bool quiet=false);
 
-	u32 getTime();
+	u32 getTimerTime();
 
 private:
 	const char *m_name;
 	u32 m_time1;
 	bool m_running;
+	TimePrecision m_precision;
 	u32 *m_result;
 };
 
diff --git a/src/voxel.cpp b/src/voxel.cpp
index c55f3f5392dd0716ba96096412932aa91e2bf273..f859a1f0382cf6025a41c0b60dd48a7c83ad3eb2 100644
--- a/src/voxel.cpp
+++ b/src/voxel.cpp
@@ -302,7 +302,7 @@ void VoxelManipulator::clearFlag(u8 flags)
 }
 
 void VoxelManipulator::unspreadLight(enum LightBank bank, v3s16 p, u8 oldlight,
-		core::map<v3s16, bool> & light_sources, INodeDefManager *nodemgr)
+		std::set<v3s16> & light_sources, INodeDefManager *nodemgr)
 {
 	v3s16 dirs[6] = {
 		v3s16(0,0,1), // back
@@ -360,7 +360,7 @@ void VoxelManipulator::unspreadLight(enum LightBank bank, v3s16 p, u8 oldlight,
 			}
 		}
 		else{
-			light_sources.insert(n2pos, true);
+			light_sources.insert(n2pos);
 		}
 	}
 }
@@ -384,24 +384,16 @@ void VoxelManipulator::unspreadLight(enum LightBank bank, v3s16 p, u8 oldlight,
 	values of from_nodes are lighting values.
 */
 void VoxelManipulator::unspreadLight(enum LightBank bank,
-		core::map<v3s16, u8> & from_nodes,
-		core::map<v3s16, bool> & light_sources, INodeDefManager *nodemgr)
+		std::map<v3s16, u8> & from_nodes,
+		std::set<v3s16> & light_sources, INodeDefManager *nodemgr)
 {
 	if(from_nodes.size() == 0)
 		return;
 	
-	core::map<v3s16, u8>::Iterator j;
-	j = from_nodes.getIterator();
-
-	for(; j.atEnd() == false; j++)
+	for(std::map<v3s16, u8>::iterator j = from_nodes.begin();
+		j != from_nodes.end(); ++j)
 	{
-		v3s16 pos = j.getNode()->getKey();
-		
-		//MapNode &n = m_data[m_area.index(pos)];
-		
-		u8 oldlight = j.getNode()->getValue();
-
-		unspreadLight(bank, pos, oldlight, light_sources, nodemgr);
+		unspreadLight(bank, j->first, j->second, light_sources, nodemgr);
 	}
 }
 #endif
@@ -609,7 +601,7 @@ void VoxelManipulator::spreadLight(enum LightBank bank,
 	goes on recursively.
 */
 void VoxelManipulator::spreadLight(enum LightBank bank,
-		core::map<v3s16, bool> & from_nodes, INodeDefManager *nodemgr)
+		std::set<v3s16> & from_nodes, INodeDefManager *nodemgr)
 {
 	const v3s16 dirs[6] = {
 		v3s16(0,0,1), // back
@@ -623,13 +615,12 @@ void VoxelManipulator::spreadLight(enum LightBank bank,
 	if(from_nodes.size() == 0)
 		return;
 	
-	core::map<v3s16, bool> lighted_nodes;
-	core::map<v3s16, bool>::Iterator j;
-	j = from_nodes.getIterator();
+	std::set<v3s16> lighted_nodes;
 
-	for(; j.atEnd() == false; j++)
+	for(std::set<v3s16>::iterator j = from_nodes.begin();
+		j != from_nodes.end(); ++j)
 	{
-		v3s16 pos = j.getNode()->getKey();
+		v3s16 pos = *j;
 
 		emerge(VoxelArea(pos - v3s16(1,1,1), pos + v3s16(1,1,1)));
 
@@ -666,7 +657,7 @@ void VoxelManipulator::spreadLight(enum LightBank bank,
 				*/
 				if(light2 > undiminish_light(oldlight))
 				{
-					lighted_nodes.insert(n2pos, true);
+					lighted_nodes.insert(n2pos);
 				}
 				/*
 					If the neighbor is dimmer than how much light this node
@@ -677,7 +668,7 @@ void VoxelManipulator::spreadLight(enum LightBank bank,
 					if(nodemgr->get(n2).light_propagates)
 					{
 						n2.setLight(bank, newlight, nodemgr);
-						lighted_nodes.insert(n2pos, true);
+						lighted_nodes.insert(n2pos);
 					}
 				}
 			}
diff --git a/src/voxel.h b/src/voxel.h
index b48943624f2a7325ced98a64892c0d9e8a8d0d8e..bed35b57e87a43acc9818995a4a24b91a07c37d9 100644
--- a/src/voxel.h
+++ b/src/voxel.h
@@ -26,6 +26,8 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include <iostream>
 #include "debug.h"
 #include "mapnode.h"
+#include <set>
+#include <list>
 
 class INodeDefManager;
 
@@ -186,7 +188,7 @@ class VoxelArea
 
 		a: area inside *this
 	*/
-	void diff(const VoxelArea &a, core::list<VoxelArea> &result)
+	void diff(const VoxelArea &a, std::list<VoxelArea> &result)
 	{
 		/*
 			This can result in a maximum of 6 areas
@@ -519,14 +521,14 @@ class VoxelManipulator /*: public NodeContainer*/
 	// TODO: Move to voxelalgorithms.h
 
 	void unspreadLight(enum LightBank bank, v3s16 p, u8 oldlight,
-			core::map<v3s16, bool> & light_sources, INodeDefManager *nodemgr);
+			std::set<v3s16> & light_sources, INodeDefManager *nodemgr);
 	void unspreadLight(enum LightBank bank,
-			core::map<v3s16, u8> & from_nodes,
-			core::map<v3s16, bool> & light_sources, INodeDefManager *nodemgr);
+			std::map<v3s16, u8> & from_nodes,
+			std::set<v3s16> & light_sources, INodeDefManager *nodemgr);
 
 	void spreadLight(enum LightBank bank, v3s16 p, INodeDefManager *nodemgr);
 	void spreadLight(enum LightBank bank,
-			core::map<v3s16, bool> & from_nodes, INodeDefManager *nodemgr);
+			std::set<v3s16> & from_nodes, INodeDefManager *nodemgr);
 
 	/*
 		Virtual functions
diff --git a/src/voxelalgorithms.cpp b/src/voxelalgorithms.cpp
index bd8f816b854134d1cdd0682071770fdcdf1a750a..14638a82776cc5b5264c0daadce1c0b24447105e 100644
--- a/src/voxelalgorithms.cpp
+++ b/src/voxelalgorithms.cpp
@@ -39,8 +39,8 @@ void setLight(VoxelManipulator &v, VoxelArea a, u8 light,
 
 void clearLightAndCollectSources(VoxelManipulator &v, VoxelArea a,
 		enum LightBank bank, INodeDefManager *ndef,
-		core::map<v3s16, bool> & light_sources,
-		core::map<v3s16, u8> & unlight_from)
+		std::set<v3s16> & light_sources,
+		std::map<v3s16, u8> & unlight_from)
 {
 	// The full area we shall touch
 	VoxelArea required_a = a;
@@ -60,7 +60,7 @@ void clearLightAndCollectSources(VoxelManipulator &v, VoxelArea a,
 		// If node sources light, add to list
 		u8 source = ndef->get(n).light_source;
 		if(source != 0)
-			light_sources[p] = true;
+			light_sources.insert(p);
 
 		// Collect borders for unlighting
 		if((x==a.MinEdge.X || x == a.MaxEdge.X
@@ -68,14 +68,14 @@ void clearLightAndCollectSources(VoxelManipulator &v, VoxelArea a,
 		|| z==a.MinEdge.Z || z == a.MaxEdge.Z)
 		&& oldlight != 0)
 		{
-			unlight_from.insert(p, oldlight);
+			unlight_from[p] = oldlight;
 		}
 	}
 }
 
 SunlightPropagateResult propagateSunlight(VoxelManipulator &v, VoxelArea a,
 		bool inexistent_top_provides_sunlight,
-		core::map<v3s16, bool> & light_sources,
+		std::set<v3s16> & light_sources,
 		INodeDefManager *ndef)
 {
 	// Return values
@@ -127,7 +127,7 @@ SunlightPropagateResult propagateSunlight(VoxelManipulator &v, VoxelArea a,
 				n.setLight(LIGHTBANK_DAY, incoming_light, ndef);
 
 			if(diminish_light(incoming_light) != 0)
-				light_sources.insert(p, true);
+				light_sources.insert(p);
 		}
 
 		// Check validity of sunlight at top of block below if it
diff --git a/src/voxelalgorithms.h b/src/voxelalgorithms.h
index 2a5fc394e239a41cdc8e9f715be983c0a232a646..2eba6a17646e2a965e4b7b6991c9061aea14d30c 100644
--- a/src/voxelalgorithms.h
+++ b/src/voxelalgorithms.h
@@ -22,6 +22,8 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 
 #include "voxel.h"
 #include "mapnode.h"
+#include <set>
+#include <map>
 
 namespace voxalgo
 {
@@ -33,8 +35,8 @@ void setLight(VoxelManipulator &v, VoxelArea a, u8 light,
 
 void clearLightAndCollectSources(VoxelManipulator &v, VoxelArea a,
 		enum LightBank bank, INodeDefManager *ndef,
-		core::map<v3s16, bool> & light_sources,
-		core::map<v3s16, u8> & unlight_from);
+		std::set<v3s16> & light_sources,
+		std::map<v3s16, u8> & unlight_from);
 
 struct SunlightPropagateResult
 {
@@ -47,7 +49,7 @@ struct SunlightPropagateResult
 
 SunlightPropagateResult propagateSunlight(VoxelManipulator &v, VoxelArea a,
 		bool inexistent_top_provides_sunlight,
-		core::map<v3s16, bool> & light_sources,
+		std::set<v3s16> & light_sources,
 		INodeDefManager *ndef);
 
 } // namespace voxalgo
diff --git a/textures/base/pack/logo.png b/textures/base/pack/logo.png
new file mode 100644
index 0000000000000000000000000000000000000000..f341e88925a2b9fd935d60627cc2ab6a009922d1
Binary files /dev/null and b/textures/base/pack/logo.png differ
diff --git a/textures/base/pack/menufooter.png b/textures/base/pack/menufooter.png
new file mode 100644
index 0000000000000000000000000000000000000000..fd431846eb4cfa923dcf938ec9c28fca216e9467
Binary files /dev/null and b/textures/base/pack/menufooter.png differ
diff --git a/textures/base/pack/menuheader.png b/textures/base/pack/menuheader.png
new file mode 100644
index 0000000000000000000000000000000000000000..66fd6def577f6c8862469c37f5382f91d184001d
Binary files /dev/null and b/textures/base/pack/menuheader.png differ
diff --git a/textures/base/pack/menulogo.png b/textures/base/pack/menulogo.png
deleted file mode 100644
index 76595c48dc78e626365261633ec9f13b5e5eeaac..0000000000000000000000000000000000000000
Binary files a/textures/base/pack/menulogo.png and /dev/null differ
diff --git a/util/buildbot/buildwin32.sh b/util/buildbot/buildwin32.sh
index fc42db8a79c9e78b0abdb382d23d32a6193031d1..0c0d7cf214602792375e18454bbf35cd462d21e4 100755
--- a/util/buildbot/buildwin32.sh
+++ b/util/buildbot/buildwin32.sh
@@ -53,6 +53,9 @@ cd $builddir
 wget http://github.com/minetest/minetest/zipball/master \
 	-c -O $packagedir/minetest.zip --tries=3 || (echo "Please download http://github.com/minetest/minetest/zipball/master manually and save it as $packagedir/minetest.zip"; read -s)
 [ -e $packagedir/minetest.zip ] || (echo "minetest.zip not found"; exit 1)
+wget http://github.com/minetest/common/zipball/master \
+	-c -O $packagedir/common.zip --tries=3 || (echo "Please download http://github.com/minetest/common/zipball/master manually and save it as $packagedir/common.zip"; read -s)
+[ -e $packagedir/common.zip ] || (echo "common.zip not found"; exit 1)
 wget http://github.com/minetest/minetest_game/zipball/master \
 	-c -O $packagedir/minetest_game.zip --tries=3 || (echo "Please download http://github.com/minetest/minetest_game/zipball/master manually and save it as $packagedir/minetest_game.zip"; read -s)
 [ -e $packagedir/minetest_game.zip ] || (echo "minetest_game.zip not found"; exit 1)
@@ -66,6 +69,7 @@ wget http://github.com/minetest/minetest_game/zipball/master \
 minetestdirname=`unzip -l $packagedir/minetest.zip | head -n 7 | tail -n 1 | sed -e 's/^[^m]*//' -e 's/\/.*$//'`
 minetestdir=$builddir/$minetestdirname || exit 1
 git_hash=`echo $minetestdirname | sed -e 's/minetest-minetest-//'`
+commondirname=`unzip -l $packagedir/common.zip | head -n 7 | tail -n 1 | sed -e 's/^[^m]*//' -e 's/\/.*$//'`
 minetest_gamedirname=`unzip -l $packagedir/minetest_game.zip | head -n 7 | tail -n 1 | sed -e 's/^[^m]*//' -e 's/\/.*$//'`
 
 # Extract stuff
@@ -86,6 +90,13 @@ unzip -o $packagedir/minetest.zip || exit 1
 rm -rf $builddir/minetest
 ln -s $minetestdir $builddir/minetest
 
+# Extract common
+cd $minetestdir/games || exit 1
+rm -rf common || exit 1
+unzip -o $packagedir/common.zip || exit 1
+commondir=$minetestdir/games/$commondirname || exit 1
+mv $commondir $minetestdir/games/common || exit 1
+
 # Extract minetest_game
 cd $minetestdir/games || exit 1
 rm -rf minetest_game || exit 1
diff --git a/util/master/list.js b/util/master/list.js
index dcc30e091501e1d5271e90d76beeee45a156edb8..990bb6ded9f11f33f5ad162724b34bcb7a540e69 100644
--- a/util/master/list.js
+++ b/util/master/list.js
@@ -2,11 +2,12 @@ function e(s) {
     if (typeof s === "undefined") s = '';
     return s.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/"/g, '&quot;'); //mc"
 }
-function human_time(t) {
+
+function human_time(t, abs) {
     var n = 's';
     if (!t || t < 0) t = 0;
     var f = 0;
-    var s = parseInt((new Date().getTime() / 1000 - (t || 0)));
+    var s = parseInt(abs ? (t || 0) : (new Date().getTime() / 1000 - (t || 0)));
     if (!s || s <= 0) s = 0;
     if (s == 0) return 'now';
     if (s >= 60) {
@@ -35,38 +36,36 @@ function human_time(t) {
     }
     return ((f ? parseFloat(s).toFixed(1) : parseInt(s)) + n);
 }
+
 function success(r) {
     if (!r || !r.list) return;
-    var h = '<table><tr><th>ip:port</th><th>clients, max</th><th>version</th><th>name</th><th>desc</th><th>flags</th><th>updated/started</th><th>ping</th></tr>';
+    var h = '<table class="mts_table"><tr class="mts_head"><th>ip[:port]</th><th>clients/max</th><th>version gameid</th><th>name</th><th>desc</th><th>flags</th><th>uptime</th><th>ping</th></tr>';
     for (var i = 0; i < r.list.length; ++i) {
         var s = r.list[i];
         if (!s) continue;
-        h += '<tr>';
-        h += '<td>' + e(s.address) + ':' + e(s.port) + '</td>';
-        h += '<td>' + e(s.clients) + (s.clients_max ? '/' + e(s.clients_max) : '') + (s.clients_top ? ', ' + s.clients_top : '') + '</td>';
-        h += '<td>' + e(s.version) + '</td>';
-        h += '<td>';
+        h += '<tr class="mts_row">';
+        h += '<td class="mts_address">' + e(s.address) + (s.port != 30000 ? (':' + e(s.port)) : '') + '</td>';
+        h += '<td class="mts_clients">' + e(s.clients) + (s.clients_max ? '/' + e(s.clients_max) : '') + (s.clients_top ? ', ' + s.clients_top : '') + '</td>';
+        h += '<td class="mts_version">' + e(s.version) + ' ' + e(s.gameid) + '</td>';
+        h += '<td class="mts_url">';
         if (s.url) h += '<a href="' + e(s.url) + '">';
         h += e(s.name || s.url);
         if (s.url) h += '</a>';
         h += '</td>';
-        h += '<td>' + e(s.description) + '</td>';
-        h += '<td>' + e(s.password ? 'Pwd ' : '') + (s.creative ? 'Cre ' : '') + (s.damage ? 'Dmg ' : '') + (s.pvp ? 'Pvp ' : '') + (s.dedicated ? 'Ded ' : '') + '</td>';
-        if (!s.time || s.time < 0) s.time = 0;
+        h += '<td class="mts_description">' + e(s.description) + '</td>';
+        h += '<td class="mts_flags">' + e(s.password ? 'Pwd ' : '') + (s.creative ? 'Cre ' : '') + (s.damage ? 'Dmg ' : '') + (s.pvp ? 'Pvp ' : '') + (s.dedicated ? 'Ded ' : '') + '</td>';
         if (!s.start || s.start < 0) s.start = 0;
-        h += '<td>' + human_time(s.time) + (s.start ? '/' + human_time(s.start) : '') + '</td>';
-        h += '<td>' + (s.ping ? parseFloat(s.ping).toFixed(3)*1000 : '') + '</td>';
+        h += '<td class="mts_time">' + (s.uptime ? human_time(s.uptime, 1) : s.start ? human_time(s.start) : '') + '</td>';
+        h += '<td class="mts_ping">' + (s.ping ? parseFloat(s.ping).toFixed(3) * 1000 : '') + '</td>';
         h += '</tr>';
     }
     h += '</table>'
     jQuery('#table').html(h);
 }
+var master_root;
+
 function get() {
-    jQuery.ajax({
-        url: 'list',
-        dataType: 'json',
-        success: success
-    });
+    jQuery.getJSON((master_root || '') + 'list', success);
     setTimeout(get, 60000);
 }
-get();
\ No newline at end of file
+get();
diff --git a/util/master/master.cgi b/util/master/master.cgi
index b918876bd29a08889c367d84669c10ccd7865a5e..0e456ed0cace4ee77216b27ffceb0cdf8376294a 100755
--- a/util/master/master.cgi
+++ b/util/master/master.cgi
@@ -18,6 +18,7 @@ nginx:
 
         location / {
             index  index.html;
+            add_header Access-Control-Allow-Origin *;
         }
         location /announce {
             fastcgi_pass   unix:/var/run/fcgiwrap/fcgiwrap.sock;
@@ -35,6 +36,10 @@ apache .htaccess:
   Allow from all
  </FilesMatch>
  Deny from all
+ <ifModule mod_headers.c>
+     Header set Access-Control-Allow-Origin: *
+ </ifModule>
+
 
 
 =cut