]> git.lizzy.rs Git - dragonfireclient.git/blobdiff - src/environment.cpp
FIx wrong error message on invalid use of the formspec element image_button
[dragonfireclient.git] / src / environment.cpp
index 9f87b8c919b60817538a0489618bd2b9a483e8f4..03b436890b333d4c7fe8db968806889264da7ce1 100644 (file)
@@ -17,9 +17,6 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 */
 
-#include <set>
-#include <list>
-#include <map>
 #include "environment.h"
 #include "filesys.h"
 #include "porting.h"
@@ -28,11 +25,10 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include "mapblock.h"
 #include "serverobject.h"
 #include "content_sao.h"
-#include "mapgen.h"
 #include "settings.h"
 #include "log.h"
 #include "profiler.h"
-#include "scriptapi.h"
+#include "scripting_game.h"
 #include "nodedef.h"
 #include "nodemetadata.h"
 #include "main.h" // For g_settings, g_profiler
@@ -40,9 +36,11 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #ifndef SERVER
 #include "clientmap.h"
 #include "localplayer.h"
+#include "event.h"
 #endif
 #include "daynightratio.h"
 #include "map.h"
+#include "emerge.h"
 #include "util/serialize.h"
 
 #define PP(x) "("<<(x).X<<","<<(x).Y<<","<<(x).Z<<")"
@@ -190,20 +188,9 @@ std::list<Player*> Environment::getPlayers(bool ignore_disconnected)
        return newlist;
 }
 
-void Environment::printPlayers(std::ostream &o)
-{
-       o<<"Players in environment:"<<std::endl;
-       for(std::list<Player*>::iterator i = m_players.begin();
-                       i != m_players.end(); i++)
-       {
-               Player *player = *i;
-               o<<"Player peer_id="<<player->peer_id<<std::endl;
-       }
-}
-
 u32 Environment::getDayNightRatio()
 {
-       bool smooth = (g_settings->getS32("enable_shaders") != 0);
+       bool smooth = g_settings->getBool("enable_shaders");
        return time_to_daynight_ratio(m_time_of_day_f*24000, smooth);
 }
 
@@ -320,10 +307,11 @@ void ActiveBlockList::update(std::list<v3s16> &active_positions,
        ServerEnvironment
 */
 
-ServerEnvironment::ServerEnvironment(ServerMap *map, lua_State *L,
+ServerEnvironment::ServerEnvironment(ServerMap *map,
+               GameScripting *scriptIface,
                IGameDef *gamedef, IBackgroundBlockEmerger *emerger):
        m_map(map),
-       m_lua(L),
+       m_script(scriptIface),
        m_gamedef(gamedef),
        m_emerger(emerger),
        m_random_spawn_timer(3),
@@ -331,8 +319,10 @@ ServerEnvironment::ServerEnvironment(ServerMap *map, lua_State *L,
        m_active_block_interval_overload_skip(0),
        m_game_time(0),
        m_game_time_fraction_counter(0),
-       m_recommended_send_interval(0.1)
+       m_recommended_send_interval(0.1),
+       m_max_lag_estimate(0.1)
 {
+       m_use_weather = g_settings->getBool("weather");
 }
 
 ServerEnvironment::~ServerEnvironment()
@@ -398,7 +388,7 @@ void ServerEnvironment::serializePlayers(const std::string &savedir)
        std::vector<fs::DirListNode> player_files = fs::GetDirListing(players_path);
        for(u32 i=0; i<player_files.size(); i++)
        {
-               if(player_files[i].dir)
+               if(player_files[i].dir || player_files[i].name[0] == '.')
                        continue;
                
                // Full path to this file
@@ -416,7 +406,7 @@ void ServerEnvironment::serializePlayers(const std::string &savedir)
                                infostream<<"Failed to read "<<path<<std::endl;
                                continue;
                        }
-                       testplayer.deSerialize(is);
+                       testplayer.deSerialize(is, player_files[i].name);
                }
 
                //infostream<<"Loaded test player with name "<<testplayer.getName()<<std::endl;
@@ -433,15 +423,18 @@ void ServerEnvironment::serializePlayers(const std::string &savedir)
                //infostream<<"Found matching player, overwriting."<<std::endl;
 
                // OK, found. Save player there.
+               if(player->checkModified())
                {
                        // Open file and serialize
-                       std::ofstream os(path.c_str(), std::ios_base::binary);
-                       if(os.good() == false)
+                       std::ostringstream ss(std::ios_base::binary);
+                       player->serialize(ss);
+                       if(!fs::safeWriteToFile(path, ss.str()))
                        {
-                               infostream<<"Failed to overwrite "<<path<<std::endl;
+                               infostream<<"Failed to write "<<path<<std::endl;
                                continue;
                        }
-                       player->serialize(os);
+                       saved_players.insert(player);
+               } else {
                        saved_players.insert(player);
                }
        }
@@ -489,13 +482,13 @@ void ServerEnvironment::serializePlayers(const std::string &savedir)
                        /*infostream<<"Saving player "<<player->getName()<<" to "
                                        <<path<<std::endl;*/
                        // Open file and serialize
-                       std::ofstream os(path.c_str(), std::ios_base::binary);
-                       if(os.good() == false)
+                       std::ostringstream ss(std::ios_base::binary);
+                       player->serialize(ss);
+                       if(!fs::safeWriteToFile(path, ss.str()))
                        {
-                               infostream<<"Failed to overwrite "<<path<<std::endl;
+                               infostream<<"Failed to write "<<path<<std::endl;
                                continue;
                        }
-                       player->serialize(os);
                        saved_players.insert(player);
                }
        }
