map unloading is now a whole lot better
authorPerttu Ahola <celeron55@gmail.com>
Sun, 26 Jun 2011 21:27:17 +0000 (00:27 +0300)
committerPerttu Ahola <celeron55@gmail.com>
Sun, 26 Jun 2011 21:27:17 +0000 (00:27 +0300)
minetest.conf.example
src/client.cpp
src/client.h
src/clientserver.h
src/defaultsettings.cpp
src/environment.cpp
src/main.cpp
src/map.cpp
src/map.h
src/server.cpp
src/server.h

index 6e8a82bacc1a6c62b0a72095b8bb0cd4b9d26fcb..f94c8dee59cccef0b0394a0e5d15b09de0fb2e09 100644 (file)
@@ -9,6 +9,10 @@
 #
 # Further documentation:
 # http://celeron.55.lt/~celeron55/minetest/wiki/doku.php
+#
+# NOTE: This file might not be up-to-date, refer to the
+#       defaultsettings.cpp file for an up-to-date list:
+# https://bitbucket.org/celeron55/minetest/src/tip/src/defaultsettings.cpp
 
 #
 # Client side stuff
@@ -92,7 +96,7 @@
 #random_input = false
 
 # Timeout for client to remove unused map data from memory
-#client_delete_unused_sectors_timeout = 1200
+#client_unload_unused_data_timeout = 1200
 
 #
 # Server side stuff
 #time_speed = 1440
 
 #time_send_interval = 5
-#server_unload_unused_sectors_timeout = 60
+#server_unload_unused_data_timeout = 60
 #server_map_save_interval = 60
 
index 449b0c2f26ccaedf024a85158120fd100633b10d..585fce11c3557624ac14ae72ef80e23f1da2bb67 100644 (file)
@@ -199,7 +199,7 @@ Client::Client(
        m_access_denied(false)
 {
        m_packetcounter_timer = 0.0;
-       m_delete_unused_sectors_timer = 0.0;
+       //m_delete_unused_sectors_timer = 0.0;
        m_connection_reinit_timer = 0.0;
        m_avg_rtt_timer = 0.0;
        m_playerpos_send_timer = 0.0;
@@ -303,7 +303,11 @@ void Client::step(float dtime)
                        m_packetcounter.clear();
                }
        }
+       
+       // Get connection status
+       bool connected = connectedAndInitialized();
 
