From: Nils Dagsson Moskopp Date: Sat, 30 Jul 2011 16:53:54 +0000 (+0200) Subject: Merge remote-tracking branch 'origin/upstream' X-Git-Url: http://81.2.79.47:8989/gitweb/?a=commitdiff_plain;h=4ef9c7675ada82961d83601712a47ebad78b67b5;p=zefram%2Fminetest%2Fminetest_engine.git Merge remote-tracking branch 'origin/upstream' --- 4ef9c7675ada82961d83601712a47ebad78b67b5 diff --cc src/main.cpp index bcca60d9,09c29900..da17b84b --- a/src/main.cpp +++ b/src/main.cpp @@@ -1,1353 -1,1384 +1,1382 @@@ - /* - Minetest-c55 - Copyright (C) 2010-2011 celeron55, Perttu Ahola - - 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. - - 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 - - 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 - - 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 - - TODO: Restart irrlicht completely when coming back to main menu from game. - - This gets rid of everything that is stored in irrlicht's caches. - - TODO: Merge bahamada's audio stuff (clean patch available) - - TODO: Merge key configuration menu (no clean patch available) - - 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 - #include - #include - #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"<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 map1; - tempf = -324; - const s16 ii=300; - for(s16 y=0; y=0; y--){ - for(s16 x=0; x 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 rect(0,0,texturesize,texturesize); - rect += v2s32(x*texturesize, y*texturesize); - driver->draw2DImage(bgtexture, rect, - core::rect(core::position2d(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 bgrect(0, screensize.Height-logosize.Y-20, - screensize.Width, screensize.Height); - driver->draw2DRectangle(bgcolor, bgrect, NULL); - - core::rect 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(core::position2d(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 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:"<::Iterator - i = allowed_options.getIterator(); - i.atEnd() == false; i++) - { - dstream<<" --"<getKey(); - if(i.getNode()->getValue().type == VALUETYPE_FLAG) - { - } - else - { - dstream<<" "; - } - dstream<getValue().help != NULL) - { - dstream<<" "<getValue().help - < 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(screenW, screenH), - 16, fullscreen, false, false, &receiver); - - if (device == 0) - return 1; // could not create selected driver. - - // Set device in game parameters - device = device; + /* + Minetest-c55 + Copyright (C) 2010-2011 celeron55, Perttu Ahola + + 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 + #include + #include + #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"<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 map1; + tempf = -324; + const s16 ii=300; + for(s16 y=0; y=0; y--){ + for(s16 x=0; x 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 rect(0,0,texturesize,texturesize); + rect += v2s32(x*texturesize, y*texturesize); + driver->draw2DImage(bgtexture, rect, + core::rect(core::position2d(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 bgrect(0, screensize.Height-logosize.Y-20, + screensize.Width, screensize.Height); + driver->draw2DRectangle(bgcolor, bgrect, NULL); + + core::rect 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(core::position2d(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 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:"<::Iterator + i = allowed_options.getIterator(); + i.atEnd() == false; i++) + { + dstream<<" --"<getKey(); + if(i.getNode()->getValue().type == VALUETYPE_FLAG) + { + } + else + { + dstream<<" "; + } + dstream<getValue().help != NULL) + { + dstream<<" "<getValue().help + < 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(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 e1769b8e,83062706..092ce97f --- a/src/map.cpp +++ b/src/map.cpp @@@ -1570,220 -1559,240 +1570,220 @@@ void Map::transformLiquids(core::map= 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< 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<= 100000) - if(loopcount >= initial_size * 1) + if(loopcount >= initial_size * 10) { break; + } } //dstream<<"Map::transformLiquids(): loopcount="<getPos(); + char spos[20]; + snprintf(spos, 20, "(%2d,%2d,%2d), ", p.X, p.Y, p.Z); + desc<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; z0getNode(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<