@@ -528,7 +521,7 @@ void ServerEnvironment::deSerializePlayers(const std::string &savedir)
                                infostream<<"Failed to read "<<path<<std::endl;
                                continue;
                        }
-                       testplayer.deSerialize(is);
+                       testplayer.deSerialize(is, player_files[i].name);
                }
 
                if(!string_allowed(testplayer.getName(), PLAYERNAME_ALLOWED_CHARS))
@@ -562,7 +555,7 @@ void ServerEnvironment::deSerializePlayers(const std::string &savedir)
                                infostream<<"Failed to read "<<path<<std::endl;
                                continue;
                        }
-                       player->deSerialize(is);
+                       player->deSerialize(is, player_files[i].name);
                }
 
                if(newplayer)
@@ -577,19 +570,20 @@ void ServerEnvironment::saveMeta(const std::string &savedir)
        std::string path = savedir + "/env_meta.txt";
 
        // Open file and serialize
-       std::ofstream os(path.c_str(), std::ios_base::binary);
-       if(os.good() == false)
-       {
-               infostream<<"ServerEnvironment::saveMeta(): Failed to open "
-                               <<path<<std::endl;
-               throw SerializationError("Couldn't save env meta");
-       }
+       std::ostringstream ss(std::ios_base::binary);
 
        Settings args;
        args.setU64("game_time", m_game_time);
        args.setU64("time_of_day", getTimeOfDay());
-       args.writeLines(os);
-       os<<"EnvArgsEnd\n";
+       args.writeLines(ss);
+       ss<<"EnvArgsEnd\n";
+
+       if(!fs::safeWriteToFile(path, ss.str()))
+       {
+               infostream<<"ServerEnvironment::saveMeta(): Failed to write "
+                               <<path<<std::endl;
+               throw SerializationError("Couldn't save env meta");
+       }
 }
 
 void ServerEnvironment::loadMeta(const std::string &savedir)
@@ -815,6 +809,16 @@ void ServerEnvironment::activateBlock(MapBlock *block, u32 additional_dtime)
        
        // Activate stored objects
        activateObjects(block, dtime_s);
+       
+       // Calculate weather conditions
+       if (m_use_weather) {
+               m_map->updateBlockHeat(this, block->getPos() *  MAP_BLOCKSIZE, block);
+               m_map->updateBlockHumidity(this, block->getPos() * MAP_BLOCKSIZE, block);
+       } else {
+               block->heat     = HEAT_UNDEFINED;
+               block->humidity = HUMIDITY_UNDEFINED;
+               block->weather_update_time = 0;
+       }
 
        // Run node timers
        std::map<v3s16, NodeTimer> elapsed_timers =
@@ -826,7 +830,7 @@ void ServerEnvironment::activateBlock(MapBlock *block, u32 additional_dtime)
                                i != elapsed_timers.end(); i++){
                        n = block->getNodeNoEx(i->first);
                        v3s16 p = i->first + block->getPosRelative();
-                       if(scriptapi_node_on_timer(m_lua,p,n,i->second.elapsed))
+                       if(m_script->node_on_timer(p,n,i->second.elapsed))
                                block->setNodeTimer(i->first,NodeTimer(i->second.timeout,0));
                }
        }