+#if 0
        {
                /*
                        Delete unused sectors
@@ -324,8 +328,7 @@ void Client::step(float dtime)
 
                        core::list<v3s16> deleted_blocks;
 
-                       float delete_unused_sectors_timeout = 
-                               g_settings.getFloat("client_delete_unused_sectors_timeout");
+                               g_settings.getFloat("client_unload_unused_data_timeout");
        
                        // Delete sector blocks
                        /*u32 num = m_env.getMap().unloadUnusedData
@@ -392,8 +395,7 @@ void Client::step(float dtime)
                        }
                }
        }
-
-       bool connected = connectedAndInitialized();
+#endif
 
        if(connected == false)
        {
@@ -438,6 +440,67 @@ void Client::step(float dtime)
                Do stuff if connected
        */
        
+       /*
+               Run Map's timers and unload unused data
+       */
+       const float map_timer_and_unload_dtime = 5.25;
+       if(m_map_timer_and_unload_interval.step(dtime, map_timer_and_unload_dtime))
+       {
+               ScopeProfiler sp(&g_profiler, "Client: map timer and unload");
+               core::list<v3s16> deleted_blocks;
+               m_env.getMap().timerUpdate(map_timer_and_unload_dtime,
+                               g_settings.getFloat("client_unload_unused_data_timeout"),
+                               &deleted_blocks);
+                               
+               /*if(deleted_blocks.size() > 0)
+                       dstream<<"Client: Unloaded "<<deleted_blocks.size()
+                                       <<" unused blocks"<<std::endl;*/
+                       
+               /*
+                       Send info to server
+                       NOTE: This loop is intentionally iterated the way it is.
+               */
+
+               core::list<v3s16>::Iterator i = deleted_blocks.begin();
+               core::list<v3s16> sendlist;
+               for(;;)
+               {
+                       if(sendlist.size() == 255 || i == deleted_blocks.end())
+                       {
+                               if(sendlist.size() == 0)
+                                       break;
+                               /*
+                                       [0] u16 command
+                                       [2] u8 count
+                                       [3] v3s16 pos_0
+                                       [3+6] v3s16 pos_1
+                                       ...
+                               */
+                               u32 replysize = 2+1+6*sendlist.size();
+                               SharedBuffer<u8> reply(replysize);
+                               writeU16(&reply[0], TOSERVER_DELETEDBLOCKS);
+                               reply[2] = sendlist.size();
+                               u32 k = 0;
+                               for(core::list<v3s16>::Iterator
+                                               j = sendlist.begin();
+                                               j != sendlist.end(); j++)
+                               {
+                                       writeV3S16(&reply[2+1+6*k], *j);
+                                       k++;
+                               }
+                               m_con.Send(PEER_ID_SERVER, 1, reply, true);
+
+                               if(i == deleted_blocks.end())
+                                       break;
+
+                               sendlist.clear();
+                       }
+
+                       sendlist.push_back(*i);
+                       i++;
+               }
+       }
+
        /*
                Handle environment
        */
@@ -453,23 +516,23 @@ void Client::step(float dtime)
                //TimeTaker envtimer("env step", m_device);
                // Step environment
                m_env.step(dtime);
-
-               // Step active blocks
+               
+               /*
+                       Handle active blocks
+                       NOTE: These old objects are DEPRECATED. TODO: Remove
+               */
                for(core::map<v3s16, bool>::Iterator
                                i = m_active_blocks.getIterator();
                                i.atEnd() == false; i++)
                {
                        v3s16 p = i.getNode()->getKey();
 
-                       MapBlock *block = NULL;
-                       try
-                       {
-                               block = m_env.getMap().getBlockNoCreate(p);
-                               block->stepObjects(dtime, false, m_env.getDayNightRatio());
-                       }
-                       catch(InvalidPositionException &e)
-                       {
-                       }
+                       MapBlock *block = m_env.getMap().getBlockNoCreateNoEx(p);
+                       if(block == NULL)
+                               continue;
+                       
+                       // Step MapBlockObjects
+                       block->stepObjects(dtime, false, m_env.getDayNightRatio());
                }
 
                /*
@@ -1183,6 +1246,7 @@ void Client::ProcessData(u8 *data, u32 datasize, u16 sender_peer_id)
 
                /*
                        Read block objects
+                       NOTE: Deprecated stuff here, TODO: Remove
                */
 
                // Read active block count
index 442eaef5df9282ac460bffb9743f6e8738394dba..bd838fee08165778657de65d1a4e84ae6d036ee6 100644 (file)
@@ -28,6 +28,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include "jmutex.h"
 #include <ostream>
 #include "clientobject.h"
+#include "utility.h" // For IntervalLimiter
 
 struct MeshMakeData;
 
@@ -306,11 +307,11 @@ private:
        void sendPlayerInfo();
        
        float m_packetcounter_timer;
-       float m_delete_unused_sectors_timer;
        float m_connection_reinit_timer;
        float m_avg_rtt_timer;
        float m_playerpos_send_timer;
        float m_ignore_damage_timer; // Used after server moves player
+       IntervalLimiter m_map_timer_and_unload_interval;
 
        MeshUpdateThread m_mesh_update_thread;
        
index 7972762c05af82eccdbf9be60b0026c530a42fc9..35484fe76c47b970d6d81ecf3d6d4310ba47ad40 100644 (file)
@@ -37,7 +37,7 @@ enum ToClientCommand
                [0] u16 TOSERVER_INIT
                [2] u8 deployed version
                [3] v3s16 player's position + v3f(0,BS/2,0) floatToInt'd 
-               ([4] u64 map seed (new as of 2011-02-27))
+               [12] u64 map seed (new as of 2011-02-27)
 
                NOTE: The position in here is deprecated; position is
                      explicitly sent afterwards
index 99bead6b0f8228529c151eef4c141e3c6165100a..0213ede1f38c08f3a3ee0e804c6cd9efb6810c4f 100644 (file)
@@ -56,7 +56,7 @@ void set_default_settings()
        g_settings.setDefault("screenH", "600");
        g_settings.setDefault("address", "");
        g_settings.setDefault("random_input", "false");
-       g_settings.setDefault("client_delete_unused_sectors_timeout", "1200");
+       g_settings.setDefault("client_unload_unused_data_timeout", "1200");
        g_settings.setDefault("enable_fog", "true");
        g_settings.setDefault("new_style_water", "false");
        g_settings.setDefault("new_style_leaves", "true");
@@ -94,7 +94,7 @@ void set_default_settings()
        g_settings.setDefault("max_block_generate_distance", "8");
        g_settings.setDefault("time_send_interval", "20");
        g_settings.setDefault("time_speed", "96");
-       g_settings.setDefault("server_unload_unused_sectors_timeout", "60");
+       g_settings.setDefault("server_unload_unused_data_timeout", "60");
        g_settings.setDefault("server_map_save_interval", "60");
        g_settings.setDefault("full_block_send_enable_min_time_from_building", "2.0");
        //g_settings.setDefault("dungeon_rarity", "0.025");
index b52a46ddedb1b33669430ad6c9bb4dd7c5ce18db..ac69c8ae28798690d3fb9f6b4cefe05f09fba086 100644 (file)
@@ -660,14 +660,6 @@ void ServerEnvironment::step(float dtime)
                m_game_time_fraction_counter -= (float)inc_i;
        }
        
