Merge remote-tracking branch 'origin/upstream'
authorNils Dagsson Moskopp <nils@dieweltistgarnichtso.net>
Sat, 30 Jul 2011 16:53:54 +0000 (18:53 +0200)
committerNils Dagsson Moskopp <nils@dieweltistgarnichtso.net>
Sat, 30 Jul 2011 16:55:43 +0000 (18:55 +0200)
1  2 
src/main.cpp
src/map.cpp
src/mapblock.cpp
src/mapnode.h

diff --cc src/main.cpp
index bcca60d95d58ba16e343f035bef17e7fd80e86f1,09c299004078bdf28045c320b62dfe249d4f70c1..da17b84bc593b44cb217d927a2c39142635c6cd6
- /*\r
- Minetest-c55\r
- Copyright (C) 2010-2011 celeron55, Perttu Ahola <celeron55@gmail.com>\r
\r
- This program is free software; you can redistribute it and/or modify\r
- it under the terms of the GNU General Public License as published by\r
- the Free Software Foundation; either version 2 of the License, or\r
- (at your option) any later version.\r
\r
- This program is distributed in the hope that it will be useful,\r
- but WITHOUT ANY WARRANTY; without even the implied warranty of\r
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
- GNU General Public License for more details.\r
\r
- You should have received a copy of the GNU General Public License along\r
- with this program; if not, write to the Free Software Foundation, Inc.,\r
- 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\r
- */\r
\r
- /*\r
- =============================== NOTES ==============================\r
- NOTE: Things starting with TODO are sometimes only suggestions.\r
\r
- NOTE: iostream.imbue(std::locale("C")) is very slow\r
- NOTE: Global locale is now set at initialization\r
\r
- NOTE: If VBO (EHM_STATIC) is used, remember to explicitly free the\r
-       hardware buffer (it is not freed automatically)\r
\r
- NOTE: A random to-do list saved here as documentation:\r
- A list of "active blocks" in which stuff happens. (+=done)\r
-       + Add a never-resetted game timer to the server\r
-       + Add a timestamp value to blocks\r
-       + The simple rule: All blocks near some player are "active"\r
-       - Do stuff in real time in active blocks\r
-               + Handle objects\r
-               - Grow grass, delete leaves without a tree\r
-               - Spawn some mobs based on some rules\r
-               - Transform cobble to mossy cobble near water\r
-               - Run a custom script\r
-               - ...And all kinds of other dynamic stuff\r
-       + Keep track of when a block becomes active and becomes inactive\r
-       + When a block goes inactive:\r
-               + Store objects statically to block\r
-               + Store timer value as the timestamp\r
-       + When a block goes active:\r
-               + Create active objects out of static objects\r
-               - Simulate the results of what would have happened if it would have\r
-                 been active for all the time\r
-                       - Grow a lot of grass and so on\r
-       + Initially it is fine to send information about every active object\r
-         to every player. Eventually it should be modified to only send info\r
-         about the nearest ones.\r
-               + This was left to be done by the old system and it sends only the\r
-                 nearest ones.\r
\r
- Old, wild and random suggestions that probably won't be done:\r
- -------------------------------------------------------------\r
\r
- SUGG: If player is on ground, mainly fetch ground-level blocks\r
\r
- SUGG: Expose Connection's seqnums and ACKs to server and client.\r
-       - This enables saving many packets and making a faster connection\r
-         - This also enables server to check if client has received the\r
-           most recent block sent, for example.\r
- SUGG: Add a sane bandwidth throttling system to Connection\r
\r
- SUGG: More fine-grained control of client's dumping of blocks from\r
-       memory\r
-         - ...What does this mean in the first place?\r
\r
- SUGG: A map editing mode (similar to dedicated server mode)\r
\r
- SUGG: Transfer more blocks in a single packet\r
- SUGG: A blockdata combiner class, to which blocks are added and at\r
-       destruction it sends all the stuff in as few packets as possible.\r
- SUGG: Make a PACKET_COMBINED which contains many subpackets. Utilize\r
-       it by sending more stuff in a single packet.\r
-         - Add a packet queue to RemoteClient, from which packets will be\r
-           combined with object data packets\r
-               - This is not exactly trivial: the object data packets are\r
-                 sometimes very big by themselves\r
-         - This might not give much network performance gain though.\r
\r
- SUGG: Precalculate lighting translation table at runtime (at startup)\r
-       - This is not doable because it is currently hand-made and not\r
-           based on some mathematical function.\r
-               - Note: This has been changing lately\r
\r
- SUGG: A version number to blocks, which increments when the block is\r
-       modified (node add/remove, water update, lighting update)\r
-         - This can then be used to make sure the most recent version of\r
-           a block has been sent to client, for example\r
\r
- SUGG: Make the amount of blocks sending to client and the total\r
-         amount of blocks dynamically limited. Transferring blocks is the\r
-         main network eater of this system, so it is the one that has\r
-         to be throttled so that RTTs stay low.\r
\r
- SUGG: Meshes of blocks could be split into 6 meshes facing into\r
-       different directions and then only those drawn that need to be\r
\r
- SUGG: Background music based on cellular automata?\r
-       http://www.earslap.com/projectslab/otomata\r
\r
- SUGG: Simple light color information to air\r
\r
- SUGG: Server-side objects could be moved based on nodes to enable very\r
-       lightweight operation and simple AI\r
-       - Not practical; client would still need to show smooth movement.\r
\r
- SUGG: Make a system for pregenerating quick information for mapblocks, so\r
-         that the client can show them as cubes before they are actually sent\r
-         or even generated.\r
\r
- SUGG: Erosion simulation at map generation time\r
-     - This might be plausible if larger areas of map were pregenerated\r
-         without lighting (which is slow)\r
-       - Simulate water flows, which would carve out dirt fast and\r
-         then turn stone into gravel and sand and relocate it.\r
-       - How about relocating minerals, too? Coal and gold in\r
-         downstream sand and gravel would be kind of cool\r
-         - This would need a better way of handling minerals, mainly\r
-               to have mineral content as a separate field. the first\r
-               parameter field is free for this.\r
-       - Simulate rock falling from cliffs when water has removed\r
-         enough solid rock from the bottom\r
\r
- SUGG: For non-mapgen FarMesh: Add a per-sector database to store surface\r
-       stuff as simple flags/values\r
-       - Light?\r
-         - A building?\r
-         And at some point make the server send this data to the client too,\r
-         instead of referring to the noise functions\r
-         - Ground height\r
-         - Surface ground type\r
-         - Trees?\r
\r
- Gaming ideas:\r
- -------------\r
\r
- - Aim for something like controlling a single dwarf in Dwarf Fortress\r
- - The player could go faster by a crafting a boat, or riding an animal\r
- - Random NPC traders. what else?\r
\r
- Game content:\r
- -------------\r
\r
- - When furnace is destroyed, move items to player's inventory\r
- - Add lots of stuff\r
- - Glass blocks\r
- - Growing grass, decaying leaves\r
-       - This can be done in the active blocks I guess.\r
-       - Lots of stuff can be done in the active blocks.\r
-       - Uh, is there an active block list somewhere? I think not. Add it.\r
- - Breaking weak structures\r
-       - This can probably be accomplished in the same way as grass\r
- - Player health points\r
-       - When player dies, throw items on map (needs better item-on-map\r
-         implementation)\r
- - Cobble to get mossy if near water\r
- - More slots in furnace source list, so that multiple ingredients\r
-   are possible.\r
- - Keys to chests?\r
\r
- - The Treasure Guard; a big monster with a hammer\r
-       - The hammer does great damage, shakes the ground and removes a block\r
-       - You can drop on top of it, and have some time to attack there\r
-         before he shakes you off\r
\r
- - Maybe the difficulty could come from monsters getting tougher in\r
-   far-away places, and the player starting to need something from\r
-   there when time goes by.\r
-   - The player would have some of that stuff at the beginning, and\r
-     would need new supplies of it when it runs out\r
\r
- - A bomb\r
- - A spread-items-on-map routine for the bomb, and for dying players\r
\r
- - Fighting:\r
-   - Proper sword swing simulation\r
-   - Player should get damage from colliding to a wall at high speed\r
\r
- Documentation:\r
- --------------\r
\r
- Build system / running:\r
- -----------------------\r
\r
- Networking and serialization:\r
- -----------------------------\r
\r
- SUGG: Fix address to be ipv6 compatible\r
\r
- User Interface:\r
- ---------------\r
\r
- Graphics:\r
- ---------\r
\r
- SUGG: Combine MapBlock's face caches to so big pieces that VBO\r
-       can be used\r
-       - That is >500 vertices\r
-         - This is not easy; all the MapBlocks close to the player would\r
-           still need to be drawn separately and combining the blocks\r
-               would have to happen in a background thread\r
\r
- SUGG: Make fetching sector's blocks more efficient when rendering\r
-       sectors that have very large amounts of blocks (on client)\r
-         - Is this necessary at all?\r
\r
- SUGG: Draw cubes in inventory directly with 3D drawing commands, so that\r
-       animating them is easier.\r
\r
- SUGG: Option for enabling proper alpha channel for textures\r
\r
- TODO: Flowing water animation\r
\r
- TODO: A setting for enabling bilinear filtering for textures\r
\r
- TODO: Better control of draw_control.wanted_max_blocks\r
\r
- TODO: Further investigate the use of GPU lighting in addition to the\r
-       current one\r
\r
- TODO: Artificial (night) light could be more yellow colored than sunlight.\r
-       - This is technically doable.\r
-         - Also the actual colors of the textures could be made less colorful\r
-           in the dark but it's a bit more difficult.\r
\r
- SUGG: Somehow make the night less colorful\r
\r
- TODO: Occlusion culling\r
-       - At the same time, move some of the renderMap() block choosing code\r
-         to the same place as where the new culling happens.\r
-       - Shoot some rays per frame and when ready, make a new list of\r
-           blocks for usage of renderMap and give it a new pointer to it.\r
\r
- Configuration:\r
- --------------\r
\r
- Client:\r
- -------\r
\r
- TODO: Untie client network operations from framerate\r
-       - Needs some input queues or something\r
-         - This won't give much performance boost because calculating block\r
-           meshes takes so long\r
\r
- SUGG: Make morning and evening transition more smooth and maybe shorter\r
\r
- TODO: Don't update all meshes always on single node changes, but\r
-       check which ones should be updated\r
-         - implement Map::updateNodeMeshes() and the usage of it\r
-         - It will give almost always a 4x boost in mesh update performance.\r
\r
- - A weapon engine\r
\r
- - Tool/weapon visualization\r
\r
- FIXME: When disconnected to the menu, memory is not freed properly\r
\r
- TODO: Investigate how much the mesh generator thread gets used when\r
-       transferring map data\r
\r
- Server:\r
- -------\r
\r
- SUGG: Make an option to the server to disable building and digging near\r
-       the starting position\r
\r
- FIXME: Server sometimes goes into some infinite PeerNotFoundException loop\r
\r
- * Fix the problem with the server constantly saving one or a few\r
-   blocks? List the first saved block, maybe it explains.\r
-   - It is probably caused by oscillating water\r
-   - TODO: Investigate if this still happens (this is a very old one)\r
- * Make a small history check to transformLiquids to detect and log\r
-   continuous oscillations, in such detail that they can be fixed.\r
\r
- FIXME: The new optimized map sending doesn't sometimes send enough blocks\r
-        from big caves and such\r
- FIXME: Block send distance configuration does not take effect for some reason\r
\r
- Environment:\r
- ------------\r
\r
- TODO: Add proper hooks to when adding and removing active blocks\r
\r
- TODO: Finish the ActiveBlockModifier stuff and use it for something\r
\r
- Objects:\r
- --------\r
\r
- TODO: Get rid of MapBlockObjects and use only ActiveObjects\r
-       - Skipping the MapBlockObject data is nasty - there is no "total\r
-         length" stored; have to make a SkipMBOs function which contains\r
-         enough of the current code to skip them properly.\r
\r
- SUGG: MovingObject::move and Player::move are basically the same.\r
-       combine them.\r
-       - NOTE: This is a bit tricky because player has the sneaking ability\r
-       - NOTE: Player::move is more up-to-date.\r
-       - NOTE: There is a simple move implementation now in collision.{h,cpp}\r
-       - NOTE: MovingObject will be deleted (MapBlockObject)\r
\r
- TODO: Add a long step function to objects that is called with the time\r
-       difference when block activates\r
\r
- Map:\r
- ----\r
\r
- TODO: Mineral and ground material properties\r
-       - This way mineral ground toughness can be calculated with just\r
-           some formula, as well as tool strengths\r
-         - There are TODOs in appropriate files: material.h, content_mapnode.h\r
\r
- TODO: Flowing water to actually contain flow direction information\r
-       - There is a space for this - it just has to be implemented.\r
\r
- TODO: Consider smoothening cave floors after generating them\r
\r
- Misc. stuff:\r
- ------------\r
- TODO: Make sure server handles removing grass when a block is placed (etc)\r
-       - The client should not do it by itself\r
-         - NOTE: I think nobody does it currently...\r
- TODO: Block cube placement around player's head\r
- TODO: Protocol version field\r
- TODO: Think about using same bits for material for fences and doors, for\r
-         example\r
- TODO: Move mineral to param2, increment map serialization version, add\r
-       conversion\r
\r
- TODO: Restart irrlicht completely when coming back to main menu from game.\r
-       - This gets rid of everything that is stored in irrlicht's caches.\r
\r
- TODO: Merge bahamada's audio stuff (clean patch available)\r
\r
- TODO: Merge key configuration menu (no clean patch available)\r
\r
- Making it more portable:\r
- ------------------------\r
-  \r
- Stuff to do before release:\r
- ---------------------------\r
\r
- Fixes to the current release:\r
- -----------------------------\r
\r
- Stuff to do after release:\r
- ---------------------------\r
\r
- Doing currently:\r
- ----------------\r
\r
- ======================================================================\r
\r
- */\r
\r
- #ifdef NDEBUG\r
-       #ifdef _WIN32\r
-               #pragma message ("Disabling unit tests")\r
-       #else\r
-               #warning "Disabling unit tests"\r
-       #endif\r
-       // Disable unit tests\r
-       #define ENABLE_TESTS 0\r
- #else\r
-       // Enable unit tests\r
-       #define ENABLE_TESTS 1\r
- #endif\r
\r
- #ifdef _MSC_VER\r
-       #pragma comment(lib, "Irrlicht.lib")\r
-       //#pragma comment(lib, "jthread.lib")\r
-       #pragma comment(lib, "zlibwapi.lib")\r
-       #pragma comment(lib, "Shell32.lib")\r
-       // This would get rid of the console window\r
-       //#pragma comment(linker, "/subsystem:windows /ENTRY:mainCRTStartup")\r
- #endif\r
\r
- #include <iostream>\r
- #include <fstream>\r
- #include <locale.h>\r
- #include "main.h"\r
- #include "common_irrlicht.h"\r
- #include "debug.h"\r
- #include "test.h"\r
- #include "server.h"\r
- #include "constants.h"\r
- #include "porting.h"\r
- #include "gettime.h"\r
- #include "guiMessageMenu.h"\r
- #include "filesys.h"\r
- #include "config.h"\r
- #include "guiMainMenu.h"\r
- #include "mineral.h"\r
- #include "materials.h"\r
- #include "game.h"\r
- #include "keycode.h"\r
- #include "tile.h"\r
\r
- #include "gettext.h"\r
\r
- // This makes textures\r
- ITextureSource *g_texturesource = NULL;\r
\r
- /*\r
-       Settings.\r
-       These are loaded from the config file.\r
- */\r
\r
- Settings g_settings;\r
- // This is located in defaultsettings.cpp\r
- extern void set_default_settings();\r
\r
- // Global profiler\r
- Profiler g_profiler;\r
\r
- /*\r
-       Random stuff\r
- */\r
\r
- /*\r
-       GUI Stuff\r
- */\r
\r
- gui::IGUIEnvironment* guienv = NULL;\r
- gui::IGUIStaticText *guiroot = NULL;\r
\r
- MainMenuManager g_menumgr;\r
\r
- bool noMenuActive()\r
- {\r
-       return (g_menumgr.menuCount() == 0);\r
- }\r
\r
- // Passed to menus to allow disconnecting and exiting\r
\r
- MainGameCallback *g_gamecallback = NULL;\r
\r
- /*\r
-       Debug streams\r
- */\r
\r
- // Connection\r
- std::ostream *dout_con_ptr = &dummyout;\r
- std::ostream *derr_con_ptr = &dstream_no_stderr;\r
- //std::ostream *dout_con_ptr = &dstream_no_stderr;\r
- //std::ostream *derr_con_ptr = &dstream_no_stderr;\r
- //std::ostream *dout_con_ptr = &dstream;\r
- //std::ostream *derr_con_ptr = &dstream;\r
\r
- // Server\r
- std::ostream *dout_server_ptr = &dstream;\r
- std::ostream *derr_server_ptr = &dstream;\r
\r
- // Client\r
- std::ostream *dout_client_ptr = &dstream;\r
- std::ostream *derr_client_ptr = &dstream;\r
\r
- /*\r
-       gettime.h implementation\r
- */\r
\r
- // A small helper class\r
- class TimeGetter\r
- {\r
- public:\r
-       virtual u32 getTime() = 0;\r
- };\r
\r
- // A precise irrlicht one\r
- class IrrlichtTimeGetter: public TimeGetter\r
- {\r
- public:\r
-       IrrlichtTimeGetter(IrrlichtDevice *device):\r
-               m_device(device)\r
-       {}\r
-       u32 getTime()\r
-       {\r
-               if(m_device == NULL)\r
-                       return 0;\r
-               return m_device->getTimer()->getRealTime();\r
-       }\r
- private:\r
-       IrrlichtDevice *m_device;\r
- };\r
- // Not so precise one which works without irrlicht\r
- class SimpleTimeGetter: public TimeGetter\r
- {\r
- public:\r
-       u32 getTime()\r
-       {\r
-               return porting::getTimeMs();\r
-       }\r
- };\r
\r
- // A pointer to a global instance of the time getter\r
- // TODO: why?\r
- TimeGetter *g_timegetter = NULL;\r
\r
- u32 getTimeMs()\r
- {\r
-       if(g_timegetter == NULL)\r
-               return 0;\r
-       return g_timegetter->getTime();\r
- }\r
\r
- /*\r
-       Event handler for Irrlicht\r
\r
-       NOTE: Everything possible should be moved out from here,\r
-             probably to InputHandler and the_game\r
- */\r
\r
- class MyEventReceiver : public IEventReceiver\r
- {\r
- public:\r
-       // This is the one method that we have to implement\r
-       virtual bool OnEvent(const SEvent& event)\r
-       {\r
-               /*\r
-                       React to nothing here if a menu is active\r
-               */\r
-               if(noMenuActive() == false)\r
-               {\r
-                       return false;\r
-               }\r
\r
-               // Remember whether each key is down or up\r
-               if(event.EventType == irr::EET_KEY_INPUT_EVENT)\r
-               {\r
-                       keyIsDown[event.KeyInput.Key] = event.KeyInput.PressedDown;\r
\r
-                       if(event.KeyInput.PressedDown)\r
-                               keyWasDown[event.KeyInput.Key] = true;\r
-               }\r
\r
-               if(event.EventType == irr::EET_MOUSE_INPUT_EVENT)\r
-               {\r
-                       if(noMenuActive() == false)\r
-                       {\r
-                               left_active = false;\r
-                               middle_active = false;\r
-                               right_active = false;\r
-                       }\r
-                       else\r
-                       {\r
-                               //dstream<<"MyEventReceiver: mouse input"<<std::endl;\r
-                               left_active = event.MouseInput.isLeftPressed();\r
-                               middle_active = event.MouseInput.isMiddlePressed();\r
-                               right_active = event.MouseInput.isRightPressed();\r
\r
-                               if(event.MouseInput.Event == EMIE_LMOUSE_PRESSED_DOWN)\r
-                               {\r
-                                       leftclicked = true;\r
-                               }\r
-                               if(event.MouseInput.Event == EMIE_RMOUSE_PRESSED_DOWN)\r
-                               {\r
-                                       rightclicked = true;\r
-                               }\r
-                               if(event.MouseInput.Event == EMIE_LMOUSE_LEFT_UP)\r
-                               {\r
-                                       leftreleased = true;\r
-                               }\r
-                               if(event.MouseInput.Event == EMIE_RMOUSE_LEFT_UP)\r
-                               {\r
-                                       rightreleased = true;\r
-                               }\r
-                               if(event.MouseInput.Event == EMIE_MOUSE_WHEEL)\r
-                               {\r
-                                       mouse_wheel += event.MouseInput.Wheel;\r
-                               }\r
-                       }\r
-               }\r
\r
-               return false;\r
-       }\r
\r
-       bool IsKeyDown(EKEY_CODE keyCode) const\r
-       {\r
-               return keyIsDown[keyCode];\r
-       }\r
-       \r
-       // Checks whether a key was down and resets the state\r
-       bool WasKeyDown(EKEY_CODE keyCode)\r
-       {\r
-               bool b = keyWasDown[keyCode];\r
-               keyWasDown[keyCode] = false;\r
-               return b;\r
-       }\r
\r
-       s32 getMouseWheel()\r
-       {\r
-               s32 a = mouse_wheel;\r
-               mouse_wheel = 0;\r
-               return a;\r
-       }\r
\r
-       void clearInput()\r
-       {\r
-               for(u32 i=0; i<KEY_KEY_CODES_COUNT; i++)\r
-               {\r
-                       keyIsDown[i] = false;\r
-                       keyWasDown[i] = false;\r
-               }\r
-               \r
-               leftclicked = false;\r
-               rightclicked = false;\r
-               leftreleased = false;\r
-               rightreleased = false;\r
\r
-               left_active = false;\r
-               middle_active = false;\r
-               right_active = false;\r
\r
-               mouse_wheel = 0;\r
-       }\r
\r
-       MyEventReceiver()\r
-       {\r
-               clearInput();\r
-       }\r
\r
-       bool leftclicked;\r
-       bool rightclicked;\r
-       bool leftreleased;\r
-       bool rightreleased;\r
\r
-       bool left_active;\r
-       bool middle_active;\r
-       bool right_active;\r
\r
-       s32 mouse_wheel;\r
\r
- private:\r
-       IrrlichtDevice *m_device;\r
-       \r
-       // The current state of keys\r
-       bool keyIsDown[KEY_KEY_CODES_COUNT];\r
-       // Whether a key has been pressed or not\r
-       bool keyWasDown[KEY_KEY_CODES_COUNT];\r
- };\r
\r
- /*\r
-       Separated input handler\r
- */\r
\r
- class RealInputHandler : public InputHandler\r
- {\r
- public:\r
-       RealInputHandler(IrrlichtDevice *device, MyEventReceiver *receiver):\r
-               m_device(device),\r
-               m_receiver(receiver)\r
-       {\r
-       }\r
-       virtual bool isKeyDown(EKEY_CODE keyCode)\r
-       {\r
-               return m_receiver->IsKeyDown(keyCode);\r
-       }\r
-       virtual bool wasKeyDown(EKEY_CODE keyCode)\r
-       {\r
-               return m_receiver->WasKeyDown(keyCode);\r
-       }\r
-       virtual v2s32 getMousePos()\r
-       {\r
-               return m_device->getCursorControl()->getPosition();\r
-       }\r
-       virtual void setMousePos(s32 x, s32 y)\r
-       {\r
-               m_device->getCursorControl()->setPosition(x, y);\r
-       }\r
\r
-       virtual bool getLeftState()\r
-       {\r
-               return m_receiver->left_active;\r
-       }\r
-       virtual bool getRightState()\r
-       {\r
-               return m_receiver->right_active;\r
-       }\r
-       \r
-       virtual bool getLeftClicked()\r
-       {\r
-               return m_receiver->leftclicked;\r
-       }\r
-       virtual bool getRightClicked()\r
-       {\r
-               return m_receiver->rightclicked;\r
-       }\r
-       virtual void resetLeftClicked()\r
-       {\r
-               m_receiver->leftclicked = false;\r
-       }\r
-       virtual void resetRightClicked()\r
-       {\r
-               m_receiver->rightclicked = false;\r
-       }\r
\r
-       virtual bool getLeftReleased()\r
-       {\r
-               return m_receiver->leftreleased;\r
-       }\r
-       virtual bool getRightReleased()\r
-       {\r
-               return m_receiver->rightreleased;\r
-       }\r
-       virtual void resetLeftReleased()\r
-       {\r
-               m_receiver->leftreleased = false;\r
-       }\r
-       virtual void resetRightReleased()\r
-       {\r
-               m_receiver->rightreleased = false;\r
-       }\r
\r
-       virtual s32 getMouseWheel()\r
-       {\r
-               return m_receiver->getMouseWheel();\r
-       }\r
\r
-       void clear()\r
-       {\r
-               m_receiver->clearInput();\r
-       }\r
- private:\r
-       IrrlichtDevice *m_device;\r
-       MyEventReceiver *m_receiver;\r
- };\r
\r
- class RandomInputHandler : public InputHandler\r
- {\r
- public:\r
-       RandomInputHandler()\r
-       {\r
-               leftdown = false;\r
-               rightdown = false;\r
-               leftclicked = false;\r
-               rightclicked = false;\r
-               leftreleased = false;\r
-               rightreleased = false;\r
-               for(u32 i=0; i<KEY_KEY_CODES_COUNT; ++i)\r
-                       keydown[i] = false;\r
-       }\r
-       virtual bool isKeyDown(EKEY_CODE keyCode)\r
-       {\r
-               return keydown[keyCode];\r
-       }\r
-       virtual bool wasKeyDown(EKEY_CODE keyCode)\r
-       {\r
-               return false;\r
-       }\r
-       virtual v2s32 getMousePos()\r
-       {\r
-               return mousepos;\r
-       }\r
-       virtual void setMousePos(s32 x, s32 y)\r
-       {\r
-               mousepos = v2s32(x,y);\r
-       }\r
\r
-       virtual bool getLeftState()\r
-       {\r
-               return leftdown;\r
-       }\r
-       virtual bool getRightState()\r
-       {\r
-               return rightdown;\r
-       }\r
\r
-       virtual bool getLeftClicked()\r
-       {\r
-               return leftclicked;\r
-       }\r
-       virtual bool getRightClicked()\r
-       {\r
-               return rightclicked;\r
-       }\r
-       virtual void resetLeftClicked()\r
-       {\r
-               leftclicked = false;\r
-       }\r
-       virtual void resetRightClicked()\r
-       {\r
-               rightclicked = false;\r
-       }\r
\r
-       virtual bool getLeftReleased()\r
-       {\r
-               return leftreleased;\r
-       }\r
-       virtual bool getRightReleased()\r
-       {\r
-               return rightreleased;\r
-       }\r
-       virtual void resetLeftReleased()\r
-       {\r
-               leftreleased = false;\r
-       }\r
-       virtual void resetRightReleased()\r
-       {\r
-               rightreleased = false;\r
-       }\r
\r
-       virtual s32 getMouseWheel()\r
-       {\r
-               return 0;\r
-       }\r
\r
-       virtual void step(float dtime)\r
-       {\r
-               {\r
-                       static float counter1 = 0;\r
-                       counter1 -= dtime;\r
-                       if(counter1 < 0.0)\r
-                       {\r
-                               counter1 = 0.1*Rand(1, 40);\r
-                               keydown[getKeySetting("keymap_jump")] =\r
-                                               !keydown[getKeySetting("keymap_jump")];\r
-                       }\r
-               }\r
-               {\r
-                       static float counter1 = 0;\r
-                       counter1 -= dtime;\r
-                       if(counter1 < 0.0)\r
-                       {\r
-                               counter1 = 0.1*Rand(1, 40);\r
-                               keydown[getKeySetting("keymap_special1")] =\r
-                                               !keydown[getKeySetting("keymap_special1")];\r
-                       }\r
-               }\r
-               {\r
-                       static float counter1 = 0;\r
-                       counter1 -= dtime;\r
-                       if(counter1 < 0.0)\r
-                       {\r
-                               counter1 = 0.1*Rand(1, 40);\r
-                               keydown[getKeySetting("keymap_forward")] =\r
-                                               !keydown[getKeySetting("keymap_forward")];\r
-                       }\r
-               }\r
-               {\r
-                       static float counter1 = 0;\r
-                       counter1 -= dtime;\r
-                       if(counter1 < 0.0)\r
-                       {\r
-                               counter1 = 0.1*Rand(1, 40);\r
-                               keydown[getKeySetting("keymap_left")] =\r
-                                               !keydown[getKeySetting("keymap_left")];\r
-                       }\r
-               }\r
-               {\r
-                       static float counter1 = 0;\r
-                       counter1 -= dtime;\r
-                       if(counter1 < 0.0)\r
-                       {\r
-                               counter1 = 0.1*Rand(1, 20);\r
-                               mousespeed = v2s32(Rand(-20,20), Rand(-15,20));\r
-                       }\r
-               }\r
-               {\r
-                       static float counter1 = 0;\r
-                       counter1 -= dtime;\r
-                       if(counter1 < 0.0)\r
-                       {\r
-                               counter1 = 0.1*Rand(1, 30);\r
-                               leftdown = !leftdown;\r
-                               if(leftdown)\r
-                                       leftclicked = true;\r
-                               if(!leftdown)\r
-                                       leftreleased = true;\r
-                       }\r
-               }\r
-               {\r
-                       static float counter1 = 0;\r
-                       counter1 -= dtime;\r
-                       if(counter1 < 0.0)\r
-                       {\r
-                               counter1 = 0.1*Rand(1, 15);\r
-                               rightdown = !rightdown;\r
-                               if(rightdown)\r
-                                       rightclicked = true;\r
-                               if(!rightdown)\r
-                                       rightreleased = true;\r
-                       }\r
-               }\r
-               mousepos += mousespeed;\r
-       }\r
\r
-       s32 Rand(s32 min, s32 max)\r
-       {\r
-               return (myrand()%(max-min+1))+min;\r
-       }\r
- private:\r
-       bool keydown[KEY_KEY_CODES_COUNT];\r
-       v2s32 mousepos;\r
-       v2s32 mousespeed;\r
-       bool leftdown;\r
-       bool rightdown;\r
-       bool leftclicked;\r
-       bool rightclicked;\r
-       bool leftreleased;\r
-       bool rightreleased;\r
- };\r
\r
- // These are defined global so that they're not optimized too much.\r
- // Can't change them to volatile.\r
- s16 temp16;\r
- f32 tempf;\r
- v3f tempv3f1;\r
- v3f tempv3f2;\r
- std::string tempstring;\r
- std::string tempstring2;\r
\r
- void SpeedTests()\r
- {\r
-       {\r
-               dstream<<"The following test should take around 20ms."<<std::endl;\r
-               TimeTaker timer("Testing std::string speed");\r
-               const u32 jj = 10000;\r
-               for(u32 j=0; j<jj; j++)\r
-               {\r
-                       tempstring = "";\r
-                       tempstring2 = "";\r
-                       const u32 ii = 10;\r
-                       for(u32 i=0; i<ii; i++){\r
-                               tempstring2 += "asd";\r
-                       }\r
-                       for(u32 i=0; i<ii+1; i++){\r
-                               tempstring += "asd";\r
-                               if(tempstring == tempstring2)\r
-                                       break;\r
-                       }\r
-               }\r
-       }\r
-       \r
-       dstream<<"All of the following tests should take around 100ms each."\r
-                       <<std::endl;\r
\r
-       {\r
-               TimeTaker timer("Testing floating-point conversion speed");\r
-               tempf = 0.001;\r
-               for(u32 i=0; i<4000000; i++){\r
-                       temp16 += tempf;\r
-                       tempf += 0.001;\r
-               }\r
-       }\r
-       \r
-       {\r
-               TimeTaker timer("Testing floating-point vector speed");\r
\r
-               tempv3f1 = v3f(1,2,3);\r
-               tempv3f2 = v3f(4,5,6);\r
-               for(u32 i=0; i<10000000; i++){\r
-                       tempf += tempv3f1.dotProduct(tempv3f2);\r
-                       tempv3f2 += v3f(7,8,9);\r
-               }\r
-       }\r
\r
-       {\r
-               TimeTaker timer("Testing core::map speed");\r
-               \r
-               core::map<v2s16, f32> map1;\r
-               tempf = -324;\r
-               const s16 ii=300;\r
-               for(s16 y=0; y<ii; y++){\r
-                       for(s16 x=0; x<ii; x++){\r
-                               map1.insert(v2s16(x,y), tempf);\r
-                               tempf += 1;\r
-                       }\r
-               }\r
-               for(s16 y=ii-1; y>=0; y--){\r
-                       for(s16 x=0; x<ii; x++){\r
-                               tempf = map1[v2s16(x,y)];\r
-                       }\r
-               }\r
-       }\r
\r
-       {\r
-               dstream<<"Around 5000/ms should do well here."<<std::endl;\r
-               TimeTaker timer("Testing mutex speed");\r
-               \r
-               JMutex m;\r
-               m.Init();\r
-               u32 n = 0;\r
-               u32 i = 0;\r
-               do{\r
-                       n += 10000;\r
-                       for(; i<n; i++){\r
-                               m.Lock();\r
-                               m.Unlock();\r
-                       }\r
-               }\r
-               // Do at least 10ms\r
-               while(timer.getTime() < 10);\r
\r
-               u32 dtime = timer.stop();\r
-               u32 per_ms = n / dtime;\r
-               std::cout<<"Done. "<<dtime<<"ms, "\r
-                               <<per_ms<<"/ms"<<std::endl;\r
-       }\r
- }\r
\r
- void drawMenuBackground(video::IVideoDriver* driver)\r
- {\r
-       core::dimension2d<u32> screensize = driver->getScreenSize();\r
-               \r
-       video::ITexture *bgtexture =\r
-                       driver->getTexture(getTexturePath("mud.png").c_str());\r
-       if(bgtexture)\r
-       {\r
-               s32 texturesize = 128;\r
-               s32 tiled_y = screensize.Height / texturesize + 1;\r
-               s32 tiled_x = screensize.Width / texturesize + 1;\r
-               \r
-               for(s32 y=0; y<tiled_y; y++)\r
-               for(s32 x=0; x<tiled_x; x++)\r
-               {\r
-                       core::rect<s32> rect(0,0,texturesize,texturesize);\r
-                       rect += v2s32(x*texturesize, y*texturesize);\r
-                       driver->draw2DImage(bgtexture, rect,\r
-                               core::rect<s32>(core::position2d<s32>(0,0),\r
-                               core::dimension2di(bgtexture->getSize())),\r
-                               NULL, NULL, true);\r
-               }\r
-       }\r
-       \r
-       video::ITexture *logotexture =\r
-                       driver->getTexture(getTexturePath("menulogo.png").c_str());\r
-       if(logotexture)\r
-       {\r
-               v2s32 logosize(logotexture->getOriginalSize().Width,\r
-                               logotexture->getOriginalSize().Height);\r
-               logosize *= 4;\r
\r
-               video::SColor bgcolor(255,50,50,50);\r
-               core::rect<s32> bgrect(0, screensize.Height-logosize.Y-20,\r
-                               screensize.Width, screensize.Height);\r
-               driver->draw2DRectangle(bgcolor, bgrect, NULL);\r
\r
-               core::rect<s32> rect(0,0,logosize.X,logosize.Y);\r
-               rect += v2s32(screensize.Width/2,screensize.Height-10-logosize.Y);\r
-               rect -= v2s32(logosize.X/2, 0);\r
-               driver->draw2DImage(logotexture, rect,\r
-                       core::rect<s32>(core::position2d<s32>(0,0),\r
-                       core::dimension2di(logotexture->getSize())),\r
-                       NULL, NULL, true);\r
-       }\r
- }\r
\r
- int main(int argc, char *argv[])\r
- {\r
-       /*\r
-               Initialization\r
-       */\r
\r
-       // Set locale. This is for forcing '.' as the decimal point.\r
-       std::locale::global(std::locale("C"));\r
-       // This enables printing all characters in bitmap font\r
-       setlocale(LC_CTYPE, "en_US");\r
-       /*\r
-               Parse command line\r
-       */\r
-       \r
-       // List all allowed options\r
-       core::map<std::string, ValueSpec> allowed_options;\r
-       allowed_options.insert("help", ValueSpec(VALUETYPE_FLAG));\r
-       allowed_options.insert("server", ValueSpec(VALUETYPE_FLAG,\r
-                       "Run server directly"));\r
-       allowed_options.insert("config", ValueSpec(VALUETYPE_STRING,\r
-                       "Load configuration from specified file"));\r
-       allowed_options.insert("port", ValueSpec(VALUETYPE_STRING));\r
-       allowed_options.insert("address", ValueSpec(VALUETYPE_STRING));\r
-       allowed_options.insert("random-input", ValueSpec(VALUETYPE_FLAG));\r
-       allowed_options.insert("disable-unittests", ValueSpec(VALUETYPE_FLAG));\r
-       allowed_options.insert("enable-unittests", ValueSpec(VALUETYPE_FLAG));\r
-       allowed_options.insert("map-dir", ValueSpec(VALUETYPE_STRING));\r
- #ifdef _WIN32\r
-       allowed_options.insert("dstream-on-stderr", ValueSpec(VALUETYPE_FLAG));\r
- #endif\r
-       allowed_options.insert("speedtests", ValueSpec(VALUETYPE_FLAG));\r
\r
-       Settings cmd_args;\r
-       \r
-       bool ret = cmd_args.parseCommandLine(argc, argv, allowed_options);\r
\r
-       if(ret == false || cmd_args.getFlag("help"))\r
-       {\r
-               dstream<<"Allowed options:"<<std::endl;\r
-               for(core::map<std::string, ValueSpec>::Iterator\r
-                               i = allowed_options.getIterator();\r
-                               i.atEnd() == false; i++)\r
-               {\r
-                       dstream<<"  --"<<i.getNode()->getKey();\r
-                       if(i.getNode()->getValue().type == VALUETYPE_FLAG)\r
-                       {\r
-                       }\r
-                       else\r
-                       {\r
-                               dstream<<" <value>";\r
-                       }\r
-                       dstream<<std::endl;\r
\r
-                       if(i.getNode()->getValue().help != NULL)\r
-                       {\r
-                               dstream<<"      "<<i.getNode()->getValue().help\r
-                                               <<std::endl;\r
-                       }\r
-               }\r
\r
-               return cmd_args.getFlag("help") ? 0 : 1;\r
-       }\r
-       \r
-       /*\r
-               Low-level initialization\r
-       */\r
\r
-       bool disable_stderr = false;\r
- #ifdef _WIN32\r
-       if(cmd_args.getFlag("dstream-on-stderr") == false)\r
-               disable_stderr = true;\r
- #endif\r
\r
-       porting::signal_handler_init();\r
-       bool &kill = *porting::signal_handler_killstatus();\r
-       \r
-       // Initialize porting::path_data and porting::path_userdata\r
-       porting::initializePaths();\r
\r
-       // Create user data directory\r
-       fs::CreateDir(porting::path_userdata);\r
\r
-       init_gettext((porting::path_data+"/../locale").c_str());\r
\r
-       // Initialize debug streams\r
- #ifdef RUN_IN_PLACE\r
-       std::string debugfile = DEBUGFILE;\r
- #else\r
-       std::string debugfile = porting::path_userdata+"/"+DEBUGFILE;\r
- #endif\r
-       debugstreams_init(disable_stderr, debugfile.c_str());\r
-       // Initialize debug stacks\r
-       debug_stacks_init();\r
\r
-       DSTACK(__FUNCTION_NAME);\r
\r
-       // Init material properties table\r
-       //initializeMaterialProperties();\r
\r
-       // Debug handler\r
-       BEGIN_DEBUG_EXCEPTION_HANDLER\r
\r
-       // Print startup message\r
-       dstream<<DTIME<<PROJECT_NAME <<\r
-                       " with SER_FMT_VER_HIGHEST="<<(int)SER_FMT_VER_HIGHEST\r
-                       <<", "<<BUILD_INFO\r
-                       <<std::endl;\r
-       \r
-       /*\r
-               Basic initialization\r
-       */\r
\r
-       // Initialize default settings\r
-       set_default_settings();\r
-       \r
-       // Initialize sockets\r
-       sockets_init();\r
-       atexit(sockets_cleanup);\r
-       \r
-       /*\r
-               Read config file\r
-       */\r
-       \r
-       // Path of configuration file in use\r
-       std::string configpath = "";\r
-       \r
-       if(cmd_args.exists("config"))\r
-       {\r
-               bool r = g_settings.readConfigFile(cmd_args.get("config").c_str());\r
-               if(r == false)\r
-               {\r
-                       dstream<<"Could not read configuration from \""\r
-                                       <<cmd_args.get("config")<<"\""<<std::endl;\r
-                       return 1;\r
-               }\r
-               configpath = cmd_args.get("config");\r
-       }\r
-       else\r
-       {\r
-               core::array<std::string> filenames;\r
-               filenames.push_back(porting::path_userdata + "/minetest.conf");\r
- #ifdef RUN_IN_PLACE\r
-               filenames.push_back(porting::path_userdata + "/../minetest.conf");\r
- #endif\r
\r
-               for(u32 i=0; i<filenames.size(); i++)\r
-               {\r
-                       bool r = g_settings.readConfigFile(filenames[i].c_str());\r
-                       if(r)\r
-                       {\r
-                               configpath = filenames[i];\r
-                               break;\r
-                       }\r
-               }\r
-               \r
-               // If no path found, use the first one (menu creates the file)\r
-               if(configpath == "")\r
-                       configpath = filenames[0];\r
-       }\r
\r
-       // Initialize random seed\r
-       srand(time(0));\r
-       mysrand(time(0));\r
\r
-       /*\r
-               Pre-initialize some stuff with a dummy irrlicht wrapper.\r
\r
-               These are needed for unit tests at least.\r
-       */\r
-       \r
-       // Initial call with g_texturesource not set.\r
-       init_mapnode();\r
\r
-       /*\r
-               Run unit tests\r
-       */\r
\r
-       if((ENABLE_TESTS && cmd_args.getFlag("disable-unittests") == false)\r
-                       || cmd_args.getFlag("enable-unittests") == true)\r
-       {\r
-               run_tests();\r
-       }\r
-       \r
-       /*for(s16 y=-100; y<100; y++)\r
-       for(s16 x=-100; x<100; x++)\r
-       {\r
-               std::cout<<noise2d_gradient((double)x/10,(double)y/10, 32415)<<std::endl;\r
-       }\r
-       return 0;*/\r
-       \r
-       /*\r
-               Game parameters\r
-       */\r
\r
-       // Port\r
-       u16 port = 30000;\r
-       if(cmd_args.exists("port"))\r
-               port = cmd_args.getU16("port");\r
-       else if(g_settings.exists("port"))\r
-               port = g_settings.getU16("port");\r
-       if(port == 0)\r
-               port = 30000;\r
-       \r
-       // Map directory\r
-       std::string map_dir = porting::path_userdata+"/world";\r
-       if(cmd_args.exists("map-dir"))\r
-               map_dir = cmd_args.get("map-dir");\r
-       else if(g_settings.exists("map-dir"))\r
-               map_dir = g_settings.get("map-dir");\r
-       \r
-       // Run dedicated server if asked to\r
-       if(cmd_args.getFlag("server"))\r
-       {\r
-               DSTACK("Dedicated server branch");\r
\r
-               // Create time getter\r
-               g_timegetter = new SimpleTimeGetter();\r
-               \r
-               // Create server\r
-               Server server(map_dir.c_str());\r
-               server.start(port);\r
-               \r
-               // Run server\r
-               dedicated_server_loop(server, kill);\r
\r
-               return 0;\r
-       }\r
\r
\r
-       /*\r
-               More parameters\r
-       */\r
-       \r
-       // Address to connect to\r
-       std::string address = "";\r
-       \r
-       if(cmd_args.exists("address"))\r
-       {\r
-               address = cmd_args.get("address");\r
-       }\r
-       else\r
-       {\r
-               address = g_settings.get("address");\r
-       }\r
-       \r
-       std::string playername = g_settings.get("name");\r
\r
-       /*\r
-               Device initialization\r
-       */\r
\r
-       // Resolution selection\r
-       \r
-       bool fullscreen = false;\r
-       u16 screenW = g_settings.getU16("screenW");\r
-       u16 screenH = g_settings.getU16("screenH");\r
\r
-       // Determine driver\r
\r
-       video::E_DRIVER_TYPE driverType;\r
-       \r
-       std::string driverstring = g_settings.get("video_driver");\r
\r
-       if(driverstring == "null")\r
-               driverType = video::EDT_NULL;\r
-       else if(driverstring == "software")\r
-               driverType = video::EDT_SOFTWARE;\r
-       else if(driverstring == "burningsvideo")\r
-               driverType = video::EDT_BURNINGSVIDEO;\r
-       else if(driverstring == "direct3d8")\r
-               driverType = video::EDT_DIRECT3D8;\r
-       else if(driverstring == "direct3d9")\r
-               driverType = video::EDT_DIRECT3D9;\r
-       else if(driverstring == "opengl")\r
-               driverType = video::EDT_OPENGL;\r
-       else\r
-       {\r
-               dstream<<"WARNING: Invalid video_driver specified; defaulting "\r
-                               "to opengl"<<std::endl;\r
-               driverType = video::EDT_OPENGL;\r
-       }\r
\r
-       /*\r
-               Create device and exit if creation failed\r
-       */\r
\r
-       MyEventReceiver receiver;\r
\r
-       IrrlichtDevice *device;\r
-       device = createDevice(driverType,\r
-                       core::dimension2d<u32>(screenW, screenH),\r
-                       16, fullscreen, false, false, &receiver);\r
\r
-       if (device == 0)\r
-               return 1; // could not create selected driver.\r
-       \r
-       // Set device in game parameters\r
-       device = device;\r
+ /*
+ Minetest-c55
+ Copyright (C) 2010-2011 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 General Public License as published by
+ the Free Software Foundation; either version 2 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 General Public License for more details.
+ You should have received a copy of the GNU 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.
+ */
+ /*
+ =============================== NOTES ==============================
+ NOTE: Things starting with TODO are sometimes only suggestions.
+ NOTE: iostream.imbue(std::locale("C")) is very slow
+ NOTE: Global locale is now set at initialization
+ NOTE: If VBO (EHM_STATIC) is used, remember to explicitly free the
+       hardware buffer (it is not freed automatically)
+ NOTE: A random to-do list saved here as documentation:
+ A list of "active blocks" in which stuff happens. (+=done)
+       + Add a never-resetted game timer to the server
+       + Add a timestamp value to blocks
+       + The simple rule: All blocks near some player are "active"
+       - Do stuff in real time in active blocks
+               + Handle objects
+               - Grow grass, delete leaves without a tree
+               - Spawn some mobs based on some rules
+               - Transform cobble to mossy cobble near water
+               - Run a custom script
+               - ...And all kinds of other dynamic stuff
+       + Keep track of when a block becomes active and becomes inactive
+       + When a block goes inactive:
+               + Store objects statically to block
+               + Store timer value as the timestamp
+       + When a block goes active:
+               + Create active objects out of static objects
+               - Simulate the results of what would have happened if it would have
+                 been active for all the time
+                       - Grow a lot of grass and so on
+       + Initially it is fine to send information about every active object
+         to every player. Eventually it should be modified to only send info
+         about the nearest ones.
+               + This was left to be done by the old system and it sends only the
+                 nearest ones.
+ Vim conversion regexpes for moving to extended content type storage:
+ %s/\(\.\|->\)d \([!=]=\)/\1getContent() \2/g
+ %s/content_features(\([^.]*\)\.d)/content_features(\1)/g
+ %s/\(\.\|->\)d = \([^;]*\);/\1setContent(\2);/g
+ %s/\(getNodeNoExNoEmerge([^)]*)\)\.d/\1.getContent()/g
+ %s/\(getNodeNoExNoEmerge(.*)\)\.d/\1.getContent()/g
+ %s/\.d;/.getContent();/g
+ %s/\(content_liquid\|content_flowing_liquid\|make_liquid_flowing\|content_pointable\)(\([^.]*\).d)/\1(\2.getContent())/g
+ Other things to note:
+ - node.d = node.param0 (only in raw serialization; use getContent() otherwise)
+ - node.param = node.param1
+ - node.dir = node.param2
+ - content_walkable(node.d) etc should be changed to
+   content_features(node).walkable etc
+ - Also check for lines that store the result of getContent to a 8-bit
+   variable and fix them (result of getContent() must be stored in
+   content_t, which is 16-bit)
+ NOTE: Seeds in 1260:6c77e7dbfd29:
+ 5721858502589302589:
+       Spawns you on a small sand island with a surface dungeon
+ 2983455799928051958:
+       Enormous jungle + a surface dungeon at ~(250,0,0)
+ Old, wild and random suggestions that probably won't be done:
+ -------------------------------------------------------------
+ SUGG: If player is on ground, mainly fetch ground-level blocks
+ SUGG: Expose Connection's seqnums and ACKs to server and client.
+       - This enables saving many packets and making a faster connection
+         - This also enables server to check if client has received the
+           most recent block sent, for example.
+ SUGG: Add a sane bandwidth throttling system to Connection
+ SUGG: More fine-grained control of client's dumping of blocks from
+       memory
+         - ...What does this mean in the first place?
+ SUGG: A map editing mode (similar to dedicated server mode)
+ SUGG: Transfer more blocks in a single packet
+ SUGG: A blockdata combiner class, to which blocks are added and at
+       destruction it sends all the stuff in as few packets as possible.
+ SUGG: Make a PACKET_COMBINED which contains many subpackets. Utilize
+       it by sending more stuff in a single packet.
+         - Add a packet queue to RemoteClient, from which packets will be
+           combined with object data packets
+               - This is not exactly trivial: the object data packets are
+                 sometimes very big by themselves
+         - This might not give much network performance gain though.
+ SUGG: Precalculate lighting translation table at runtime (at startup)
+       - This is not doable because it is currently hand-made and not
+           based on some mathematical function.
+               - Note: This has been changing lately
+ SUGG: A version number to blocks, which increments when the block is
+       modified (node add/remove, water update, lighting update)
+         - This can then be used to make sure the most recent version of
+           a block has been sent to client, for example
+ SUGG: Make the amount of blocks sending to client and the total
+         amount of blocks dynamically limited. Transferring blocks is the
+         main network eater of this system, so it is the one that has
+         to be throttled so that RTTs stay low.
+ SUGG: Meshes of blocks could be split into 6 meshes facing into
+       different directions and then only those drawn that need to be
+ SUGG: Background music based on cellular automata?
+       http://www.earslap.com/projectslab/otomata
+ SUGG: Simple light color information to air
+ SUGG: Server-side objects could be moved based on nodes to enable very
+       lightweight operation and simple AI
+       - Not practical; client would still need to show smooth movement.
+ SUGG: Make a system for pregenerating quick information for mapblocks, so
+         that the client can show them as cubes before they are actually sent
+         or even generated.
+ SUGG: Erosion simulation at map generation time
+     - This might be plausible if larger areas of map were pregenerated
+         without lighting (which is slow)
+       - Simulate water flows, which would carve out dirt fast and
+         then turn stone into gravel and sand and relocate it.
+       - How about relocating minerals, too? Coal and gold in
+         downstream sand and gravel would be kind of cool
+         - This would need a better way of handling minerals, mainly
+               to have mineral content as a separate field. the first
+               parameter field is free for this.
+       - Simulate rock falling from cliffs when water has removed
+         enough solid rock from the bottom
+ SUGG: For non-mapgen FarMesh: Add a per-sector database to store surface
+       stuff as simple flags/values
+       - Light?
+         - A building?
+         And at some point make the server send this data to the client too,
+         instead of referring to the noise functions
+         - Ground height
+         - Surface ground type
+         - Trees?
+ Gaming ideas:
+ -------------
+ - Aim for something like controlling a single dwarf in Dwarf Fortress
+ - The player could go faster by a crafting a boat, or riding an animal
+ - Random NPC traders. what else?
+ Game content:
+ -------------
+ - When furnace is destroyed, move items to player's inventory
+ - Add lots of stuff
+ - Glass blocks
+ - Growing grass, decaying leaves
+       - This can be done in the active blocks I guess.
+       - Lots of stuff can be done in the active blocks.
+       - Uh, is there an active block list somewhere? I think not. Add it.
+ - Breaking weak structures
+       - This can probably be accomplished in the same way as grass
+ - Player health points
+       - When player dies, throw items on map (needs better item-on-map
+         implementation)
+ - Cobble to get mossy if near water
+ - More slots in furnace source list, so that multiple ingredients
+   are possible.
+ - Keys to chests?
+ - The Treasure Guard; a big monster with a hammer
+       - The hammer does great damage, shakes the ground and removes a block
+       - You can drop on top of it, and have some time to attack there
+         before he shakes you off
+ - Maybe the difficulty could come from monsters getting tougher in
+   far-away places, and the player starting to need something from
+   there when time goes by.
+   - The player would have some of that stuff at the beginning, and
+     would need new supplies of it when it runs out
+ - A bomb
+ - A spread-items-on-map routine for the bomb, and for dying players
+ - Fighting:
+   - Proper sword swing simulation
+   - Player should get damage from colliding to a wall at high speed
+ Documentation:
+ --------------
+ Build system / running:
+ -----------------------
+ Networking and serialization:
+ -----------------------------
+ SUGG: Fix address to be ipv6 compatible
+ User Interface:
+ ---------------
+ Graphics:
+ ---------
+ SUGG: Combine MapBlock's face caches to so big pieces that VBO
+       can be used
+       - That is >500 vertices
+         - This is not easy; all the MapBlocks close to the player would
+           still need to be drawn separately and combining the blocks
+               would have to happen in a background thread
+ SUGG: Make fetching sector's blocks more efficient when rendering
+       sectors that have very large amounts of blocks (on client)
+         - Is this necessary at all?
+ SUGG: Draw cubes in inventory directly with 3D drawing commands, so that
+       animating them is easier.
+ SUGG: Option for enabling proper alpha channel for textures
+ TODO: Flowing water animation
+ TODO: A setting for enabling bilinear filtering for textures
+ TODO: Better control of draw_control.wanted_max_blocks
+ TODO: Further investigate the use of GPU lighting in addition to the
+       current one
+ TODO: Artificial (night) light could be more yellow colored than sunlight.
+       - This is technically doable.
+         - Also the actual colors of the textures could be made less colorful
+           in the dark but it's a bit more difficult.
+ SUGG: Somehow make the night less colorful
+ TODO: Occlusion culling
+       - At the same time, move some of the renderMap() block choosing code
+         to the same place as where the new culling happens.
+       - Shoot some rays per frame and when ready, make a new list of
+           blocks for usage of renderMap and give it a new pointer to it.
+ Configuration:
+ --------------
+ Client:
+ -------
+ TODO: Untie client network operations from framerate
+       - Needs some input queues or something
+         - This won't give much performance boost because calculating block
+           meshes takes so long
+ SUGG: Make morning and evening transition more smooth and maybe shorter
+ TODO: Don't update all meshes always on single node changes, but
+       check which ones should be updated
+         - implement Map::updateNodeMeshes() and the usage of it
+         - It will give almost always a 4x boost in mesh update performance.
+ - A weapon engine
+ - Tool/weapon visualization
+ FIXME: When disconnected to the menu, memory is not freed properly
+ TODO: Investigate how much the mesh generator thread gets used when
+       transferring map data
+ Server:
+ -------
+ SUGG: Make an option to the server to disable building and digging near
+       the starting position
+ FIXME: Server sometimes goes into some infinite PeerNotFoundException loop
+ * Fix the problem with the server constantly saving one or a few
+   blocks? List the first saved block, maybe it explains.
+   - It is probably caused by oscillating water
+   - TODO: Investigate if this still happens (this is a very old one)
+ * Make a small history check to transformLiquids to detect and log
+   continuous oscillations, in such detail that they can be fixed.
+ FIXME: The new optimized map sending doesn't sometimes send enough blocks
+        from big caves and such
+ FIXME: Block send distance configuration does not take effect for some reason
+ Environment:
+ ------------
+ TODO: Add proper hooks to when adding and removing active blocks
+ TODO: Finish the ActiveBlockModifier stuff and use it for something
+ Objects:
+ --------
+ TODO: Get rid of MapBlockObjects and use only ActiveObjects
+       - Skipping the MapBlockObject data is nasty - there is no "total
+         length" stored; have to make a SkipMBOs function which contains
+         enough of the current code to skip them properly.
+ SUGG: MovingObject::move and Player::move are basically the same.
+       combine them.
+       - NOTE: This is a bit tricky because player has the sneaking ability
+       - NOTE: Player::move is more up-to-date.
+       - NOTE: There is a simple move implementation now in collision.{h,cpp}
+       - NOTE: MovingObject will be deleted (MapBlockObject)
+ TODO: Add a long step function to objects that is called with the time
+       difference when block activates
+ Map:
+ ----
+ TODO: Mineral and ground material properties
+       - This way mineral ground toughness can be calculated with just
+           some formula, as well as tool strengths. Sounds too.
+         - There are TODOs in appropriate files: material.h, content_mapnode.h
+ TODO: Flowing water to actually contain flow direction information
+       - There is a space for this - it just has to be implemented.
+ TODO: Consider smoothening cave floors after generating them
+ TODO: Fix make_tree, make_* to use seed-position-consistent pseudorandom
+         - delta also
+ Misc. stuff:
+ ------------
+ TODO: Make sure server handles removing grass when a block is placed (etc)
+       - The client should not do it by itself
+         - NOTE: I think nobody does it currently...
+ TODO: Block cube placement around player's head
+ TODO: Protocol version field
+ TODO: Think about using same bits for material for fences and doors, for
+         example
+ TODO: Move mineral to param2, increment map serialization version, add
+       conversion
+ SUGG: Restart irrlicht completely when coming back to main menu from game.
+       - This gets rid of everything that is stored in irrlicht's caches.
+       - This might be needed for texture pack selection in menu
+ TODO: Merge bahamada's audio stuff (clean patch available)
+ TODO: Move content_features to mapnode_content_features.{h,cpp} or so
+ Making it more portable:
+ ------------------------
+  
+ Stuff to do before release:
+ ---------------------------
+ Fixes to the current release:
+ -----------------------------
+ Stuff to do after release:
+ ---------------------------
+ Doing currently:
+ ----------------
+ ======================================================================
+ */
+ #ifdef NDEBUG
+       #ifdef _WIN32
+               #pragma message ("Disabling unit tests")
+       #else
+               #warning "Disabling unit tests"
+       #endif
+       // Disable unit tests
+       #define ENABLE_TESTS 0
+ #else
+       // Enable unit tests
+       #define ENABLE_TESTS 1
+ #endif
+ #ifdef _MSC_VER
+       #pragma comment(lib, "Irrlicht.lib")
+       //#pragma comment(lib, "jthread.lib")
+       #pragma comment(lib, "zlibwapi.lib")
+       #pragma comment(lib, "Shell32.lib")
+       // This would get rid of the console window
+       //#pragma comment(linker, "/subsystem:windows /ENTRY:mainCRTStartup")
+ #endif
+ #include <iostream>
+ #include <fstream>
+ #include <locale.h>
+ #include "main.h"
+ #include "common_irrlicht.h"
+ #include "debug.h"
+ #include "test.h"
+ #include "server.h"
+ #include "constants.h"
+ #include "porting.h"
+ #include "gettime.h"
+ #include "guiMessageMenu.h"
+ #include "filesys.h"
+ #include "config.h"
+ #include "guiMainMenu.h"
+ #include "mineral.h"
+ #include "materials.h"
+ #include "game.h"
+ #include "keycode.h"
+ #include "tile.h"
+ #include "gettext.h"
+ // This makes textures
+ ITextureSource *g_texturesource = NULL;
+ /*
+       Settings.
+       These are loaded from the config file.
+ */
+ Settings g_settings;
+ // This is located in defaultsettings.cpp
+ extern void set_default_settings();
+ // Global profiler
+ Profiler g_profiler;
+ /*
+       Random stuff
+ */
+ /*
+       GUI Stuff
+ */
+ gui::IGUIEnvironment* guienv = NULL;
+ gui::IGUIStaticText *guiroot = NULL;
+ MainMenuManager g_menumgr;
+ bool noMenuActive()
+ {
+       return (g_menumgr.menuCount() == 0);
+ }
+ // Passed to menus to allow disconnecting and exiting
+ MainGameCallback *g_gamecallback = NULL;
+ /*
+       Debug streams
+ */
+ // Connection
+ std::ostream *dout_con_ptr = &dummyout;
+ std::ostream *derr_con_ptr = &dstream_no_stderr;
+ //std::ostream *dout_con_ptr = &dstream_no_stderr;
+ //std::ostream *derr_con_ptr = &dstream_no_stderr;
+ //std::ostream *dout_con_ptr = &dstream;
+ //std::ostream *derr_con_ptr = &dstream;
+ // Server
+ std::ostream *dout_server_ptr = &dstream;
+ std::ostream *derr_server_ptr = &dstream;
+ // Client
+ std::ostream *dout_client_ptr = &dstream;
+ std::ostream *derr_client_ptr = &dstream;
+ /*
+       gettime.h implementation
+ */
+ // A small helper class
+ class TimeGetter
+ {
+ public:
+       virtual u32 getTime() = 0;
+ };
+ // A precise irrlicht one
+ class IrrlichtTimeGetter: public TimeGetter
+ {
+ public:
+       IrrlichtTimeGetter(IrrlichtDevice *device):
+               m_device(device)
+       {}
+       u32 getTime()
+       {
+               if(m_device == NULL)
+                       return 0;
+               return m_device->getTimer()->getRealTime();
+       }
+ private:
+       IrrlichtDevice *m_device;
+ };
+ // Not so precise one which works without irrlicht
+ class SimpleTimeGetter: public TimeGetter
+ {
+ public:
+       u32 getTime()
+       {
+               return porting::getTimeMs();
+       }
+ };
+ // A pointer to a global instance of the time getter
+ // TODO: why?
+ TimeGetter *g_timegetter = NULL;
+ u32 getTimeMs()
+ {
+       if(g_timegetter == NULL)
+               return 0;
+       return g_timegetter->getTime();
+ }
+ /*
+       Event handler for Irrlicht
+       NOTE: Everything possible should be moved out from here,
+             probably to InputHandler and the_game
+ */
+ class MyEventReceiver : public IEventReceiver
+ {
+ public:
+       // This is the one method that we have to implement
+       virtual bool OnEvent(const SEvent& event)
+       {
+               /*
+                       React to nothing here if a menu is active
+               */
+               if(noMenuActive() == false)
+               {
+                       return false;
+               }
+               // Remember whether each key is down or up
+               if(event.EventType == irr::EET_KEY_INPUT_EVENT)
+               {
+                       keyIsDown[event.KeyInput.Key] = event.KeyInput.PressedDown;
+                       if(event.KeyInput.PressedDown)
+                               keyWasDown[event.KeyInput.Key] = true;
+               }
+               if(event.EventType == irr::EET_MOUSE_INPUT_EVENT)
+               {
+                       if(noMenuActive() == false)
+                       {
+                               left_active = false;
+                               middle_active = false;
+                               right_active = false;
+                       }
+                       else
+                       {
+                               //dstream<<"MyEventReceiver: mouse input"<<std::endl;
+                               left_active = event.MouseInput.isLeftPressed();
+                               middle_active = event.MouseInput.isMiddlePressed();
+                               right_active = event.MouseInput.isRightPressed();
+                               if(event.MouseInput.Event == EMIE_LMOUSE_PRESSED_DOWN)
+                               {
+                                       leftclicked = true;
+                               }
+                               if(event.MouseInput.Event == EMIE_RMOUSE_PRESSED_DOWN)
+                               {
+                                       rightclicked = true;
+                               }
+                               if(event.MouseInput.Event == EMIE_LMOUSE_LEFT_UP)
+                               {
+                                       leftreleased = true;
+                               }
+                               if(event.MouseInput.Event == EMIE_RMOUSE_LEFT_UP)
+                               {
+                                       rightreleased = true;
+                               }
+                               if(event.MouseInput.Event == EMIE_MOUSE_WHEEL)
+                               {
+                                       mouse_wheel += event.MouseInput.Wheel;
+                               }
+                       }
+               }
+               return false;
+       }
+       bool IsKeyDown(EKEY_CODE keyCode) const
+       {
+               return keyIsDown[keyCode];
+       }
+       
+       // Checks whether a key was down and resets the state
+       bool WasKeyDown(EKEY_CODE keyCode)
+       {
+               bool b = keyWasDown[keyCode];
+               keyWasDown[keyCode] = false;
+               return b;
+       }
+       s32 getMouseWheel()
+       {
+               s32 a = mouse_wheel;
+               mouse_wheel = 0;
+               return a;
+       }
+       void clearInput()
+       {
+               for(u32 i=0; i<KEY_KEY_CODES_COUNT; i++)
+               {
+                       keyIsDown[i] = false;
+                       keyWasDown[i] = false;
+               }
+               
+               leftclicked = false;
+               rightclicked = false;
+               leftreleased = false;
+               rightreleased = false;
+               left_active = false;
+               middle_active = false;
+               right_active = false;
+               mouse_wheel = 0;
+       }
+       MyEventReceiver()
+       {
+               clearInput();
+       }
+       bool leftclicked;
+       bool rightclicked;
+       bool leftreleased;
+       bool rightreleased;
+       bool left_active;
+       bool middle_active;
+       bool right_active;
+       s32 mouse_wheel;
+ private:
+       IrrlichtDevice *m_device;
+       
+       // The current state of keys
+       bool keyIsDown[KEY_KEY_CODES_COUNT];
+       // Whether a key has been pressed or not
+       bool keyWasDown[KEY_KEY_CODES_COUNT];
+ };
+ /*
+       Separated input handler
+ */
+ class RealInputHandler : public InputHandler
+ {
+ public:
+       RealInputHandler(IrrlichtDevice *device, MyEventReceiver *receiver):
+               m_device(device),
+               m_receiver(receiver)
+       {
+       }
+       virtual bool isKeyDown(EKEY_CODE keyCode)
+       {
+               return m_receiver->IsKeyDown(keyCode);
+       }
+       virtual bool wasKeyDown(EKEY_CODE keyCode)
+       {
+               return m_receiver->WasKeyDown(keyCode);
+       }
+       virtual v2s32 getMousePos()
+       {
+               return m_device->getCursorControl()->getPosition();
+       }
+       virtual void setMousePos(s32 x, s32 y)
+       {
+               m_device->getCursorControl()->setPosition(x, y);
+       }
+       virtual bool getLeftState()
+       {
+               return m_receiver->left_active;
+       }
+       virtual bool getRightState()
+       {
+               return m_receiver->right_active;
+       }
+       
+       virtual bool getLeftClicked()
+       {
+               return m_receiver->leftclicked;
+       }
+       virtual bool getRightClicked()
+       {
+               return m_receiver->rightclicked;
+       }
+       virtual void resetLeftClicked()
+       {
+               m_receiver->leftclicked = false;
+       }
+       virtual void resetRightClicked()
+       {
+               m_receiver->rightclicked = false;
+       }
+       virtual bool getLeftReleased()
+       {
+               return m_receiver->leftreleased;
+       }
+       virtual bool getRightReleased()
+       {
+               return m_receiver->rightreleased;
+       }
+       virtual void resetLeftReleased()
+       {
+               m_receiver->leftreleased = false;
+       }
+       virtual void resetRightReleased()
+       {
+               m_receiver->rightreleased = false;
+       }
+       virtual s32 getMouseWheel()
+       {
+               return m_receiver->getMouseWheel();
+       }
+       void clear()
+       {
+               m_receiver->clearInput();
+       }
+ private:
+       IrrlichtDevice *m_device;
+       MyEventReceiver *m_receiver;
+ };
+ class RandomInputHandler : public InputHandler
+ {
+ public:
+       RandomInputHandler()
+       {
+               leftdown = false;
+               rightdown = false;
+               leftclicked = false;
+               rightclicked = false;
+               leftreleased = false;
+               rightreleased = false;
+               for(u32 i=0; i<KEY_KEY_CODES_COUNT; ++i)
+                       keydown[i] = false;
+       }
+       virtual bool isKeyDown(EKEY_CODE keyCode)
+       {
+               return keydown[keyCode];
+       }
+       virtual bool wasKeyDown(EKEY_CODE keyCode)
+       {
+               return false;
+       }
+       virtual v2s32 getMousePos()
+       {
+               return mousepos;
+       }
+       virtual void setMousePos(s32 x, s32 y)
+       {
+               mousepos = v2s32(x,y);
+       }
+       virtual bool getLeftState()
+       {
+               return leftdown;
+       }
+       virtual bool getRightState()
+       {
+               return rightdown;
+       }
+       virtual bool getLeftClicked()
+       {
+               return leftclicked;
+       }
+       virtual bool getRightClicked()
+       {
+               return rightclicked;
+       }
+       virtual void resetLeftClicked()
+       {
+               leftclicked = false;
+       }
+       virtual void resetRightClicked()
+       {
+               rightclicked = false;
+       }
+       virtual bool getLeftReleased()
+       {
+               return leftreleased;
+       }
+       virtual bool getRightReleased()
+       {
+               return rightreleased;
+       }
+       virtual void resetLeftReleased()
+       {
+               leftreleased = false;
+       }
+       virtual void resetRightReleased()
+       {
+               rightreleased = false;
+       }
+       virtual s32 getMouseWheel()
+       {
+               return 0;
+       }
+       virtual void step(float dtime)
+       {
+               {
+                       static float counter1 = 0;
+                       counter1 -= dtime;
+                       if(counter1 < 0.0)
+                       {
+                               counter1 = 0.1*Rand(1, 40);
+                               keydown[getKeySetting("keymap_jump")] =
+                                               !keydown[getKeySetting("keymap_jump")];
+                       }
+               }
+               {
+                       static float counter1 = 0;
+                       counter1 -= dtime;
+                       if(counter1 < 0.0)
+                       {
+                               counter1 = 0.1*Rand(1, 40);
+                               keydown[getKeySetting("keymap_special1")] =
+                                               !keydown[getKeySetting("keymap_special1")];
+                       }
+               }
+               {
+                       static float counter1 = 0;
+                       counter1 -= dtime;
+                       if(counter1 < 0.0)
+                       {
+                               counter1 = 0.1*Rand(1, 40);
+                               keydown[getKeySetting("keymap_forward")] =
+                                               !keydown[getKeySetting("keymap_forward")];
+                       }
+               }
+               {
+                       static float counter1 = 0;
+                       counter1 -= dtime;
+                       if(counter1 < 0.0)
+                       {
+                               counter1 = 0.1*Rand(1, 40);
+                               keydown[getKeySetting("keymap_left")] =
+                                               !keydown[getKeySetting("keymap_left")];
+                       }
+               }
+               {
+                       static float counter1 = 0;
+                       counter1 -= dtime;
+                       if(counter1 < 0.0)
+                       {
+                               counter1 = 0.1*Rand(1, 20);
+                               mousespeed = v2s32(Rand(-20,20), Rand(-15,20));
+                       }
+               }
+               {
+                       static float counter1 = 0;
+                       counter1 -= dtime;
+                       if(counter1 < 0.0)
+                       {
+                               counter1 = 0.1*Rand(1, 30);
+                               leftdown = !leftdown;
+                               if(leftdown)
+                                       leftclicked = true;
+                               if(!leftdown)
+                                       leftreleased = true;
+                       }
+               }
+               {
+                       static float counter1 = 0;
+                       counter1 -= dtime;
+                       if(counter1 < 0.0)
+                       {
+                               counter1 = 0.1*Rand(1, 15);
+                               rightdown = !rightdown;
+                               if(rightdown)
+                                       rightclicked = true;
+                               if(!rightdown)
+                                       rightreleased = true;
+                       }
+               }
+               mousepos += mousespeed;
+       }
+       s32 Rand(s32 min, s32 max)
+       {
+               return (myrand()%(max-min+1))+min;
+       }
+ private:
+       bool keydown[KEY_KEY_CODES_COUNT];
+       v2s32 mousepos;
+       v2s32 mousespeed;
+       bool leftdown;
+       bool rightdown;
+       bool leftclicked;
+       bool rightclicked;
+       bool leftreleased;
+       bool rightreleased;
+ };
+ // These are defined global so that they're not optimized too much.
+ // Can't change them to volatile.
+ s16 temp16;
+ f32 tempf;
+ v3f tempv3f1;
+ v3f tempv3f2;
+ std::string tempstring;
+ std::string tempstring2;
+ void SpeedTests()
+ {
+       {
+               dstream<<"The following test should take around 20ms."<<std::endl;
+               TimeTaker timer("Testing std::string speed");
+               const u32 jj = 10000;
+               for(u32 j=0; j<jj; j++)
+               {
+                       tempstring = "";
+                       tempstring2 = "";
+                       const u32 ii = 10;
+                       for(u32 i=0; i<ii; i++){
+                               tempstring2 += "asd";
+                       }
+                       for(u32 i=0; i<ii+1; i++){
+                               tempstring += "asd";
+                               if(tempstring == tempstring2)
+                                       break;
+                       }
+               }
+       }
+       
+       dstream<<"All of the following tests should take around 100ms each."
+                       <<std::endl;
+       {
+               TimeTaker timer("Testing floating-point conversion speed");
+               tempf = 0.001;
+               for(u32 i=0; i<4000000; i++){
+                       temp16 += tempf;
+                       tempf += 0.001;
+               }
+       }
+       
+       {
+               TimeTaker timer("Testing floating-point vector speed");
+               tempv3f1 = v3f(1,2,3);
+               tempv3f2 = v3f(4,5,6);
+               for(u32 i=0; i<10000000; i++){
+                       tempf += tempv3f1.dotProduct(tempv3f2);
+                       tempv3f2 += v3f(7,8,9);
+               }
+       }
+       {
+               TimeTaker timer("Testing core::map speed");
+               
+               core::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);
+                               tempf += 1;
+                       }
+               }
+               for(s16 y=ii-1; y>=0; y--){
+                       for(s16 x=0; x<ii; x++){
+                               tempf = map1[v2s16(x,y)];
+                       }
+               }
+       }
+       {
+               dstream<<"Around 5000/ms should do well here."<<std::endl;
+               TimeTaker timer("Testing mutex speed");
+               
+               JMutex m;
+               m.Init();
+               u32 n = 0;
+               u32 i = 0;
+               do{
+                       n += 10000;
+                       for(; i<n; i++){
+                               m.Lock();
+                               m.Unlock();
+                       }
+               }
+               // Do at least 10ms
+               while(timer.getTime() < 10);
+               u32 dtime = timer.stop();
+               u32 per_ms = n / dtime;
+               std::cout<<"Done. "<<dtime<<"ms, "
+                               <<per_ms<<"/ms"<<std::endl;
+       }
+ }
+ void drawMenuBackground(video::IVideoDriver* driver)
+ {
+       core::dimension2d<u32> screensize = driver->getScreenSize();
+               
+       video::ITexture *bgtexture =
+                       driver->getTexture(getTexturePath("mud.png").c_str());
+       if(bgtexture)
+       {
+               s32 texturesize = 128;
+               s32 tiled_y = screensize.Height / texturesize + 1;
+               s32 tiled_x = screensize.Width / texturesize + 1;
+               
+               for(s32 y=0; y<tiled_y; y++)
+               for(s32 x=0; x<tiled_x; x++)
+               {
+                       core::rect<s32> rect(0,0,texturesize,texturesize);
+                       rect += v2s32(x*texturesize, y*texturesize);
+                       driver->draw2DImage(bgtexture, rect,
+                               core::rect<s32>(core::position2d<s32>(0,0),
+                               core::dimension2di(bgtexture->getSize())),
+                               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);
+       }
+ }
+ int main(int argc, char *argv[])
+ {
+       /*
+               Initialization
+       */
+       // Set locale. This is for forcing '.' as the decimal point.
+       std::locale::global(std::locale("C"));
+       // This enables printing all characters in bitmap font
+       setlocale(LC_CTYPE, "en_US");
+       /*
+               Parse command line
+       */
+       
+       // List all allowed options
+       core::map<std::string, ValueSpec> allowed_options;
+       allowed_options.insert("help", ValueSpec(VALUETYPE_FLAG));
+       allowed_options.insert("server", ValueSpec(VALUETYPE_FLAG,
+                       "Run server directly"));
+       allowed_options.insert("config", ValueSpec(VALUETYPE_STRING,
+                       "Load configuration from specified file"));
+       allowed_options.insert("port", ValueSpec(VALUETYPE_STRING));
+       allowed_options.insert("address", ValueSpec(VALUETYPE_STRING));
+       allowed_options.insert("random-input", ValueSpec(VALUETYPE_FLAG));
+       allowed_options.insert("disable-unittests", ValueSpec(VALUETYPE_FLAG));
+       allowed_options.insert("enable-unittests", ValueSpec(VALUETYPE_FLAG));
+       allowed_options.insert("map-dir", ValueSpec(VALUETYPE_STRING));
+ #ifdef _WIN32
+       allowed_options.insert("dstream-on-stderr", ValueSpec(VALUETYPE_FLAG));
+ #endif
+       allowed_options.insert("speedtests", ValueSpec(VALUETYPE_FLAG));
+       Settings cmd_args;
+       
+       bool ret = cmd_args.parseCommandLine(argc, argv, allowed_options);
+       if(ret == false || cmd_args.getFlag("help"))
+       {
+               dstream<<"Allowed options:"<<std::endl;
+               for(core::map<std::string, ValueSpec>::Iterator
+                               i = allowed_options.getIterator();
+                               i.atEnd() == false; i++)
+               {
+                       dstream<<"  --"<<i.getNode()->getKey();
+                       if(i.getNode()->getValue().type == VALUETYPE_FLAG)
+                       {
+                       }
+                       else
+                       {
+                               dstream<<" <value>";
+                       }
+                       dstream<<std::endl;
+                       if(i.getNode()->getValue().help != NULL)
+                       {
+                               dstream<<"      "<<i.getNode()->getValue().help
+                                               <<std::endl;
+                       }
+               }
+               return cmd_args.getFlag("help") ? 0 : 1;
+       }
+       
+       /*
+               Low-level initialization
+       */
+       bool disable_stderr = false;
+ #ifdef _WIN32
+       if(cmd_args.getFlag("dstream-on-stderr") == false)
+               disable_stderr = true;
+ #endif
+       porting::signal_handler_init();
+       bool &kill = *porting::signal_handler_killstatus();
+       
+       // Initialize porting::path_data and porting::path_userdata
+       porting::initializePaths();
+       // Create user data directory
+       fs::CreateDir(porting::path_userdata);
 -      setlocale(LC_MESSAGES, "");
 -      bindtextdomain("minetest", (porting::path_userdata+"/locale").c_str());
 -      textdomain("minetest");
++      init_gettext((porting::path_data+"/../locale").c_str());
+       
+       // Initialize debug streams
+ #ifdef RUN_IN_PLACE
+       std::string debugfile = DEBUGFILE;
+ #else
+       std::string debugfile = porting::path_userdata+"/"+DEBUGFILE;
+ #endif
+       debugstreams_init(disable_stderr, debugfile.c_str());
+       // Initialize debug stacks
+       debug_stacks_init();
+       DSTACK(__FUNCTION_NAME);
+       // Init material properties table
+       //initializeMaterialProperties();
+       // Debug handler
+       BEGIN_DEBUG_EXCEPTION_HANDLER
+       // Print startup message
 -      dstream<<DTIME<<"minetest-c55"
++      dstream<<DTIME<<PROJECT_NAME
+                       " with SER_FMT_VER_HIGHEST="<<(int)SER_FMT_VER_HIGHEST
+                       <<", "<<BUILD_INFO
+                       <<std::endl;
+       
+       /*
+               Basic initialization
+       */
+       // Initialize default settings
+       set_default_settings();
+       
+       // Initialize sockets
+       sockets_init();
+       atexit(sockets_cleanup);
+       
+       /*
+               Read config file
+       */
+       
+       // Path of configuration file in use
+       std::string configpath = "";
+       
+       if(cmd_args.exists("config"))
+       {
+               bool r = g_settings.readConfigFile(cmd_args.get("config").c_str());
+               if(r == false)
+               {
+                       dstream<<"Could not read configuration from \""
+                                       <<cmd_args.get("config")<<"\""<<std::endl;
+                       return 1;
+               }
+               configpath = cmd_args.get("config");
+       }
+       else
+       {
+               core::array<std::string> filenames;
+               filenames.push_back(porting::path_userdata + "/minetest.conf");
+ #ifdef RUN_IN_PLACE
+               filenames.push_back(porting::path_userdata + "/../minetest.conf");
+ #endif
+               for(u32 i=0; i<filenames.size(); i++)
+               {
+                       bool r = g_settings.readConfigFile(filenames[i].c_str());
+                       if(r)
+                       {
+                               configpath = filenames[i];
+                               break;
+                       }
+               }
+               
+               // If no path found, use the first one (menu creates the file)
+               if(configpath == "")
+                       configpath = filenames[0];
+       }
+       // Initialize random seed
+       srand(time(0));
+       mysrand(time(0));
+       /*
+               Pre-initialize some stuff with a dummy irrlicht wrapper.
+               These are needed for unit tests at least.
+       */
+       
+       // Initial call with g_texturesource not set.
+       init_mapnode();
+       /*
+               Run unit tests
+       */
+       if((ENABLE_TESTS && cmd_args.getFlag("disable-unittests") == false)
+                       || cmd_args.getFlag("enable-unittests") == true)
+       {
+               run_tests();
+       }
+       
+       /*for(s16 y=-100; y<100; y++)
+       for(s16 x=-100; x<100; x++)
+       {
+               std::cout<<noise2d_gradient((double)x/10,(double)y/10, 32415)<<std::endl;
+       }
+       return 0;*/
+       
+       /*
+               Game parameters
+       */
+       // Port
+       u16 port = 30000;
+       if(cmd_args.exists("port"))
+               port = cmd_args.getU16("port");
+       else if(g_settings.exists("port"))
+               port = g_settings.getU16("port");
+       if(port == 0)
+               port = 30000;
+       
+       // Map directory
+       std::string map_dir = porting::path_userdata+"/world";
+       if(cmd_args.exists("map-dir"))
+               map_dir = cmd_args.get("map-dir");
+       else if(g_settings.exists("map-dir"))
+               map_dir = g_settings.get("map-dir");
+       
+       // Run dedicated server if asked to
+       if(cmd_args.getFlag("server"))
+       {
+               DSTACK("Dedicated server branch");
+               // Create time getter
+               g_timegetter = new SimpleTimeGetter();
+               
+               // Create server
+               Server server(map_dir.c_str());
+               server.start(port);
+               
+               // Run server
+               dedicated_server_loop(server, kill);
+               return 0;
+       }
+       /*
+               More parameters
+       */
+       
+       // Address to connect to
+       std::string address = "";
+       
+       if(cmd_args.exists("address"))
+       {
+               address = cmd_args.get("address");
+       }
+       else
+       {
+               address = g_settings.get("address");
+       }
+       
+       std::string playername = g_settings.get("name");
+       /*
+               Device initialization
+       */
+       // Resolution selection
+       
+       bool fullscreen = false;
+       u16 screenW = g_settings.getU16("screenW");
+       u16 screenH = g_settings.getU16("screenH");
+       // Determine driver
+       video::E_DRIVER_TYPE driverType;
+       
+       std::string driverstring = g_settings.get("video_driver");
+       if(driverstring == "null")
+               driverType = video::EDT_NULL;
+       else if(driverstring == "software")
+               driverType = video::EDT_SOFTWARE;
+       else if(driverstring == "burningsvideo")
+               driverType = video::EDT_BURNINGSVIDEO;
+       else if(driverstring == "direct3d8")
+               driverType = video::EDT_DIRECT3D8;
+       else if(driverstring == "direct3d9")
+               driverType = video::EDT_DIRECT3D9;
+       else if(driverstring == "opengl")
+               driverType = video::EDT_OPENGL;
+       else
+       {
+               dstream<<"WARNING: Invalid video_driver specified; defaulting "
+                               "to opengl"<<std::endl;
+               driverType = video::EDT_OPENGL;
+       }
+       /*
+               Create device and exit if creation failed
+       */
+       MyEventReceiver receiver;
+       IrrlichtDevice *device;
+       device = createDevice(driverType,
+                       core::dimension2d<u32>(screenW, screenH),
+                       16, fullscreen, false, false, &receiver);
+       if (device == 0)
+               return 1; // could not create selected driver.
+       
+       // Set device in game parameters
+       device = device;
  
        // Set the window caption
        device->setWindowCaption(L"Minetest [Main Menu]");
diff --cc src/map.cpp
index e1769b8eff897718ace3b053b00cdf9c20a3bd08,83062706694022984f5ae077c86d29e9c6c1c11b..092ce97fdcf030cca3b60c8af40a9b09a0b6a0fe
@@@ -1570,220 -1559,240 +1570,220 @@@ void Map::transformLiquids(core::map<v3
                v3s16 p0 = m_transforming_liquid.pop_front();
  
                MapNode n0 = getNodeNoEx(p0);
 -
 -              // Don't deal with non-liquids
 -              if(content_liquid(n0.getContent()) == false)
 -                      continue;
 -
 -              bool is_source = !content_flowing_liquid(n0.getContent());
 -
 -              u8 liquid_level = 8;
 -              if(is_source == false)
 -                      liquid_level = n0.param2 & 0x0f;
 -
 -              // Turn possible source into non-source
 -              u8 nonsource_c = make_liquid_flowing(n0.getContent());
 -
 +                              
                /*
 -                      If not source, check that some node flows into this one
 -                      and what is the level of liquid in this one
 -              */
 -              if(is_source == false)
 -              {
 -                      s8 new_liquid_level_max = -1;
 -
 -                      v3s16 dirs_from[5] = {
 -                              v3s16(0,1,0), // top
 -                              v3s16(0,0,1), // back
 -                              v3s16(1,0,0), // right
 -                              v3s16(0,0,-1), // front
 -                              v3s16(-1,0,0), // left
 -                      };
 -                      for(u16 i=0; i<5; i++)
 -                      {
 -                              bool from_top = (i==0);
 -
 -                              v3s16 p2 = p0 + dirs_from[i];
 -                              MapNode n2 = getNodeNoEx(p2);
 -
 -                              if(content_liquid(n2.getContent()))
 -                              {
 -                                      u8 n2_nonsource_c = make_liquid_flowing(n2.getContent());
 -                                      // Check that the liquids are the same type
 -                                      if(n2_nonsource_c != nonsource_c)
 -                                      {
 -                                              dstream<<"WARNING: Not handling: different liquids"
 -                                                              " collide"<<std::endl;
 -                                              continue;
 +                      Collect information about current node
 +               */
 +              s8 liquid_level = -1;
 +              u8 liquid_kind = CONTENT_IGNORE;
-               LiquidType liquid_type = content_features(n0.d).liquid_type;
++              LiquidType liquid_type = content_features(n0.getContent()).liquid_type;
 +              switch (liquid_type) {
 +                      case LIQUID_SOURCE:
 +                              liquid_level = 8;
-                               liquid_kind = content_features(n0.d).liquid_alternative_flowing;
++                              liquid_kind = content_features(n0.getContent()).liquid_alternative_flowing;
 +                              break;
 +                      case LIQUID_FLOWING:
 +                              liquid_level = (n0.param2 & LIQUID_LEVEL_MASK);
-                               liquid_kind = n0.d;
++                              liquid_kind = n0.getContent();
 +                              break;
 +                      case LIQUID_NONE:
 +                              // if this is an air node, it *could* be transformed into a liquid. otherwise,
 +                              // continue with the next node.
-                               if (n0.d != CONTENT_AIR)
++                              if (n0.getContent() != CONTENT_AIR)
 +                                      continue;
 +                              liquid_kind = CONTENT_AIR;
 +                              break;
 +              }
 +              
 +              /*
 +                      Collect information about the environment
 +               */
 +              v3s16 dirs[6] = {
 +                      v3s16( 0, 1, 0), // top
 +                      v3s16( 0,-1, 0), // bottom
 +                      v3s16( 1, 0, 0), // right
 +                      v3s16(-1, 0, 0), // left
 +                      v3s16( 0, 0, 1), // back
 +                      v3s16( 0, 0,-1), // front
 +              };
 +              NodeNeighbor sources[6]; // surrounding sources
 +              int num_sources = 0;
 +              NodeNeighbor flows[6]; // surrounding flowing liquid nodes
 +              int num_flows = 0;
 +              NodeNeighbor airs[6]; // surrounding air
 +              int num_airs = 0;
 +              NodeNeighbor neutrals[6]; // nodes that are solid or another kind of liquid
 +              int num_neutrals = 0;
 +              bool flowing_down = false;
 +              for (u16 i = 0; i < 6; i++) {
 +                      NeighborType nt = NEIGHBOR_SAME_LEVEL;
 +                      switch (i) {
 +                              case 0:
 +                                      nt = NEIGHBOR_UPPER;
 +                                      break;
 +                              case 1:
 +                                      nt = NEIGHBOR_LOWER;
 +                                      break;
 +                      }
 +                      v3s16 npos = p0 + dirs[i];
 +                      NodeNeighbor nb = {getNodeNoEx(npos), nt, npos};
-                       switch (content_features(nb.n.d).liquid_type) {
++                      switch (content_features(nb.n.getContent()).liquid_type) {
 +                              case LIQUID_NONE:
-                                       if (nb.n.d == CONTENT_AIR) {
++                                      if (nb.n.getContent() == CONTENT_AIR) {
 +                                              airs[num_airs++] = nb;
 +                                              // if the current node happens to be a flowing node, it will start to flow down here.
 +                                              if (nb.t == NEIGHBOR_LOWER)
 +                                                      flowing_down = true;
 +                                      } else {
 +                                              neutrals[num_neutrals++] = nb;
                                        }
 -                                      bool n2_is_source = !content_flowing_liquid(n2.getContent());
 -                                      s8 n2_liquid_level = 8;
 -                                      if(n2_is_source == false)
 -                                              n2_liquid_level = n2.param2 & 0x07;
 -
 -                                      s8 new_liquid_level = -1;
 -                                      if(from_top)
 -                                      {
 -                                              //new_liquid_level = 7;
 -                                              if(n2_liquid_level >= 7 - WATER_DROP_BOOST)
 -                                                      new_liquid_level = 7;
 -                                              else
 -                                                      new_liquid_level = n2_liquid_level + WATER_DROP_BOOST;
 +                                      break;
 +                              case LIQUID_SOURCE:
 +                                      // if this node is not (yet) of a liquid type, choose the first liquid type we encounter 
 +                                      if (liquid_kind == CONTENT_AIR)
-                                               liquid_kind = content_features(nb.n.d).liquid_alternative_flowing;
-                                       if (content_features(nb.n.d).liquid_alternative_flowing !=liquid_kind) {
++                                              liquid_kind = content_features(nb.n.getContent()).liquid_alternative_flowing;
++                                      if (content_features(nb.n.getContent()).liquid_alternative_flowing !=liquid_kind) {
 +                                              neutrals[num_neutrals++] = nb;
 +                                      } else {
 +                                              sources[num_sources++] = nb;
                                        }
 -                                      else if(n2_liquid_level > 0)
 -                                      {
 -                                              new_liquid_level = n2_liquid_level - 1;
 +                                      break;
 +                              case LIQUID_FLOWING:
 +                                      // if this node is not (yet) of a liquid type, choose the first liquid type we encounter
 +                                      if (liquid_kind == CONTENT_AIR)
-                                               liquid_kind = content_features(nb.n.d).liquid_alternative_flowing;
-                                       if (content_features(nb.n.d).liquid_alternative_flowing != liquid_kind) {
++                                              liquid_kind = content_features(nb.n.getContent()).liquid_alternative_flowing;
++                                      if (content_features(nb.n.getContent()).liquid_alternative_flowing != liquid_kind) {
 +                                              neutrals[num_neutrals++] = nb;
 +                                      } else {
 +                                              flows[num_flows++] = nb;
 +                                              if (nb.t == NEIGHBOR_LOWER)
 +                                                      flowing_down = true;
                                        }
 -
 -                                      if(new_liquid_level > new_liquid_level_max)
 -                                              new_liquid_level_max = new_liquid_level;
 -                              }
 -                      } //for
 -
 -                      /*
 -                              If liquid level should be something else, update it and
 -                              add all the neighboring water nodes to the transform queue.
 -                      */
 -                      if(new_liquid_level_max != liquid_level)
 -                      {
 -                              if(new_liquid_level_max == -1)
 -                              {
 -                                      // Remove water alltoghether
 -                                      n0.setContent(CONTENT_AIR);
 -                                      n0.param2 = 0;
 -                                      setNode(p0, n0);
 -                              }
 -                              else
 -                              {
 -                                      n0.param2 = new_liquid_level_max;
 -                                      setNode(p0, n0);
 -                              }
 -
 -                              // Block has been modified
 -                              {
 -                                      v3s16 blockpos = getNodeBlockPos(p0);
 -                                      MapBlock *block = getBlockNoCreateNoEx(blockpos);
 -                                      if(block != NULL)
 -                                              modified_blocks.insert(blockpos, block);
 +                                      break;
 +                      }
 +              }
 +              
 +              /*
 +                      decide on the type (and possibly level) of the current node
 +               */
 +              u8 new_node_content;
 +              s8 new_node_level = -1;
 +              if (num_sources >= 2 || liquid_type == LIQUID_SOURCE) {
 +                      // liquid_kind will be set to either the flowing alternative of the node (if it's a liquid)
 +                      // or the flowing alternative of the first of the surrounding sources (if it's air), so
 +                      // it's perfectly safe to use liquid_kind here to determine the new node content.
 +                      new_node_content = content_features(liquid_kind).liquid_alternative_source;
 +              } else if (num_sources == 1 && sources[0].t != NEIGHBOR_LOWER) {
 +                      // liquid_kind is set properly, see above
 +                      new_node_content = liquid_kind;
 +                      new_node_level = 7;
 +              } else {
 +                      // no surrounding sources, so get the maximum level that can flow into this node
 +                      for (u16 i = 0; i < num_flows; i++) {
 +                              u8 nb_liquid_level = (flows[i].n.param2 & LIQUID_LEVEL_MASK);
 +                              switch (flows[i].t) {
 +                                      case NEIGHBOR_UPPER:
 +                                              if (nb_liquid_level + WATER_DROP_BOOST > new_node_level) {
 +                                                      new_node_level = 7;
 +                                                      if (nb_liquid_level + WATER_DROP_BOOST < 7)
 +                                                              new_node_level = nb_liquid_level + WATER_DROP_BOOST;
 +                                              }
 +                                              break;
 +                                      case NEIGHBOR_LOWER:
 +                                              break;
 +                                      case NEIGHBOR_SAME_LEVEL:
 +                                              if ((flows[i].n.param2 & LIQUID_FLOW_DOWN_MASK) != LIQUID_FLOW_DOWN_MASK &&
 +                                                      nb_liquid_level > 0 && nb_liquid_level - 1 > new_node_level) {
 +                                                      new_node_level = nb_liquid_level - 1;
 +                                              }
 +                                              break;
                                }
 -
 -                              /*
 -                                      Add neighboring non-source liquid nodes to transform queue.
 -                              */
 -                              v3s16 dirs[6] = {
 -                                      v3s16(0,0,1), // back
 -                                      v3s16(0,1,0), // top
 -                                      v3s16(1,0,0), // right
 -                                      v3s16(0,0,-1), // front
 -                                      v3s16(0,-1,0), // bottom
 -                                      v3s16(-1,0,0), // left
 -                              };
 -                              for(u16 i=0; i<6; i++)
 -                              {
 -                                      v3s16 p2 = p0 + dirs[i];
 -
 -                                      MapNode n2 = getNodeNoEx(p2);
 -                                      if(content_flowing_liquid(n2.getContent()))
 -                                      {
 -                                              m_transforming_liquid.push_back(p2);
 +                      }
 +                      // don't flow as far in open terrain - if there isn't at least one adjacent solid block,
 +                      // substract another unit from the resulting water level.
 +                      if (!flowing_down && new_node_level >= 1) {
 +                              bool at_wall = false;
 +                              for (u16 i = 0; i < num_neutrals; i++) {
 +                                      if (neutrals[i].t == NEIGHBOR_SAME_LEVEL) {
 +                                              at_wall = true;
 +                                              break;
                                        }
                                }
 +                              if (!at_wall)
 +                                      new_node_level -= 1;
                        }
 +                      
 +                      if (new_node_level >= 0)
 +                              new_node_content = liquid_kind;
 +                      else
 +                              new_node_content = CONTENT_AIR;
                }
 -
 -              // Get a new one from queue if the node has turned into non-water
 -              if(content_liquid(n0.getContent()) == false)
 +              
 +              /*
 +                      check if anything has changed. if not, just continue with the next node.
 +               */
-               if (new_node_content == n0.d && (content_features(n0.d).liquid_type != LIQUID_FLOWING ||
++              if (new_node_content == n0.getContent() && (content_features(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)))
                        continue;
 -
 +              
 +              
                /*
 -                      Flow water from this node
 -              */
 -              v3s16 dirs_to[5] = {
 -                      v3s16(0,-1,0), // bottom
 -                      v3s16(0,0,1), // back
 -                      v3s16(1,0,0), // right
 -                      v3s16(0,0,-1), // front
 -                      v3s16(-1,0,0), // left
 -              };
 -              for(u16 i=0; i<5; i++)
 -              {
 -                      bool to_bottom = (i == 0);
 -
 -                      // If liquid is at lowest possible height, it's not going
 -                      // anywhere except down
 -                      if(liquid_level == 0 && to_bottom == false)
 -                              continue;
 -
 -                      u8 liquid_next_level = 0;
 -                      // If going to bottom
 -                      if(to_bottom)
 -                      {
 -                              //liquid_next_level = 7;
 -                              if(liquid_level >= 7 - WATER_DROP_BOOST)
 -                                      liquid_next_level = 7;
 -                              else
 -                                      liquid_next_level = liquid_level + WATER_DROP_BOOST;
 -                      }
 -                      else
 -                              liquid_next_level = liquid_level - 1;
 -
 -                      bool n2_changed = false;
 -                      bool flowed = false;
 -
 -                      v3s16 p2 = p0 + dirs_to[i];
 -
 -                      MapNode n2 = getNodeNoEx(p2);
 -                      //dstream<<"[1] n2.param="<<(int)n2.param<<std::endl;
 -
 -                      if(content_liquid(n2.getContent()))
 -                      {
 -                              u8 n2_nonsource_c = make_liquid_flowing(n2.getContent());
 -                              // Check that the liquids are the same type
 -                              if(n2_nonsource_c != nonsource_c)
 -                              {
 -                                      dstream<<"WARNING: Not handling: different liquids"
 -                                                      " collide"<<std::endl;
 -                                      continue;
 -                              }
 -                              bool n2_is_source = !content_flowing_liquid(n2.getContent());
 -                              u8 n2_liquid_level = 8;
 -                              if(n2_is_source == false)
 -                                      n2_liquid_level = n2.param2 & 0x07;
 -
 -                              if(to_bottom)
 -                              {
 -                                      flowed = true;
 -                              }
 -
 -                              if(n2_is_source)
 -                              {
 -                                      // Just flow into the source, nothing changes.
 -                                      // n2_changed is not set because destination didn't change
 -                                      flowed = true;
 +                      update the current node
 +               */
 +              bool flow_down_enabled = (flowing_down && ((n0.param2 & LIQUID_FLOW_DOWN_MASK) != LIQUID_FLOW_DOWN_MASK));
-               n0.d = new_node_content;
-               if (content_features(n0.d).liquid_type == LIQUID_FLOWING) {
++              n0.setContent(new_node_content);
++              if (content_features(n0.getContent()).liquid_type == LIQUID_FLOWING) {
 +                      // set level to last 3 bits, flowing down bit to 4th bit
 +                      n0.param2 = (flowing_down ? LIQUID_FLOW_DOWN_MASK : 0x00) | (new_node_level & LIQUID_LEVEL_MASK);
 +              } else {
 +                      n0.param2 = 0;
 +              }
 +              setNode(p0, n0);
 +              v3s16 blockpos = getNodeBlockPos(p0);
 +              MapBlock *block = getBlockNoCreateNoEx(blockpos);
 +              if(block != NULL)
 +                      modified_blocks.insert(blockpos, block);
 +              
 +              /*
 +                      enqueue neighbors for update if neccessary
 +               */
-               switch (content_features(n0.d).liquid_type) {
++              switch (content_features(n0.getContent()).liquid_type) {
 +                      case LIQUID_SOURCE:
 +                              // make sure source flows into all neighboring nodes
 +                              for (u16 i = 0; i < num_flows; i++)
 +                                      if (flows[i].t != NEIGHBOR_UPPER)
 +                                              m_transforming_liquid.push_back(flows[i].p);
 +                              for (u16 i = 0; i < num_airs; i++)
 +                                      if (airs[i].t != NEIGHBOR_UPPER)
 +                                              m_transforming_liquid.push_back(airs[i].p);
 +                              break;
 +                      case LIQUID_NONE:
 +                              // this flow has turned to air; neighboring flows might need to do the same
 +                              for (u16 i = 0; i < num_flows; i++)
 +                                      m_transforming_liquid.push_back(flows[i].p);
 +                              break;
 +                      case LIQUID_FLOWING:
 +                              for (u16 i = 0; i < num_flows; i++) {
 +                                      u8 flow_level = (flows[i].n.param2 & LIQUID_LEVEL_MASK);
 +                                      // liquid_level is still the ORIGINAL level of this node.
 +                                      if (flows[i].t != NEIGHBOR_UPPER && ((flow_level < liquid_level || flow_level < new_node_level) ||
 +                                              flow_down_enabled))
 +                                              m_transforming_liquid.push_back(flows[i].p);
                                }
 -                              else
 -                              {
 -                                      if(liquid_next_level > liquid_level)
 -                                      {
 -                                              n2.param2 = liquid_next_level;
 -                                              setNode(p2, n2);
 -
 -                                              n2_changed = true;
 -                                              flowed = true;
 -                                      }
 +                              for (u16 i = 0; i < num_airs; i++) {
 +                                      if (airs[i].t != NEIGHBOR_UPPER && (airs[i].t == NEIGHBOR_LOWER || new_node_level > 0))
 +                                              m_transforming_liquid.push_back(airs[i].p);
                                }
 -                      }
 -                      else if(n2.getContent() == CONTENT_AIR)
 -                      {
 -                              n2.setContent(nonsource_c);
 -                              n2.param2 = liquid_next_level;
 -                              setNode(p2, n2);
 -
 -                              n2_changed = true;
 -                              flowed = true;
 -                      }
 -
 -                      //dstream<<"[2] n2.param="<<(int)n2.param<<std::endl;
 -
 -                      if(n2_changed)
 -                      {
 -                              m_transforming_liquid.push_back(p2);
 -
 -                              v3s16 blockpos = getNodeBlockPos(p2);
 -                              MapBlock *block = getBlockNoCreateNoEx(blockpos);
 -                              if(block != NULL)
 -                                      modified_blocks.insert(blockpos, block);
 -                      }
 -
 -                      // If n2_changed to bottom, don't flow anywhere else
 -                      if(to_bottom && flowed && !is_source)
                                break;
                }
 -
 +              
                loopcount++;
                //if(loopcount >= 100000)
 -              if(loopcount >= initial_size * 1)
 +              if(loopcount >= initial_size * 10) {
                        break;
 +              }
        }
        //dstream<<"Map::transformLiquids(): loopcount="<<loopcount<<std::endl;
  }
index 647a177564c2a1b088eb2820aebe84cf4b98afdf,b7981348c9f50f3384ea11498a87d9760f842cd6..44ca75ff0b794ab66393c8329ab70ffd9b153de3
@@@ -862,5 -867,128 +867,130 @@@ void MapBlock::deSerializeDiskExtra(std
        }
  }
  
+ /*
+       Get a quick string to describe what a block actually contains
+ */
+ std::string analyze_block(MapBlock *block)
+ {
+       if(block == NULL)
+       {
+               return "NULL";
+       }
+       std::ostringstream desc;
+       
+       v3s16 p = block->getPos();
+       char spos[20];
+       snprintf(spos, 20, "(%2d,%2d,%2d), ", p.X, p.Y, p.Z);
+       desc<<spos;
+       
+       switch(block->getModified())
+       {
+       case MOD_STATE_CLEAN:
+               desc<<"CLEAN,           ";
+               break;
+       case MOD_STATE_WRITE_AT_UNLOAD:
+               desc<<"WRITE_AT_UNLOAD, ";
+               break;
+       case MOD_STATE_WRITE_NEEDED:
+               desc<<"WRITE_NEEDED,    ";
+               break;
+       default:
+               desc<<"unknown getModified()="+itos(block->getModified())+", ";
+       }
+       if(block->isGenerated())
+               desc<<"is_gen [X], ";
+       else
+               desc<<"is_gen [ ], ";
+       if(block->getIsUnderground())
+               desc<<"is_ug [X], ";
+       else
+               desc<<"is_ug [ ], ";
++#ifndef SERVER
+       if(block->getMeshExpired())
+               desc<<"mesh_exp [X], ";
+       else
+               desc<<"mesh_exp [ ], ";
++#endif
+       if(block->getLightingExpired())
+               desc<<"lighting_exp [X], ";
+       else
+               desc<<"lighting_exp [ ], ";
+       if(block->isDummy())
+       {
+               desc<<"Dummy, ";
+       }
+       else
+       {
+               // We'll just define the numbers here, don't want to include
+               // content_mapnode.h
+               const content_t content_water = 2;
+               const content_t content_watersource = 9;
+               const content_t content_tree = 0x801;
+               const content_t content_leaves = 0x802;
+               const content_t content_jungletree = 0x815;
+               bool full_ignore = true;
+               bool some_ignore = false;
+               bool full_air = true;
+               bool some_air = false;
+               bool trees = false;
+               bool water = false;
+               for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
+               for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
+               for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
+               {
+                       v3s16 p(x0,y0,z0);
+                       MapNode n = block->getNode(p);
+                       content_t c = n.getContent();
+                       if(c == CONTENT_IGNORE)
+                               some_ignore = true;
+                       else
+                               full_ignore = false;
+                       if(c == CONTENT_AIR)
+                               some_air = true;
+                       else
+                               full_air = false;
+                       if(c == content_tree || c == content_jungletree
+                                       || c == content_leaves)
+                               trees = true;
+                       if(c == content_water
+                                       || c == content_watersource)
+                               water = true;
+               }
+               
+               desc<<"content {";
+               
+               std::ostringstream ss;
+               
+               if(full_ignore)
+                       ss<<"IGNORE (full), ";
+               else if(some_ignore)
+                       ss<<"IGNORE, ";
+               
+               if(full_air)
+                       ss<<"AIR (full), ";
+               else if(some_air)
+                       ss<<"AIR, ";
+               if(trees)
+                       ss<<"trees, ";
+               if(water)
+                       ss<<"water, ";
+               
+               if(ss.str().size()>=2)
+                       desc<<ss.str().substr(0, ss.str().size()-2);
+               desc<<"}, ";
+       }
+       return desc.str().substr(0, desc.str().size()-2);
+ }
  
  //END
diff --cc src/mapnode.h
Simple merge