@@ -847,17 +851,17 @@ bool ServerEnvironment::setNode(v3s16 p, const MapNode &n)
        MapNode n_old = m_map->getNodeNoEx(p);
        // Call destructor
        if(ndef->get(n_old).has_on_destruct)
-               scriptapi_node_on_destruct(m_lua, p, n_old);
+               m_script->node_on_destruct(p, n_old);
        // Replace node
        bool succeeded = m_map->addNodeWithEvent(p, n);
        if(!succeeded)
                return false;
        // Call post-destructor
        if(ndef->get(n_old).has_after_destruct)
-               scriptapi_node_after_destruct(m_lua, p, n_old);
+               m_script->node_after_destruct(p, n_old);
        // Call constructor
        if(ndef->get(n).has_on_construct)
-               scriptapi_node_on_construct(m_lua, p, n);
+               m_script->node_on_construct(p, n);
        return true;
 }
 
@@ -867,7 +871,7 @@ bool ServerEnvironment::removeNode(v3s16 p)
        MapNode n_old = m_map->getNodeNoEx(p);
        // Call destructor
        if(ndef->get(n_old).has_on_destruct)
-               scriptapi_node_on_destruct(m_lua, p, n_old);
+               m_script->node_on_destruct(p, n_old);
        // Replace with air
        // This is slightly optimized compared to addNodeWithEvent(air)
        bool succeeded = m_map->removeNodeWithEvent(p);
@@ -875,7 +879,7 @@ bool ServerEnvironment::removeNode(v3s16 p)
                return false;
        // Call post-destructor
        if(ndef->get(n_old).has_after_destruct)
-               scriptapi_node_after_destruct(m_lua, p, n_old);
+               m_script->node_after_destruct(p, n_old);
        // Air doesn't require constructor
        return true;
 }
@@ -930,7 +934,7 @@ void ServerEnvironment::clearAllObjects()
                // Tell the object about removal
                obj->removingFromEnvironment();
                // Deregister in scripting api
-               scriptapi_rm_object_reference(m_lua, obj);
+               m_script->removeObjectReference(obj);
 
                // Delete active object
                if(obj->environmentDeletes())
@@ -945,6 +949,16 @@ void ServerEnvironment::clearAllObjects()
                m_active_objects.erase(*i);
        }
 
+       // Get list of loaded blocks
+       std::list<v3s16> loaded_blocks;
+       infostream<<"ServerEnvironment::clearAllObjects(): "
+                       <<"Listing all loaded blocks"<<std::endl;
+       m_map->listAllLoadedBlocks(loaded_blocks);
+       infostream<<"ServerEnvironment::clearAllObjects(): "
+                       <<"Done listing all loaded blocks: "
+                       <<loaded_blocks.size()<<std::endl;
+
+       // Get list of loadable blocks
        std::list<v3s16> loadable_blocks;
        infostream<<"ServerEnvironment::clearAllObjects(): "
                        <<"Listing all loadable blocks"<<std::endl;
@@ -953,6 +967,20 @@ void ServerEnvironment::clearAllObjects()
                        <<"Done listing all loadable blocks: "
                        <<loadable_blocks.size()
                        <<", now clearing"<<std::endl;
+
+       // Grab a reference on each loaded block to avoid unloading it
+       for(std::list<v3s16>::iterator i = loaded_blocks.begin();
+                       i != loaded_blocks.end(); ++i)
+       {
+               v3s16 p = *i;
+               MapBlock *block = m_map->getBlockNoCreateNoEx(p);
+               assert(block);
+               block->refGrab();
+       }
+
+       // Remove objects in all loadable blocks
+       u32 unload_interval = g_settings->getS32("max_clearobjects_extra_loaded_blocks");
+       unload_interval = MYMAX(unload_interval, 1);
        u32 report_interval = loadable_blocks.size() / 10;
        u32 num_blocks_checked = 0;
        u32 num_blocks_cleared = 0;
@@ -987,7 +1015,22 @@ void ServerEnvironment::clearAllObjects()
                                        <<" in "<<num_blocks_cleared<<" blocks ("
                                        <<percent<<"%)"<<std::endl;
                }
+               if(num_blocks_checked % unload_interval == 0){
+                       m_map->unloadUnreferencedBlocks();
+               }
+       }
+       m_map->unloadUnreferencedBlocks();
+
+       // Drop references that were added above
+       for(std::list<v3s16>::iterator i = loaded_blocks.begin();
+                       i != loaded_blocks.end(); ++i)
+       {
+               v3s16 p = *i;
+               MapBlock *block = m_map->getBlockNoCreateNoEx(p);
+               assert(block);
+               block->refDrop();
        }