-       /*
-               Let map update it's timers
-       */
-       {
-               //TimeTaker timer("Server m_map->timerUpdate()");
-               m_map->timerUpdate(dtime);
-       }
-
        /*
                Handle players
        */
@@ -1469,11 +1461,6 @@ void ClientEnvironment::step(float dtime)
        bool free_move = g_settings.getBool("free_move");
        bool footprints = g_settings.getBool("footprints");
 
-       {
-               //TimeTaker timer("Client m_map->timerUpdate()");
-               m_map->timerUpdate(dtime);
-       }
-       
        // Get local player
        LocalPlayer *lplayer = getLocalPlayer();
        assert(lplayer);
@@ -1672,7 +1659,7 @@ void ClientEnvironment::step(float dtime)
                // Step object
                obj->step(dtime, this);
 
-               if(m_active_object_light_update_interval.step(dtime, 0.5))
+               if(m_active_object_light_update_interval.step(dtime, 0.21))
                {
                        // Update lighting
                        //u8 light = LIGHT_MAX;
index 9fb17e2112c74db887615684ecf3a0c36bb7ddcf..65d6006e3bf1f1ac3c3991e5c1e9c5b71c9390fc 100644 (file)
@@ -27,6 +27,33 @@ NOTE: Global locale is now set at initialization
 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
@@ -73,9 +100,6 @@ SUGG: Make the amount of blocks sending to client and the total
 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: Calculate lighting per vertex to get a lighting effect like in\r
-      bartwe's game\r
-\r
 SUGG: Background music based on cellular automata?\r
       http://www.earslap.com/projectslab/otomata\r
 \r
@@ -90,6 +114,8 @@ SUGG: Make a system for pregenerating quick information for mapblocks, so
          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
@@ -231,6 +257,7 @@ FIXME: Server sometimes goes into some infinite PeerNotFoundException loop
 * 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
@@ -238,42 +265,12 @@ FIXME: The new optimized map sending doesn't sometimes send enough blocks
        from big caves and such\r
 FIXME: Block send distance configuration does not take effect for some reason\r
 \r
-SUGG: Map unloading based on sector reference is not very good, it keeps\r
-       unnecessary stuff in memory. I guess. Investigate this.\r
-\r
-TODO: When block is placed and it has param_type==CPT_FACEDIR_SIMPLE, set\r
-      the direction accordingly.\r
-\r
 Environment:\r
 ------------\r
 \r
-TODO: 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
-               TODO: Make proper hooks in here\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
-               TODO: Make proper hooks in here\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
+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
@@ -285,6 +282,7 @@ TODO: Get rid of MapBlockObjects and use only ActiveObjects
 \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
@@ -303,42 +301,17 @@ TODO: Mineral and ground material properties
 TODO: Flowing water to actually contain flow direction information\r
       - There is a space for this - it just has to be implemented.\r
 \r
-SUGG: Try out the notch way of generating maps, that is, make bunches\r
-      of low-res 3d noise and interpolate linearly.\r
-\r
-Mapgen v2 (the current one):\r
-* Possibly add some kind of erosion and other stuff\r
-* Better water generation (spread it to underwater caverns but don't\r
-  fill dungeons that don't touch big water masses)\r
-* When generating a chunk and the neighboring chunk doesn't have mud\r
-  and stuff yet and the ground is fairly flat, the mud will flow to\r
-  the other chunk making nasty straight walls when the other chunk\r
-  is generated. Fix it. Maybe just a special case if the ground is\r
-  flat?\r
-* Consider not updating this one and make a good mainly block-based\r
-  generator\r
-\r
-SUGG: Make two "modified states", one that forces the block to be saved at\r
-       the next save event, and one that makes the block to be saved at exit\r
-       time.\r
-\r
-TODO: Add a not_fully_generated flag to MapBlock, which would be set for\r
-       blocks that contain eg. trees from neighboring generations but haven't\r
-       been generated itself. This is required for the future generator.\r
-\r
 Misc. stuff:\r
 ------------\r
-- Make sure server handles removing grass when a block is placed (etc)\r
-    - The client should not do it by itself\r
-- Block cube placement around player's head\r
-- Protocol version field\r
-- Consider getting some textures from cisoun's texture pack\r
-       - Ask from Cisoun\r
-- Make sure the fence implementation and data format is good\r
-       - Think about using same bits for material for fences and doors, for\r
-       example\r
-- Finish the ActiveBlockModifier stuff and use it for something\r
-- Move mineral to param2, increment map serialization version, add conversion\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: Add a per-sector database to store surface stuff as simple flags/values\r
       - Light?\r
@@ -354,8 +327,6 @@ TODO: Restart irrlicht completely when coming back to main menu from game.
 \r
 TODO: Merge bahamada's audio stuff (clean patch available)\r
 \r
-TODO: Merge spongie's chest/furnace direction (by hand)\r
-\r
 TODO: Merge key configuration menu (no clean patch available)\r
 \r
 Making it more portable:\r
@@ -373,9 +344,6 @@ Stuff to do after release:
 Doing currently:\r
 ----------------\r
 \r
-TODO: Use MapBlock::resetUsageTimer() in appropriate places\r
-      (on client and server)\r
-\r
 ======================================================================\r
 \r
 */\r
@@ -404,16 +372,12 @@ TODO: Use MapBlock::resetUsageTimer() in appropriate places
 \r
 #include <iostream>\r
 #include <fstream>\r
-//#include <jmutexautolock.h>\r
 #include <locale.h>\r
 #include "main.h"\r
 #include "common_irrlicht.h"\r
 #include "debug.h"\r
-//#include "map.h"\r
-//#include "player.h"\r
 #include "test.h"\r
 #include "server.h"\r
-//#include "client.h"\r
 #include "constants.h"\r
 #include "porting.h"\r
 #include "gettime.h"\r
@@ -422,8 +386,6 @@ TODO: Use MapBlock::resetUsageTimer() in appropriate places
 #include "config.h"\r
 #include "guiMainMenu.h"\r
 #include "mineral.h"\r
-//#include "noise.h"\r
-//#include "tile.h"\r
 #include "materials.h"\r
 #include "game.h"\r
 #include "keycode.h"\r
index 2cf7bb2e5b0dfd8ab932292364e0083b1388b1cb..0f3741691532da3283786c80c93502f63a7578be 100644 (file)
@@ -1386,8 +1386,15 @@ bool Map::dayNightDiffed(v3s16 blockpos)
 /*
        Updates usage timers
 */
-void Map::timerUpdate(float dtime)
+void Map::timerUpdate(float dtime, float unload_timeout,
+               core::list<v3s16> *unloaded_blocks)
 {
+       bool save_before_unloading = (mapType() == MAPTYPE_SERVER);
+       
+       core::list<v2s16> sector_deletion_queue;
+       u32 deleted_blocks_count = 0;
+       u32 saved_blocks_count = 0;
+
        core::map<v2s16, MapSector*>::Iterator si;
 
        si = m_sectors.getIterator();
@@ -1395,13 +1402,60 @@ void Map::timerUpdate(float dtime)
        {
                MapSector *sector = si.getNode()->getValue();
 
+               bool all_blocks_deleted = true;
+
                core::list<MapBlock*> blocks;
                sector->getBlocks(blocks);
                for(core::list<MapBlock*>::Iterator i = blocks.begin();
                                i != blocks.end(); i++)
                {
-                       (*i)->incrementUsageTimer(dtime);
+                       MapBlock *block = (*i);
+                       
+                       block->incrementUsageTimer(dtime);
+                       
+                       if(block->getUsageTimer() > unload_timeout)
+                       {
+                               v3s16 p = block->getPos();
+
+                               // Save if modified
+                               if(block->getModified() != MOD_STATE_CLEAN
+                                               && save_before_unloading)
+                               {
+                                       saveBlock(block);
+                                       saved_blocks_count++;
+                               }
+
+                               // Delete from memory
+                               sector->deleteBlock(block);
+
+                               if(unloaded_blocks)
+                                       unloaded_blocks->push_back(p);
+
+                               deleted_blocks_count++;
+                       }
+                       else
+                       {
+                               all_blocks_deleted = false;
+                       }
                }
+
+               if(all_blocks_deleted)
+               {
+                       sector_deletion_queue.push_back(si.getNode()->getKey());
+               }
+       }
+       
+       // Finally delete the empty sectors
+       deleteSectors(sector_deletion_queue);
+       
+       if(deleted_blocks_count != 0)
+       {
+               PrintInfo(dstream); // ServerMap/ClientMap:
+               dstream<<"Unloaded "<<deleted_blocks_count
+                               <<" blocks from memory";
+               if(save_before_unloading)
+                       dstream<<", of which "<<saved_blocks_count<<" were written";
+               dstream<<"."<<std::endl;
        }
 }
 
@@ -1420,6 +1474,7 @@ void Map::deleteSectors(core::list<v2s16> &list)
        }
 }
 