+
        infostream<<"ServerEnvironment::clearAllObjects(): "
                        <<"Finished: Cleared "<<num_objs_cleared<<" objects"
                        <<" in "<<num_blocks_cleared<<" blocks"<<std::endl;
@@ -1105,7 +1148,8 @@ void ServerEnvironment::step(float dtime)
                        MapBlock *block = m_map->getBlockNoCreateNoEx(p);
                        if(block==NULL){
                                // Block needs to be fetched first
-                               m_emerger->queueBlockEmerge(p, false);
+                               m_emerger->enqueueBlockEmerge(
+                                               PEER_ID_INEXISTENT, p, false);
                                m_active_blocks.m_list.erase(p);
                                continue;
                        }
@@ -1159,7 +1203,7 @@ void ServerEnvironment::step(float dtime)
                                                i != elapsed_timers.end(); i++){
                                        n = block->getNodeNoEx(i->first);
                                        p = i->first + block->getPosRelative();
-                                       if(scriptapi_node_on_timer(m_lua,p,n,i->second.elapsed))
+                                       if(m_script->node_on_timer(p,n,i->second.elapsed))
                                                block->setNodeTimer(i->first,NodeTimer(i->second.timeout,0));
                                }
                        }
@@ -1213,7 +1257,7 @@ void ServerEnvironment::step(float dtime)
        /*
                Step script environment (run global on_step())
        */
-       scriptapi_environment_step(m_lua, dtime);
+       m_script->environment_Step(dtime);
 
        /*
                Step active objects
@@ -1461,7 +1505,9 @@ ActiveObjectMessage ServerEnvironment::getActiveObjectMessage()
        if(m_active_object_messages.empty())
                return ActiveObjectMessage(0);
        
-       return m_active_object_messages.pop_front();
+       ActiveObjectMessage message = m_active_object_messages.front();
+       m_active_object_messages.pop_front();
+       return message;
 }
 
 /*
@@ -1507,7 +1553,7 @@ u16 ServerEnvironment::addActiveObjectRaw(ServerActiveObject *object,
                        <<std::endl;
        
        // Register reference in scripting api (must be done before post-init)
-       scriptapi_add_object_reference(m_lua, object);
+       m_script->addObjectReference(object);
        // Post-initialize object
        object->addedToEnvironment(dtime_s);
        
@@ -1595,7 +1641,7 @@ void ServerEnvironment::removeRemovedObjects()
                // Tell the object about removal
                obj->removingFromEnvironment();
                // Deregister in scripting api
-               scriptapi_rm_object_reference(m_lua, obj);
+               m_script->removeObjectReference(obj);
 
                // Delete
                if(obj->environmentDeletes())
@@ -1659,7 +1705,7 @@ void ServerEnvironment::activateObjects(MapBlock *block, u32 dtime_s)
                        <<"activating objects of block "<<PP(block->getPos())
                        <<" ("<<block->m_static_objects.m_stored.size()
                        <<" objects)"<<std::endl;
-       bool large_amount = (block->m_static_objects.m_stored.size() > 49);
+       bool large_amount = (block->m_static_objects.m_stored.size() > g_settings->getU16("max_objects_per_block"));
        if(large_amount){
                errorstream<<"suspiciously large amount of objects detected: "
                                <<block->m_static_objects.m_stored.size()<<" in "
@@ -1837,12 +1883,12 @@ void ServerEnvironment::deactivateFarObjects(bool force_delete)
 
                        if(block)
                        {
-                               if(block->m_static_objects.m_stored.size() >= 49){
+                               if(block->m_static_objects.m_stored.size() >= g_settings->getU16("max_objects_per_block")){
                                        errorstream<<"ServerEnv: Trying to store id="<<obj->getId()
                                                        <<" statically but block "<<PP(blockpos)
                                                        <<" already contains "
                                                        <<block->m_static_objects.m_stored.size()
-                                                       <<" (over 49) objects."
+                                                       <<" objects."
                                                        <<" Forcing delete."<<std::endl;
                                        force_delete = true;
                                } else {
@@ -1901,7 +1947,7 @@ void ServerEnvironment::deactivateFarObjects(bool force_delete)
                // Tell the object about removal
                obj->removingFromEnvironment();
                // Deregister in scripting api
-               scriptapi_rm_object_reference(m_lua, obj);
+               m_script->removeObjectReference(obj);
 
                // Delete active object
                if(obj->environmentDeletes())
@@ -2151,8 +2197,11 @@ void ClientEnvironment::step(float dtime)
                {
                        f32 damage_f = (speed - tolerance)/BS * post_factor;
                        u16 damage = (u16)(damage_f+0.5);
-                       if(damage != 0)
+                       if(damage != 0){
                                damageLocalPlayer(damage, true);
+                               MtEvent *e = new SimpleTriggerEvent("PlayerFallingDamage");
+                               m_gamedef->event()->put(e);
+                       }
                }
        }
        
@@ -2184,7 +2233,56 @@ void ClientEnvironment::step(float dtime)
                        damageLocalPlayer(damage_per_second, true);
                }
        }
-       
+
+       /*
+               Drowning
+       */
+       if(m_drowning_interval.step(dtime, 2.0))
+       {
+               v3f pf = lplayer->getPosition();
+
+               // head
+               v3s16 p = floatToInt(pf + v3f(0, BS*1.6, 0), BS);
+               MapNode n = m_map->getNodeNoEx(p);
+               ContentFeatures c = m_gamedef->ndef()->get(n);
+               u8 drowning_damage = c.drowning;
+               if(drowning_damage > 0 && lplayer->hp > 0){
+                       u16 breath = lplayer->getBreath();
+                       if(breath > 10){
+                               breath = 11;
+                       }
+                       if(breath > 0){
+                               breath -= 1;
+                       }
+                       lplayer->setBreath(breath);
+                       updateLocalPlayerBreath(breath);
+               }
+
+               if(lplayer->getBreath() == 0 && drowning_damage > 0){
+                       damageLocalPlayer(drowning_damage, true);
+               }
+       }
+       if(m_breathing_interval.step(dtime, 0.5))
+       {
+               v3f pf = lplayer->getPosition();
+
+               // head
+               v3s16 p = floatToInt(pf + v3f(0, BS*1.6, 0), BS);
+               MapNode n = m_map->getNodeNoEx(p);
+               ContentFeatures c = m_gamedef->ndef()->get(n);
+               if (!lplayer->hp){
+                       lplayer->setBreath(11);
+               }
+               else if(c.drowning == 0){
+                       u16 breath = lplayer->getBreath();
+                       if(breath <= 10){
+                               breath += 1;
+                               lplayer->setBreath(breath);
+                               updateLocalPlayerBreath(breath);
+                       }
+               }
+       }
+
        /*
                Stuff that can be done in an arbitarily large dtime
        */