+#if 0
 void Map::unloadUnusedData(float timeout,
                core::list<v3s16> *deleted_blocks)
 {
@@ -1474,6 +1529,7 @@ void Map::unloadUnusedData(float timeout,
        //return sector_deletion_queue.getSize();
        //return deleted_blocks_count;
 }
+#endif
 
 void Map::PrintInfo(std::ostream &out)
 {
@@ -1500,7 +1556,7 @@ void Map::transformLiquids(core::map<v3s16, MapBlock*> & modified_blocks)
                */
                v3s16 p0 = m_transforming_liquid.pop_front();
 
-               MapNode n0 = getNode(p0);
+               MapNode n0 = getNodeNoEx(p0);
 
                // Don't deal with non-liquids
                if(content_liquid(n0.d) == false)
@@ -1532,13 +1588,10 @@ void Map::transformLiquids(core::map<v3s16, MapBlock*> & modified_blocks)
                        };
                        for(u16 i=0; i<5; i++)
                        {
-                               try
-                               {
-
                                bool from_top = (i==0);
 
                                v3s16 p2 = p0 + dirs_from[i];
-                               MapNode n2 = getNode(p2);
+                               MapNode n2 = getNodeNoEx(p2);
 
                                if(content_liquid(n2.d))
                                {
@@ -1572,10 +1625,6 @@ void Map::transformLiquids(core::map<v3s16, MapBlock*> & modified_blocks)
                                        if(new_liquid_level > new_liquid_level_max)
                                                new_liquid_level_max = new_liquid_level;
                                }
-
-                               }catch(InvalidPositionException &e)
-                               {
-                               }
                        } //for
 
                        /*
@@ -1618,20 +1667,13 @@ void Map::transformLiquids(core::map<v3s16, MapBlock*> & modified_blocks)
                                };
                                for(u16 i=0; i<6; i++)
                                {
-                                       try
-                                       {
-
                                        v3s16 p2 = p0 + dirs[i];
 
-                                       MapNode n2 = getNode(p2);
+                                       MapNode n2 = getNodeNoEx(p2);
                                        if(content_flowing_liquid(n2.d))
                                        {
                                                m_transforming_liquid.push_back(p2);
                                        }
-
-                                       }catch(InvalidPositionException &e)
-                                       {
-                                       }
                                }
                        }
                }
@@ -1652,9 +1694,6 @@ void Map::transformLiquids(core::map<v3s16, MapBlock*> & modified_blocks)
                };
                for(u16 i=0; i<5; i++)
                {
-                       try
-                       {
-
                        bool to_bottom = (i == 0);
 
                        // If liquid is at lowest possible height, it's not going
@@ -1680,7 +1719,7 @@ void Map::transformLiquids(core::map<v3s16, MapBlock*> & modified_blocks)
 
                        v3s16 p2 = p0 + dirs_to[i];
 
-                       MapNode n2 = getNode(p2);
+                       MapNode n2 = getNodeNoEx(p2);
                        //dstream<<"[1] n2.param="<<(int)n2.param<<std::endl;
 
                        if(content_liquid(n2.d))
@@ -1746,10 +1785,6 @@ void Map::transformLiquids(core::map<v3s16, MapBlock*> & modified_blocks)
                        // If n2_changed to bottom, don't flow anywhere else
                        if(to_bottom && flowed && !is_source)
                                break;
-
-                       }catch(InvalidPositionException &e)
-                       {
-                       }
                }
 
                loopcount++;
@@ -1945,7 +1980,6 @@ ServerMap::~ServerMap()
        {
                if(m_map_saving_enabled)
                {
-                       //save(false);
                        // Save only changed parts
                        save(true);
                        dstream<<DTIME<<"Server: saved map to "<<m_savedir<<std::endl;
@@ -2104,11 +2138,15 @@ MapBlock* ServerMap::finishBlockMake(mapgen::BlockMakeData *data,
        /*
                NOTE: Lighting and object adding shouldn't really be here, but
                lighting is a bit tricky to move properly to makeBlock.
-               TODO: Do this the right way anyway.
+               TODO: Do this the right way anyway, that is, move it to makeBlock.
+                     - There needs to be some way for makeBlock to report back if
+                           the lighting update is going further down because of the
+                               new block blocking light
        */
 
        /*
                Update lighting
+               NOTE: This takes ~60ms, TODO: Investigate why
        */
        {
                TimeTaker t("finishBlockMake lighting update");
@@ -3418,6 +3456,9 @@ void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass)
                        {
                                continue;
                        }
+
+                       // Okay, this block will be drawn. Reset usage timer.
+                       block->resetUsageTimer();
                        
                        // This is ugly (spherical distance limit?)
                        /*if(m_control.range_all == false &&
index 2fe7490077a88d060aa85aeec167aad9297ba3de..71101b8e41dac60cf10cba1c35141a539da7eb04 100644 (file)
--- a/src/map.h
+++ b/src/map.h
@@ -223,19 +223,23 @@ public:
        
        virtual void save(bool only_changed){assert(0);};
        
-       // Server implements this
+       // Server implements this.
+       // Client leaves it as no-op.
        virtual void saveBlock(MapBlock *block){};
 
        /*
-               Updates usage timers
+               Updates usage timers and unloads unused blocks and sectors.
+               Saves modified blocks before unloading on MAPTYPE_SERVER.
        */
-       void timerUpdate(float dtime);
+       void timerUpdate(float dtime, float unload_timeout,
+                       core::list<v3s16> *unloaded_blocks=NULL);
                
        // Deletes sectors and their blocks from memory
        // Takes cache into account
        // If deleted sector is in sector cache, clears cache
        void deleteSectors(core::list<v2s16> &list);
-       
+
+#if 0
        /*
                Unload unused data
                = flush changed to disk and delete from memory, if usage timer of
@@ -243,8 +247,9 @@ public:
        */
        void unloadUnusedData(float timeout,
                        core::list<v3s16> *deleted_blocks=NULL);
+#endif
 
-       // For debug printing
+       // For debug printing. Prints "Map: ", "ServerMap: " or "ClientMap: "
        virtual void PrintInfo(std::ostream &out);
        
        void transformLiquids(core::map<v3s16, MapBlock*> & modified_blocks);
index b65f0bdb599952921b45b30061b8f5fb9d2e9fde..798f36ac109f3c2d29b91efc19248668a9c40193 100644 (file)
@@ -602,6 +602,9 @@ void RemoteClient::GetNextBlocks(Server *server, float dtime,
                        bool block_is_invalid = false;
                        if(block != NULL)
                        {
+                               // Reset usage timer, this block will be of use in the future.
+                               block->resetUsageTimer();
+
                                // Block is dummy if data doesn't exist.
                                // It means it has been not found from disk and not generated
                                if(block->isDummy())
@@ -1297,12 +1300,21 @@ void Server::AsyncRunStep()
        }
 
        {
-               // Step environment
-               // This also runs Map's timers
                JMutexAutoLock lock(m_env_mutex);
+               // Step environment
                ScopeProfiler sp(&g_profiler, "Server: environment step");
                m_env.step(dtime);
        }
+               
+       const float map_timer_and_unload_dtime = 5.15;
+       if(m_map_timer_and_unload_interval.step(dtime, map_timer_and_unload_dtime))
+       {
+               JMutexAutoLock lock(m_env_mutex);
+               // Run Map's timers and unload unused data
+               ScopeProfiler sp(&g_profiler, "Server: map timer and unload");
+               m_env.getMap().timerUpdate(map_timer_and_unload_dtime,
+                               g_settings.getFloat("server_unload_unused_data_timeout"));
+       }
        
        /*
                Do background stuff
@@ -1665,8 +1677,15 @@ void Server::AsyncRunStep()
                if(m_unsent_map_edit_queue.size() >= 4)
                        disable_single_change_sending = true;
 
+               bool got_any_events = false;
+
+               // We'll log the amount of each
+               Profiler prof;
+
                while(m_unsent_map_edit_queue.size() != 0)
                {
+                       got_any_events = true;
+
                        MapEditEvent* event = m_unsent_map_edit_queue.pop_front();
                        
                        // Players far away from the change are stored here.
@@ -1676,7 +1695,8 @@ void Server::AsyncRunStep()
 
                        if(event->type == MEET_ADDNODE)
                        {
-                               dstream<<"Server: MEET_ADDNODE"<<std::endl;
+                               //dstream<<"Server: MEET_ADDNODE"<<std::endl;
+                               prof.add("MEET_ADDNODE", 1);
                                if(disable_single_change_sending)
                                        sendAddNode(event->p, event->n, event->already_known_by_peer,
                                                        &far_players, 5);
@@ -1686,7 +1706,8 @@ void Server::AsyncRunStep()
                        }
                        else if(event->type == MEET_REMOVENODE)
                        {
-                               dstream<<"Server: MEET_REMOVENODE"<<std::endl;
+                               //dstream<<"Server: MEET_REMOVENODE"<<std::endl;
+                               prof.add("MEET_REMOVENODE", 1);
                                if(disable_single_change_sending)
                                        sendRemoveNode(event->p, event->already_known_by_peer,
                                                        &far_players, 5);
@@ -1697,15 +1718,18 @@ void Server::AsyncRunStep()
                        else if(event->type == MEET_BLOCK_NODE_METADATA_CHANGED)
                        {
                                dstream<<"Server: MEET_BLOCK_NODE_METADATA_CHANGED"<<std::endl;
+                               prof.add("MEET_BLOCK_NODE_METADATA_CHANGED", 1);
                                setBlockNotSent(event->p);
                        }
                        else if(event->type == MEET_OTHER)
                        {
+                               prof.add("MEET_OTHER", 1);
                                dstream<<"WARNING: Server: MEET_OTHER not implemented"
                                                <<std::endl;
                        }
                        else
                        {
+                               prof.add("unknown", 1);
                                dstream<<"WARNING: Server: Unknown MapEditEvent "
                                                <<((u32)event->type)<<std::endl;
                        }
@@ -1743,6 +1767,13 @@ void Server::AsyncRunStep()
                        if(count >= 1 && m_unsent_map_edit_queue.size() < 100)
                                break;*/
                }
+
+               if(got_any_events)
+               {
+                       dstream<<"Server: MapEditEvents:"<<std::endl;
+                       prof.print(dstream);
+               }
+               
        }
 
        /*
@@ -1765,39 +1796,6 @@ void Server::AsyncRunStep()
                }
        }
        
-       /*
-               Step node metadata
-               TODO: Move to ServerEnvironment and utilize active block stuff
-       */
-       /*{
-               //TimeTaker timer("Step node metadata");
-
-               JMutexAutoLock envlock(m_env_mutex);
-               JMutexAutoLock conlock(m_con_mutex);
-
-               ScopeProfiler sp(&g_profiler, "Server: stepping node metadata");
-
-               core::map<v3s16, MapBlock*> changed_blocks;
-               m_env.getMap().nodeMetadataStep(dtime, changed_blocks);
-               
-               // Use setBlockNotSent
-
-               for(core::map<v3s16, MapBlock*>::Iterator
-                               i = changed_blocks.getIterator();
-                               i.atEnd() == false; i++)
-               {
-                       MapBlock *block = i.getNode()->getValue();
-
-                       for(core::map<u16, RemoteClient*>::Iterator
-                               i = m_clients.getIterator();
-                               i.atEnd()==false; i++)
-                       {
-                               RemoteClient *client = i.getNode()->getValue();
-                               client->SetBlockNotSent(block->getPos());
-                       }
-               }
-       }*/
-               
        /*
                Trigger emergethread (it somehow gets to a non-triggered but
                bysy state sometimes)
@@ -1829,30 +1827,29 @@ void Server::AsyncRunStep()
                        
                        // Map
                        JMutexAutoLock lock(m_env_mutex);
-                       if(((ServerMap*)(&m_env.getMap()))->isSavingEnabled() == true)
-                       {
-                               // Unload unused data (delete from memory)
-                               m_env.getMap().unloadUnusedData(
-                                               g_settings.getFloat("server_unload_unused_sectors_timeout"));
-                               /*u32 deleted_count = m_env.getMap().unloadUnusedData(
-                                               g_settings.getFloat("server_unload_unused_sectors_timeout"));
-                                               */
 
-                               // Save only changed parts
-                               m_env.getMap().save(true);
+                       /*// Unload unused data (delete from memory)
+                       m_env.getMap().unloadUnusedData(
+                                       g_settings.getFloat("server_unload_unused_sectors_timeout"));
+                                       */
+                       /*u32 deleted_count = m_env.getMap().unloadUnusedData(
+                                       g_settings.getFloat("server_unload_unused_sectors_timeout"));
+                                       */
 
-                               /*if(deleted_count > 0)
-                               {
-                                       dout_server<<"Server: Unloaded "<<deleted_count
-                                                       <<" blocks from memory"<<std::endl;
-                               }*/
+                       // Save only changed parts
+                       m_env.getMap().save(true);
 
-                               // Save players
-                               m_env.serializePlayers(m_mapsavedir);
-                               
-                               // Save environment metadata
-                               m_env.saveMeta(m_mapsavedir);
-                       }
+                       /*if(deleted_count > 0)
+                       {
+                               dout_server<<"Server: Unloaded "<<deleted_count
+                                               <<" blocks from memory"<<std::endl;
+                       }*/
+
+                       // Save players
+                       m_env.serializePlayers(m_mapsavedir);
+                       
+                       // Save environment metadata
+                       m_env.saveMeta(m_mapsavedir);
                }
        }
 }
@@ -3336,7 +3333,7 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
 
 void Server::onMapEditEvent(MapEditEvent *event)
 {
-       dstream<<"Server::onMapEditEvent()"<<std::endl;
+       //dstream<<"Server::onMapEditEvent()"<<std::endl;
        if(m_ignore_map_edit_events)
                return;
        MapEditEvent *e = event->clone();
index b88369ddf735a1d712653a4a54b77685c85fadc7..1da004da57c78f8d805352906406b3623093d339 100644 (file)
@@ -534,6 +534,7 @@ private:
        float m_objectdata_timer;
        float m_emergethread_trigger_timer;
        float m_savemap_timer;
+       IntervalLimiter m_map_timer_and_unload_interval;
        
        // NOTE: If connection and environment are both to be locked,
        // environment shall be locked first.