@@ -2221,6 +2319,7 @@ void ClientEnvironment::step(float dtime)
                Step active objects and update lighting of them
        */
        
+       g_profiler->avg("CEnv: num of objects", m_active_objects.size());
        bool update_lighting = m_active_object_light_update_interval.step(dtime, 0.21);
        for(std::map<u16, ClientActiveObject*>::iterator
                        i = m_active_objects.begin();
@@ -2250,6 +2349,7 @@ void ClientEnvironment::step(float dtime)
        /*
                Step and handle simple objects
        */
+       g_profiler->avg("CEnv: num of simple objects", m_simple_objects.size());
        for(std::list<ClientSimpleObject*>::iterator
                        i = m_simple_objects.begin(); i != m_simple_objects.end();)
        {
@@ -2442,6 +2542,14 @@ void ClientEnvironment::damageLocalPlayer(u8 damage, bool handle_hp)
        m_client_event_queue.push_back(event);
 }
 
+void ClientEnvironment::updateLocalPlayerBreath(u16 breath)
+{
+       ClientEnvEvent event;
+       event.type = CEE_PLAYER_BREATH;
+       event.player_breath.amount = breath;
+       m_client_event_queue.push_back(event);
+}
+
 /*
        Client likes to call these
 */
@@ -2468,13 +2576,14 @@ void ClientEnvironment::getActiveObjects(v3f origin, f32 max_d,
 
 ClientEnvEvent ClientEnvironment::getClientEvent()
 {
+       ClientEnvEvent event;
        if(m_client_event_queue.empty())
-       {
-               ClientEnvEvent event;
                event.type = CEE_NONE;
-               return event;
+       else {
+               event = m_client_event_queue.front();
+               m_client_event_queue.pop_front();
        }
-       return m_client_event_queue.pop_front();
+       return event;
 }
 
 #endif // #ifndef SERVER