3 Copyright (C) 2010-2013 celeron55, Perttu Ahola <celeron55@gmail.com>
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU Lesser General Public License as published by
7 the Free Software Foundation; either version 2.1 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU Lesser General Public License for more details.
15 You should have received a copy of the GNU Lesser General Public License along
16 with this program; if not, write to the Free Software Foundation, Inc.,
17 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20 #include "environment.h"
23 #include "collision.h"
24 #include "content_mapnode.h"
26 #include "serverobject.h"
27 #include "content_sao.h"
31 #include "scripting_game.h"
33 #include "nodemetadata.h"
34 #include "main.h" // For g_settings, g_profiler
37 #include "clientmap.h"
38 #include "localplayer.h"
41 #include "daynightratio.h"
44 #include "util/serialize.h"
46 #define PP(x) "("<<(x).X<<","<<(x).Y<<","<<(x).Z<<")"
48 Environment::Environment():
50 m_time_of_day_f(9000./24000),
51 m_time_of_day_speed(0),
53 m_enable_day_night_ratio_override(false),
54 m_day_night_ratio_override(0.0f)
58 Environment::~Environment()
61 for(std::list<Player*>::iterator i = m_players.begin();
62 i != m_players.end(); ++i)
68 void Environment::addPlayer(Player *player)
70 DSTACK(__FUNCTION_NAME);
72 Check that peer_ids are unique.
73 Also check that names are unique.
74 Exception: there can be multiple players with peer_id=0
76 // If peer id is non-zero, it has to be unique.
77 if(player->peer_id != 0)
78 assert(getPlayer(player->peer_id) == NULL);
79 // Name has to be unique.
80 assert(getPlayer(player->getName()) == NULL);
82 m_players.push_back(player);
85 void Environment::removePlayer(u16 peer_id)
87 DSTACK(__FUNCTION_NAME);
89 for(std::list<Player*>::iterator i = m_players.begin();
90 i != m_players.end();)
93 if(player->peer_id == peer_id) {
95 i = m_players.erase(i);
102 Player * Environment::getPlayer(u16 peer_id)
104 for(std::list<Player*>::iterator i = m_players.begin();
105 i != m_players.end(); ++i)
108 if(player->peer_id == peer_id)
114 Player * Environment::getPlayer(const char *name)
116 for(std::list<Player*>::iterator i = m_players.begin();
117 i != m_players.end(); ++i)
120 if(strcmp(player->getName(), name) == 0)
126 Player * Environment::getRandomConnectedPlayer()
128 std::list<Player*> connected_players = getPlayers(true);
129 u32 chosen_one = myrand() % connected_players.size();
131 for(std::list<Player*>::iterator
132 i = connected_players.begin();
133 i != connected_players.end(); ++i)
145 Player * Environment::getNearestConnectedPlayer(v3f pos)
147 std::list<Player*> connected_players = getPlayers(true);
149 Player *nearest_player = NULL;
150 for(std::list<Player*>::iterator
151 i = connected_players.begin();
152 i != connected_players.end(); ++i)
155 f32 d = player->getPosition().getDistanceFrom(pos);
156 if(d < nearest_d || nearest_player == NULL)
159 nearest_player = player;
162 return nearest_player;
165 std::list<Player*> Environment::getPlayers()
170 std::list<Player*> Environment::getPlayers(bool ignore_disconnected)
172 std::list<Player*> newlist;
173 for(std::list<Player*>::iterator
174 i = m_players.begin();
175 i != m_players.end(); ++i)
179 if(ignore_disconnected)
181 // Ignore disconnected players
182 if(player->peer_id == 0)
186 newlist.push_back(player);
191 u32 Environment::getDayNightRatio()
193 if(m_enable_day_night_ratio_override)
194 return m_day_night_ratio_override;
195 bool smooth = g_settings->getBool("enable_shaders");
196 return time_to_daynight_ratio(m_time_of_day_f*24000, smooth);
199 void Environment::stepTimeOfDay(float dtime)
201 m_time_counter += dtime;
202 f32 speed = m_time_of_day_speed * 24000./(24.*3600);
203 u32 units = (u32)(m_time_counter*speed);
204 m_time_counter -= (f32)units / speed;
208 if(m_time_of_day + units >= 24000)
210 m_time_of_day = (m_time_of_day + units) % 24000;
212 m_time_of_day_f = (float)m_time_of_day / 24000.0;
215 m_time_of_day_f += m_time_of_day_speed/24/3600*dtime;
216 if(m_time_of_day_f > 1.0)
217 m_time_of_day_f -= 1.0;
218 if(m_time_of_day_f < 0.0)
219 m_time_of_day_f += 1.0;
227 ABMWithState::ABMWithState(ActiveBlockModifier *abm_):
231 // Initialize timer to random value to spread processing
232 float itv = abm->getTriggerInterval();
233 itv = MYMAX(0.001, itv); // No less than 1ms
234 int minval = MYMAX(-0.51*itv, -60); // Clamp to
235 int maxval = MYMIN(0.51*itv, 60); // +-60 seconds
236 timer = myrand_range(minval, maxval);
243 void fillRadiusBlock(v3s16 p0, s16 r, std::set<v3s16> &list)
246 for(p.X=p0.X-r; p.X<=p0.X+r; p.X++)
247 for(p.Y=p0.Y-r; p.Y<=p0.Y+r; p.Y++)
248 for(p.Z=p0.Z-r; p.Z<=p0.Z+r; p.Z++)
255 void ActiveBlockList::update(std::list<v3s16> &active_positions,
257 std::set<v3s16> &blocks_removed,
258 std::set<v3s16> &blocks_added)
263 std::set<v3s16> newlist = m_forceloaded_list;
264 for(std::list<v3s16>::iterator i = active_positions.begin();
265 i != active_positions.end(); ++i)
267 fillRadiusBlock(*i, radius, newlist);
271 Find out which blocks on the old list are not on the new list
273 // Go through old list
274 for(std::set<v3s16>::iterator i = m_list.begin();
275 i != m_list.end(); ++i)
278 // If not on new list, it's been removed
279 if(newlist.find(p) == newlist.end())
280 blocks_removed.insert(p);
284 Find out which blocks on the new list are not on the old list
286 // Go through new list
287 for(std::set<v3s16>::iterator i = newlist.begin();
288 i != newlist.end(); ++i)
291 // If not on old list, it's been added
292 if(m_list.find(p) == m_list.end())
293 blocks_added.insert(p);
300 for(std::set<v3s16>::iterator i = newlist.begin();
301 i != newlist.end(); ++i)
312 ServerEnvironment::ServerEnvironment(ServerMap *map,
313 GameScripting *scriptIface, IGameDef *gamedef):
315 m_script(scriptIface),
317 m_send_recommended_timer(0),
318 m_active_block_interval_overload_skip(0),
320 m_game_time_fraction_counter(0),
321 m_recommended_send_interval(0.1),
322 m_max_lag_estimate(0.1)
326 ServerEnvironment::~ServerEnvironment()
328 // Clear active block list.
329 // This makes the next one delete all active objects.
330 m_active_blocks.clear();
332 // Convert all objects to static and delete the active objects
333 deactivateFarObjects(true);
338 // Delete ActiveBlockModifiers
339 for(std::list<ABMWithState>::iterator
340 i = m_abms.begin(); i != m_abms.end(); ++i){
345 Map & ServerEnvironment::getMap()
350 ServerMap & ServerEnvironment::getServerMap()
355 bool ServerEnvironment::line_of_sight(v3f pos1, v3f pos2, float stepsize, v3s16 *p)
357 float distance = pos1.getDistanceFrom(pos2);
359 //calculate normalized direction vector
360 v3f normalized_vector = v3f((pos2.X - pos1.X)/distance,
361 (pos2.Y - pos1.Y)/distance,
362 (pos2.Z - pos1.Z)/distance);
364 //find out if there's a node on path between pos1 and pos2
365 for (float i = 1; i < distance; i += stepsize) {
366 v3s16 pos = floatToInt(v3f(normalized_vector.X * i,
367 normalized_vector.Y * i,
368 normalized_vector.Z * i) +pos1,BS);
370 MapNode n = getMap().getNodeNoEx(pos);
372 if(n.param0 != CONTENT_AIR) {
382 void ServerEnvironment::serializePlayers(const std::string &savedir)
384 std::string players_path = savedir + "/players";
385 fs::CreateDir(players_path);
387 std::set<Player*> saved_players;
389 std::vector<fs::DirListNode> player_files = fs::GetDirListing(players_path);
390 for(u32 i=0; i<player_files.size(); i++)
392 if(player_files[i].dir || player_files[i].name[0] == '.')
395 // Full path to this file
396 std::string path = players_path + "/" + player_files[i].name;
398 //infostream<<"Checking player file "<<path<<std::endl;
400 // Load player to see what is its name
401 RemotePlayer testplayer(m_gamedef);
403 // Open file and deserialize
404 std::ifstream is(path.c_str(), std::ios_base::binary);
405 if(is.good() == false)
407 infostream<<"Failed to read "<<path<<std::endl;
410 testplayer.deSerialize(is, player_files[i].name);
413 //infostream<<"Loaded test player with name "<<testplayer.getName()<<std::endl;
415 // Search for the player
416 std::string playername = testplayer.getName();
417 Player *player = getPlayer(playername.c_str());
420 infostream<<"Didn't find matching player, ignoring file "<<path<<std::endl;
424 //infostream<<"Found matching player, overwriting."<<std::endl;
426 // OK, found. Save player there.
427 if(player->checkModified())
429 // Open file and serialize
430 std::ostringstream ss(std::ios_base::binary);
431 player->serialize(ss);
432 if(!fs::safeWriteToFile(path, ss.str()))
434 infostream<<"Failed to write "<<path<<std::endl;
437 saved_players.insert(player);
439 saved_players.insert(player);
443 for(std::list<Player*>::iterator i = m_players.begin();
444 i != m_players.end(); ++i)
447 if(saved_players.find(player) != saved_players.end())
449 /*infostream<<"Player "<<player->getName()
450 <<" was already saved."<<std::endl;*/
453 std::string playername = player->getName();
454 // Don't save unnamed player
457 //infostream<<"Not saving unnamed player."<<std::endl;
463 if(string_allowed(playername, PLAYERNAME_ALLOWED_CHARS) == false)
464 playername = "player";
465 std::string path = players_path + "/" + playername;
467 for(u32 i=0; i<1000; i++)
469 if(fs::PathExists(path) == false)
474 path = players_path + "/" + playername + itos(i);
478 infostream<<"Didn't find free file for player"<<std::endl;
483 /*infostream<<"Saving player "<<player->getName()<<" to "
485 // Open file and serialize
486 std::ostringstream ss(std::ios_base::binary);
487 player->serialize(ss);
488 if(!fs::safeWriteToFile(path, ss.str()))
490 infostream<<"Failed to write "<<path<<std::endl;
493 saved_players.insert(player);
497 //infostream<<"Saved "<<saved_players.size()<<" players."<<std::endl;
500 void ServerEnvironment::deSerializePlayers(const std::string &savedir)
502 std::string players_path = savedir + "/players";
504 std::vector<fs::DirListNode> player_files = fs::GetDirListing(players_path);
505 for(u32 i=0; i<player_files.size(); i++)
507 if(player_files[i].dir)
510 // Full path to this file
511 std::string path = players_path + "/" + player_files[i].name;
513 //infostream<<"Checking player file "<<path<<std::endl;
515 // Load player to see what is its name
516 RemotePlayer testplayer(m_gamedef);
518 // Open file and deserialize
519 std::ifstream is(path.c_str(), std::ios_base::binary);
520 if(is.good() == false)
522 infostream<<"Failed to read "<<path<<std::endl;
525 testplayer.deSerialize(is, player_files[i].name);
528 if(!string_allowed(testplayer.getName(), PLAYERNAME_ALLOWED_CHARS))
530 infostream<<"Not loading player with invalid name: "
531 <<testplayer.getName()<<std::endl;
534 /*infostream<<"Loaded test player with name "<<testplayer.getName()
537 // Search for the player
538 std::string playername = testplayer.getName();
539 Player *player = getPlayer(playername.c_str());
540 bool newplayer = false;
543 //infostream<<"Is a new player"<<std::endl;
544 player = new RemotePlayer(m_gamedef);
550 verbosestream<<"Reading player "<<testplayer.getName()<<" from "
552 // Open file and deserialize
553 std::ifstream is(path.c_str(), std::ios_base::binary);
554 if(is.good() == false)
556 infostream<<"Failed to read "<<path<<std::endl;
559 player->deSerialize(is, player_files[i].name);
569 void ServerEnvironment::saveMeta(const std::string &savedir)
571 std::string path = savedir + "/env_meta.txt";
573 // Open file and serialize
574 std::ostringstream ss(std::ios_base::binary);
577 args.setU64("game_time", m_game_time);
578 args.setU64("time_of_day", getTimeOfDay());
582 if(!fs::safeWriteToFile(path, ss.str()))
584 infostream<<"ServerEnvironment::saveMeta(): Failed to write "
586 throw SerializationError("Couldn't save env meta");
590 void ServerEnvironment::loadMeta(const std::string &savedir)
592 std::string path = savedir + "/env_meta.txt";
594 // Open file and deserialize
595 std::ifstream is(path.c_str(), std::ios_base::binary);
596 if(is.good() == false)
598 infostream<<"ServerEnvironment::loadMeta(): Failed to open "
600 throw SerializationError("Couldn't load env meta");
608 throw SerializationError
609 ("ServerEnvironment::loadMeta(): EnvArgsEnd not found");
611 std::getline(is, line);
612 std::string trimmedline = trim(line);
613 if(trimmedline == "EnvArgsEnd")
615 args.parseConfigLine(line);
619 m_game_time = args.getU64("game_time");
620 }catch(SettingNotFoundException &e){
621 // Getting this is crucial, otherwise timestamps are useless
622 throw SerializationError("Couldn't load env meta game_time");
626 m_time_of_day = args.getU64("time_of_day");
627 }catch(SettingNotFoundException &e){
628 // This is not as important
629 m_time_of_day = 9000;
635 ActiveBlockModifier *abm;
637 std::set<content_t> required_neighbors;
643 ServerEnvironment *m_env;
644 std::map<content_t, std::list<ActiveABM> > m_aabms;
646 ABMHandler(std::list<ABMWithState> &abms,
647 float dtime_s, ServerEnvironment *env,
653 INodeDefManager *ndef = env->getGameDef()->ndef();
654 for(std::list<ABMWithState>::iterator
655 i = abms.begin(); i != abms.end(); ++i){
656 ActiveBlockModifier *abm = i->abm;
657 float trigger_interval = abm->getTriggerInterval();
658 if(trigger_interval < 0.001)
659 trigger_interval = 0.001;
660 float actual_interval = dtime_s;
663 if(i->timer < trigger_interval)
665 i->timer -= trigger_interval;
666 actual_interval = trigger_interval;
668 float intervals = actual_interval / trigger_interval;
671 float chance = abm->getTriggerChance();
676 aabm.chance = chance / intervals;
680 std::set<std::string> required_neighbors_s
681 = abm->getRequiredNeighbors();
682 for(std::set<std::string>::iterator
683 i = required_neighbors_s.begin();
684 i != required_neighbors_s.end(); i++)
686 ndef->getIds(*i, aabm.required_neighbors);
689 std::set<std::string> contents_s = abm->getTriggerContents();
690 for(std::set<std::string>::iterator
691 i = contents_s.begin(); i != contents_s.end(); i++)
693 std::set<content_t> ids;
694 ndef->getIds(*i, ids);
695 for(std::set<content_t>::const_iterator k = ids.begin();
699 std::map<content_t, std::list<ActiveABM> >::iterator j;
701 if(j == m_aabms.end()){
702 std::list<ActiveABM> aabmlist;
703 m_aabms[c] = aabmlist;
706 j->second.push_back(aabm);
711 // Find out how many objects the given block and its neighbours contain.
712 // Returns the number of objects in the block, and also in 'wider' the
713 // number of objects in the block and all its neighbours. The latter
714 // may an estimate if any neighbours are unloaded.
715 u32 countObjects(MapBlock *block, ServerMap * map, u32 &wider)
718 u32 wider_unknown_count = 0;
719 for(s16 x=-1; x<=1; x++)
720 for(s16 y=-1; y<=1; y++)
721 for(s16 z=-1; z<=1; z++)
723 MapBlock *block2 = map->getBlockNoCreateNoEx(
724 block->getPos() + v3s16(x,y,z));
726 wider_unknown_count++;
729 wider += block2->m_static_objects.m_active.size()
730 + block2->m_static_objects.m_stored.size();
733 u32 active_object_count = block->m_static_objects.m_active.size();
734 u32 wider_known_count = 3*3*3 - wider_unknown_count;
735 wider += wider_unknown_count * wider / wider_known_count;
736 return active_object_count;
739 void apply(MapBlock *block)
744 ServerMap *map = &m_env->getServerMap();
746 u32 active_object_count_wider;
747 u32 active_object_count = this->countObjects(block, map, active_object_count_wider);
748 m_env->m_added_objects = 0;
751 for(p0.X=0; p0.X<MAP_BLOCKSIZE; p0.X++)
752 for(p0.Y=0; p0.Y<MAP_BLOCKSIZE; p0.Y++)
753 for(p0.Z=0; p0.Z<MAP_BLOCKSIZE; p0.Z++)
755 MapNode n = block->getNodeNoEx(p0);
756 content_t c = n.getContent();
757 v3s16 p = p0 + block->getPosRelative();
759 std::map<content_t, std::list<ActiveABM> >::iterator j;
761 if(j == m_aabms.end())
764 for(std::list<ActiveABM>::iterator
765 i = j->second.begin(); i != j->second.end(); i++)
767 if(myrand() % i->chance != 0)
771 if(!i->required_neighbors.empty())
774 for(p1.X = p.X-1; p1.X <= p.X+1; p1.X++)
775 for(p1.Y = p.Y-1; p1.Y <= p.Y+1; p1.Y++)
776 for(p1.Z = p.Z-1; p1.Z <= p.Z+1; p1.Z++)
780 MapNode n = map->getNodeNoEx(p1);
781 content_t c = n.getContent();
782 std::set<content_t>::const_iterator k;
783 k = i->required_neighbors.find(c);
784 if(k != i->required_neighbors.end()){
788 // No required neighbor found
793 // Call all the trigger variations
794 i->abm->trigger(m_env, p, n);
795 i->abm->trigger(m_env, p, n,
796 active_object_count, active_object_count_wider);
798 // Count surrounding objects again if the abms added any
799 if(m_env->m_added_objects > 0) {
800 active_object_count = countObjects(block, map, active_object_count_wider);
801 m_env->m_added_objects = 0;
808 void ServerEnvironment::activateBlock(MapBlock *block, u32 additional_dtime)
810 // Reset usage timer immediately, otherwise a block that becomes active
811 // again at around the same time as it would normally be unloaded will
812 // get unloaded incorrectly. (I think this still leaves a small possibility
813 // of a race condition between this and server::AsyncRunStep, which only
814 // some kind of synchronisation will fix, but it at least reduces the window
815 // of opportunity for it to break from seconds to nanoseconds)
816 block->resetUsageTimer();
818 // Get time difference
820 u32 stamp = block->getTimestamp();
821 if(m_game_time > stamp && stamp != BLOCK_TIMESTAMP_UNDEFINED)
822 dtime_s = m_game_time - block->getTimestamp();
823 dtime_s += additional_dtime;
825 /*infostream<<"ServerEnvironment::activateBlock(): block timestamp: "
826 <<stamp<<", game time: "<<m_game_time<<std::endl;*/
828 // Set current time as timestamp
829 block->setTimestampNoChangedFlag(m_game_time);
831 /*infostream<<"ServerEnvironment::activateBlock(): block is "
832 <<dtime_s<<" seconds old."<<std::endl;*/
834 // Activate stored objects
835 activateObjects(block, dtime_s);
838 std::map<v3s16, NodeTimer> elapsed_timers =
839 block->m_node_timers.step((float)dtime_s);
840 if(!elapsed_timers.empty()){
842 for(std::map<v3s16, NodeTimer>::iterator
843 i = elapsed_timers.begin();
844 i != elapsed_timers.end(); i++){
845 n = block->getNodeNoEx(i->first);
846 v3s16 p = i->first + block->getPosRelative();
847 if(m_script->node_on_timer(p,n,i->second.elapsed))
848 block->setNodeTimer(i->first,NodeTimer(i->second.timeout,0));
852 /* Handle ActiveBlockModifiers */
853 ABMHandler abmhandler(m_abms, dtime_s, this, false);
854 abmhandler.apply(block);
857 void ServerEnvironment::addActiveBlockModifier(ActiveBlockModifier *abm)
859 m_abms.push_back(ABMWithState(abm));
862 bool ServerEnvironment::setNode(v3s16 p, const MapNode &n)
864 INodeDefManager *ndef = m_gamedef->ndef();
865 MapNode n_old = m_map->getNodeNoEx(p);
867 if(ndef->get(n_old).has_on_destruct)
868 m_script->node_on_destruct(p, n_old);
870 bool succeeded = m_map->addNodeWithEvent(p, n);
873 // Call post-destructor
874 if(ndef->get(n_old).has_after_destruct)
875 m_script->node_after_destruct(p, n_old);
877 if(ndef->get(n).has_on_construct)
878 m_script->node_on_construct(p, n);
882 bool ServerEnvironment::removeNode(v3s16 p)
884 INodeDefManager *ndef = m_gamedef->ndef();
885 MapNode n_old = m_map->getNodeNoEx(p);
887 if(ndef->get(n_old).has_on_destruct)
888 m_script->node_on_destruct(p, n_old);
890 // This is slightly optimized compared to addNodeWithEvent(air)
891 bool succeeded = m_map->removeNodeWithEvent(p);
894 // Call post-destructor
895 if(ndef->get(n_old).has_after_destruct)
896 m_script->node_after_destruct(p, n_old);
897 // Air doesn't require constructor
901 bool ServerEnvironment::swapNode(v3s16 p, const MapNode &n)
903 return m_map->addNodeWithEvent(p, n, false);
906 std::set<u16> ServerEnvironment::getObjectsInsideRadius(v3f pos, float radius)
908 std::set<u16> objects;
909 for(std::map<u16, ServerActiveObject*>::iterator
910 i = m_active_objects.begin();
911 i != m_active_objects.end(); ++i)
913 ServerActiveObject* obj = i->second;
915 v3f objectpos = obj->getBasePosition();
916 if(objectpos.getDistanceFrom(pos) > radius)
923 void ServerEnvironment::clearAllObjects()
925 infostream<<"ServerEnvironment::clearAllObjects(): "
926 <<"Removing all active objects"<<std::endl;
927 std::list<u16> objects_to_remove;
928 for(std::map<u16, ServerActiveObject*>::iterator
929 i = m_active_objects.begin();
930 i != m_active_objects.end(); ++i)
932 ServerActiveObject* obj = i->second;
933 if(obj->getType() == ACTIVEOBJECT_TYPE_PLAYER)
936 // Delete static object if block is loaded
937 if(obj->m_static_exists){
938 MapBlock *block = m_map->getBlockNoCreateNoEx(obj->m_static_block);
940 block->m_static_objects.remove(id);
941 block->raiseModified(MOD_STATE_WRITE_NEEDED,
943 obj->m_static_exists = false;
946 // If known by some client, don't delete immediately
947 if(obj->m_known_by_count > 0){
948 obj->m_pending_deactivation = true;
949 obj->m_removed = true;
953 // Tell the object about removal
954 obj->removingFromEnvironment();
955 // Deregister in scripting api
956 m_script->removeObjectReference(obj);
958 // Delete active object
959 if(obj->environmentDeletes())
961 // Id to be removed from m_active_objects
962 objects_to_remove.push_back(id);
964 // Remove references from m_active_objects
965 for(std::list<u16>::iterator i = objects_to_remove.begin();
966 i != objects_to_remove.end(); ++i)
968 m_active_objects.erase(*i);
971 // Get list of loaded blocks
972 std::list<v3s16> loaded_blocks;
973 infostream<<"ServerEnvironment::clearAllObjects(): "
974 <<"Listing all loaded blocks"<<std::endl;
975 m_map->listAllLoadedBlocks(loaded_blocks);
976 infostream<<"ServerEnvironment::clearAllObjects(): "
977 <<"Done listing all loaded blocks: "
978 <<loaded_blocks.size()<<std::endl;
980 // Get list of loadable blocks
981 std::list<v3s16> loadable_blocks;
982 infostream<<"ServerEnvironment::clearAllObjects(): "
983 <<"Listing all loadable blocks"<<std::endl;
984 m_map->listAllLoadableBlocks(loadable_blocks);
985 infostream<<"ServerEnvironment::clearAllObjects(): "
986 <<"Done listing all loadable blocks: "
987 <<loadable_blocks.size()
988 <<", now clearing"<<std::endl;
990 // Grab a reference on each loaded block to avoid unloading it
991 for(std::list<v3s16>::iterator i = loaded_blocks.begin();
992 i != loaded_blocks.end(); ++i)
995 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1000 // Remove objects in all loadable blocks
1001 u32 unload_interval = g_settings->getS32("max_clearobjects_extra_loaded_blocks");
1002 unload_interval = MYMAX(unload_interval, 1);
1003 u32 report_interval = loadable_blocks.size() / 10;
1004 u32 num_blocks_checked = 0;
1005 u32 num_blocks_cleared = 0;
1006 u32 num_objs_cleared = 0;
1007 for(std::list<v3s16>::iterator i = loadable_blocks.begin();
1008 i != loadable_blocks.end(); ++i)
1011 MapBlock *block = m_map->emergeBlock(p, false);
1013 errorstream<<"ServerEnvironment::clearAllObjects(): "
1014 <<"Failed to emerge block "<<PP(p)<<std::endl;
1017 u32 num_stored = block->m_static_objects.m_stored.size();
1018 u32 num_active = block->m_static_objects.m_active.size();
1019 if(num_stored != 0 || num_active != 0){
1020 block->m_static_objects.m_stored.clear();
1021 block->m_static_objects.m_active.clear();
1022 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1024 num_objs_cleared += num_stored + num_active;
1025 num_blocks_cleared++;
1027 num_blocks_checked++;
1029 if(report_interval != 0 &&
1030 num_blocks_checked % report_interval == 0){
1031 float percent = 100.0 * (float)num_blocks_checked /
1032 loadable_blocks.size();
1033 infostream<<"ServerEnvironment::clearAllObjects(): "
1034 <<"Cleared "<<num_objs_cleared<<" objects"
1035 <<" in "<<num_blocks_cleared<<" blocks ("
1036 <<percent<<"%)"<<std::endl;
1038 if(num_blocks_checked % unload_interval == 0){
1039 m_map->unloadUnreferencedBlocks();
1042 m_map->unloadUnreferencedBlocks();
1044 // Drop references that were added above
1045 for(std::list<v3s16>::iterator i = loaded_blocks.begin();
1046 i != loaded_blocks.end(); ++i)
1049 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1054 infostream<<"ServerEnvironment::clearAllObjects(): "
1055 <<"Finished: Cleared "<<num_objs_cleared<<" objects"
1056 <<" in "<<num_blocks_cleared<<" blocks"<<std::endl;
1059 void ServerEnvironment::step(float dtime)
1061 DSTACK(__FUNCTION_NAME);
1063 //TimeTaker timer("ServerEnv step");
1065 /* Step time of day */
1066 stepTimeOfDay(dtime);
1069 // NOTE: This is kind of funny on a singleplayer game, but doesn't
1070 // really matter that much.
1071 m_recommended_send_interval = g_settings->getFloat("dedicated_server_step");
1077 m_game_time_fraction_counter += dtime;
1078 u32 inc_i = (u32)m_game_time_fraction_counter;
1079 m_game_time += inc_i;
1080 m_game_time_fraction_counter -= (float)inc_i;
1087 ScopeProfiler sp(g_profiler, "SEnv: handle players avg", SPT_AVG);
1088 for(std::list<Player*>::iterator i = m_players.begin();
1089 i != m_players.end(); ++i)
1091 Player *player = *i;
1093 // Ignore disconnected players
1094 if(player->peer_id == 0)
1098 player->move(dtime, this, 100*BS);
1103 Manage active block list
1105 if(m_active_blocks_management_interval.step(dtime, 2.0))
1107 ScopeProfiler sp(g_profiler, "SEnv: manage act. block list avg /2s", SPT_AVG);
1109 Get player block positions
1111 std::list<v3s16> players_blockpos;
1112 for(std::list<Player*>::iterator
1113 i = m_players.begin();
1114 i != m_players.end(); ++i)
1116 Player *player = *i;
1117 // Ignore disconnected players
1118 if(player->peer_id == 0)
1120 v3s16 blockpos = getNodeBlockPos(
1121 floatToInt(player->getPosition(), BS));
1122 players_blockpos.push_back(blockpos);
1126 Update list of active blocks, collecting changes
1128 const s16 active_block_range = g_settings->getS16("active_block_range");
1129 std::set<v3s16> blocks_removed;
1130 std::set<v3s16> blocks_added;
1131 m_active_blocks.update(players_blockpos, active_block_range,
1132 blocks_removed, blocks_added);
1135 Handle removed blocks
1138 // Convert active objects that are no more in active blocks to static
1139 deactivateFarObjects(false);
1141 for(std::set<v3s16>::iterator
1142 i = blocks_removed.begin();
1143 i != blocks_removed.end(); ++i)
1147 /* infostream<<"Server: Block " << PP(p)
1148 << " became inactive"<<std::endl; */
1150 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1154 // Set current time as timestamp (and let it set ChangedFlag)
1155 block->setTimestamp(m_game_time);
1162 for(std::set<v3s16>::iterator
1163 i = blocks_added.begin();
1164 i != blocks_added.end(); ++i)
1168 MapBlock *block = m_map->getBlockOrEmerge(p);
1170 m_active_blocks.m_list.erase(p);
1174 activateBlock(block);
1175 /* infostream<<"Server: Block " << PP(p)
1176 << " became active"<<std::endl; */
1181 Mess around in active blocks
1183 if(m_active_blocks_nodemetadata_interval.step(dtime, 1.0))
1185 ScopeProfiler sp(g_profiler, "SEnv: mess in act. blocks avg /1s", SPT_AVG);
1189 for(std::set<v3s16>::iterator
1190 i = m_active_blocks.m_list.begin();
1191 i != m_active_blocks.m_list.end(); ++i)
1195 /*infostream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
1196 <<") being handled"<<std::endl;*/
1198 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1202 // Reset block usage timer
1203 block->resetUsageTimer();
1205 // Set current time as timestamp
1206 block->setTimestampNoChangedFlag(m_game_time);
1207 // If time has changed much from the one on disk,
1208 // set block to be saved when it is unloaded
1209 if(block->getTimestamp() > block->getDiskTimestamp() + 60)
1210 block->raiseModified(MOD_STATE_WRITE_AT_UNLOAD,
1211 "Timestamp older than 60s (step)");
1214 std::map<v3s16, NodeTimer> elapsed_timers =
1215 block->m_node_timers.step((float)dtime);
1216 if(!elapsed_timers.empty()){
1218 for(std::map<v3s16, NodeTimer>::iterator
1219 i = elapsed_timers.begin();
1220 i != elapsed_timers.end(); i++){
1221 n = block->getNodeNoEx(i->first);
1222 p = i->first + block->getPosRelative();
1223 if(m_script->node_on_timer(p,n,i->second.elapsed))
1224 block->setNodeTimer(i->first,NodeTimer(i->second.timeout,0));
1230 const float abm_interval = 1.0;
1231 if(m_active_block_modifier_interval.step(dtime, abm_interval))
1233 if(m_active_block_interval_overload_skip > 0){
1234 ScopeProfiler sp(g_profiler, "SEnv: ABM overload skips");
1235 m_active_block_interval_overload_skip--;
1238 ScopeProfiler sp(g_profiler, "SEnv: modify in blocks avg /1s", SPT_AVG);
1239 TimeTaker timer("modify in active blocks");
1241 // Initialize handling of ActiveBlockModifiers
1242 ABMHandler abmhandler(m_abms, abm_interval, this, true);
1244 for(std::set<v3s16>::iterator
1245 i = m_active_blocks.m_list.begin();
1246 i != m_active_blocks.m_list.end(); ++i)
1250 /*infostream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
1251 <<") being handled"<<std::endl;*/
1253 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1257 // Set current time as timestamp
1258 block->setTimestampNoChangedFlag(m_game_time);
1260 /* Handle ActiveBlockModifiers */
1261 abmhandler.apply(block);
1264 u32 time_ms = timer.stop(true);
1265 u32 max_time_ms = 200;
1266 if(time_ms > max_time_ms){
1267 infostream<<"WARNING: active block modifiers took "
1268 <<time_ms<<"ms (longer than "
1269 <<max_time_ms<<"ms)"<<std::endl;
1270 m_active_block_interval_overload_skip = (time_ms / max_time_ms) + 1;
1275 Step script environment (run global on_step())
1277 m_script->environment_Step(dtime);
1283 ScopeProfiler sp(g_profiler, "SEnv: step act. objs avg", SPT_AVG);
1284 //TimeTaker timer("Step active objects");
1286 g_profiler->avg("SEnv: num of objects", m_active_objects.size());
1288 // This helps the objects to send data at the same time
1289 bool send_recommended = false;
1290 m_send_recommended_timer += dtime;
1291 if(m_send_recommended_timer > getSendRecommendedInterval())
1293 m_send_recommended_timer -= getSendRecommendedInterval();
1294 send_recommended = true;
1297 for(std::map<u16, ServerActiveObject*>::iterator
1298 i = m_active_objects.begin();
1299 i != m_active_objects.end(); ++i)
1301 ServerActiveObject* obj = i->second;
1302 // Remove non-peaceful mobs on peaceful mode
1303 if(g_settings->getBool("only_peaceful_mobs")){
1304 if(!obj->isPeaceful())
1305 obj->m_removed = true;
1307 // Don't step if is to be removed or stored statically
1308 if(obj->m_removed || obj->m_pending_deactivation)
1311 obj->step(dtime, send_recommended);
1312 // Read messages from object
1313 while(!obj->m_messages_out.empty())
1315 m_active_object_messages.push_back(
1316 obj->m_messages_out.pop_front());
1322 Manage active objects
1324 if(m_object_management_interval.step(dtime, 0.5))
1326 ScopeProfiler sp(g_profiler, "SEnv: remove removed objs avg /.5s", SPT_AVG);
1328 Remove objects that satisfy (m_removed && m_known_by_count==0)
1330 removeRemovedObjects();
1334 ServerActiveObject* ServerEnvironment::getActiveObject(u16 id)
1336 std::map<u16, ServerActiveObject*>::iterator n;
1337 n = m_active_objects.find(id);
1338 if(n == m_active_objects.end())
1343 bool isFreeServerActiveObjectId(u16 id,
1344 std::map<u16, ServerActiveObject*> &objects)
1349 return objects.find(id) == objects.end();
1352 u16 getFreeServerActiveObjectId(
1353 std::map<u16, ServerActiveObject*> &objects)
1355 //try to reuse id's as late as possible
1356 static u16 last_used_id = 0;
1357 u16 startid = last_used_id;
1361 if(isFreeServerActiveObjectId(last_used_id, objects))
1362 return last_used_id;
1364 if(last_used_id == startid)
1369 u16 ServerEnvironment::addActiveObject(ServerActiveObject *object)
1373 u16 id = addActiveObjectRaw(object, true, 0);
1378 bool ServerEnvironment::addActiveObjectAsStatic(ServerActiveObject *obj)
1382 v3f objectpos = obj->getBasePosition();
1384 // The block in which the object resides in
1385 v3s16 blockpos_o = getNodeBlockPos(floatToInt(objectpos, BS));
1388 Update the static data
1391 // Create new static object
1392 std::string staticdata = obj->getStaticData();
1393 StaticObject s_obj(obj->getType(), objectpos, staticdata);
1394 // Add to the block where the object is located in
1395 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1396 // Get or generate the block
1397 MapBlock *block = m_map->emergeBlock(blockpos);
1399 bool succeeded = false;
1403 block->m_static_objects.insert(0, s_obj);
1404 block->raiseModified(MOD_STATE_WRITE_AT_UNLOAD,
1405 "addActiveObjectAsStatic");
1409 infostream<<"ServerEnvironment::addActiveObjectAsStatic: "
1410 <<"Could not find or generate "
1411 <<"a block for storing static object"<<std::endl;
1415 if(obj->environmentDeletes())
1423 Finds out what new objects have been added to
1424 inside a radius around a position
1426 void ServerEnvironment::getAddedActiveObjects(v3s16 pos, s16 radius,
1427 std::set<u16> ¤t_objects,
1428 std::set<u16> &added_objects)
1430 v3f pos_f = intToFloat(pos, BS);
1431 f32 radius_f = radius * BS;
1433 Go through the object list,
1434 - discard m_removed objects,
1435 - discard objects that are too far away,
1436 - discard objects that are found in current_objects.
1437 - add remaining objects to added_objects
1439 for(std::map<u16, ServerActiveObject*>::iterator
1440 i = m_active_objects.begin();
1441 i != m_active_objects.end(); ++i)
1445 ServerActiveObject *object = i->second;
1448 // Discard if removed or deactivating
1449 if(object->m_removed || object->m_pending_deactivation)
1451 if(object->unlimitedTransferDistance() == false){
1452 // Discard if too far
1453 f32 distance_f = object->getBasePosition().getDistanceFrom(pos_f);
1454 if(distance_f > radius_f)
1457 // Discard if already on current_objects
1458 std::set<u16>::iterator n;
1459 n = current_objects.find(id);
1460 if(n != current_objects.end())
1462 // Add to added_objects
1463 added_objects.insert(id);
1468 Finds out what objects have been removed from
1469 inside a radius around a position
1471 void ServerEnvironment::getRemovedActiveObjects(v3s16 pos, s16 radius,
1472 std::set<u16> ¤t_objects,
1473 std::set<u16> &removed_objects)
1475 v3f pos_f = intToFloat(pos, BS);
1476 f32 radius_f = radius * BS;
1478 Go through current_objects; object is removed if:
1479 - object is not found in m_active_objects (this is actually an
1480 error condition; objects should be set m_removed=true and removed
1481 only after all clients have been informed about removal), or
1482 - object has m_removed=true, or
1483 - object is too far away
1485 for(std::set<u16>::iterator
1486 i = current_objects.begin();
1487 i != current_objects.end(); ++i)
1490 ServerActiveObject *object = getActiveObject(id);
1493 infostream<<"ServerEnvironment::getRemovedActiveObjects():"
1494 <<" object in current_objects is NULL"<<std::endl;
1495 removed_objects.insert(id);
1499 if(object->m_removed || object->m_pending_deactivation)
1501 removed_objects.insert(id);
1505 // If transfer distance is unlimited, don't remove
1506 if(object->unlimitedTransferDistance())
1509 f32 distance_f = object->getBasePosition().getDistanceFrom(pos_f);
1511 if(distance_f >= radius_f)
1513 removed_objects.insert(id);
1521 ActiveObjectMessage ServerEnvironment::getActiveObjectMessage()
1523 if(m_active_object_messages.empty())
1524 return ActiveObjectMessage(0);
1526 ActiveObjectMessage message = m_active_object_messages.front();
1527 m_active_object_messages.pop_front();
1532 ************ Private methods *************
1535 u16 ServerEnvironment::addActiveObjectRaw(ServerActiveObject *object,
1536 bool set_changed, u32 dtime_s)
1539 if(object->getId() == 0){
1540 u16 new_id = getFreeServerActiveObjectId(m_active_objects);
1543 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1544 <<"no free ids available"<<std::endl;
1545 if(object->environmentDeletes())
1549 object->setId(new_id);
1552 verbosestream<<"ServerEnvironment::addActiveObjectRaw(): "
1553 <<"supplied with id "<<object->getId()<<std::endl;
1555 if(isFreeServerActiveObjectId(object->getId(), m_active_objects) == false)
1557 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1558 <<"id is not free ("<<object->getId()<<")"<<std::endl;
1559 if(object->environmentDeletes())
1563 /*infostream<<"ServerEnvironment::addActiveObjectRaw(): "
1564 <<"added (id="<<object->getId()<<")"<<std::endl;*/
1566 m_active_objects[object->getId()] = object;
1568 verbosestream<<"ServerEnvironment::addActiveObjectRaw(): "
1569 <<"Added id="<<object->getId()<<"; there are now "
1570 <<m_active_objects.size()<<" active objects."
1573 // Register reference in scripting api (must be done before post-init)
1574 m_script->addObjectReference(object);
1575 // Post-initialize object
1576 object->addedToEnvironment(dtime_s);
1578 // Add static data to block
1579 if(object->isStaticAllowed())
1581 // Add static object to active static list of the block
1582 v3f objectpos = object->getBasePosition();
1583 std::string staticdata = object->getStaticData();
1584 StaticObject s_obj(object->getType(), objectpos, staticdata);
1585 // Add to the block where the object is located in
1586 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1587 MapBlock *block = m_map->emergeBlock(blockpos);
1589 block->m_static_objects.m_active[object->getId()] = s_obj;
1590 object->m_static_exists = true;
1591 object->m_static_block = blockpos;
1594 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1595 "addActiveObjectRaw");
1597 v3s16 p = floatToInt(objectpos, BS);
1598 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1599 <<"could not emerge block for storing id="<<object->getId()
1600 <<" statically (pos="<<PP(p)<<")"<<std::endl;
1604 return object->getId();
1608 Remove objects that satisfy (m_removed && m_known_by_count==0)
1610 void ServerEnvironment::removeRemovedObjects()
1612 std::list<u16> objects_to_remove;
1613 for(std::map<u16, ServerActiveObject*>::iterator
1614 i = m_active_objects.begin();
1615 i != m_active_objects.end(); ++i)
1618 ServerActiveObject* obj = i->second;
1619 // This shouldn't happen but check it
1622 infostream<<"NULL object found in ServerEnvironment"
1623 <<" while finding removed objects. id="<<id<<std::endl;
1624 // Id to be removed from m_active_objects
1625 objects_to_remove.push_back(id);
1630 We will delete objects that are marked as removed or thatare
1631 waiting for deletion after deactivation
1633 if(obj->m_removed == false && obj->m_pending_deactivation == false)
1637 Delete static data from block if is marked as removed
1639 if(obj->m_static_exists && obj->m_removed)
1641 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
1643 block->m_static_objects.remove(id);
1644 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1645 "removeRemovedObjects/remove");
1646 obj->m_static_exists = false;
1648 infostream<<"Failed to emerge block from which an object to "
1649 <<"be removed was loaded from. id="<<id<<std::endl;
1653 // If m_known_by_count > 0, don't actually remove. On some future
1654 // invocation this will be 0, which is when removal will continue.
1655 if(obj->m_known_by_count > 0)
1659 Move static data from active to stored if not marked as removed
1661 if(obj->m_static_exists && !obj->m_removed){
1662 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
1664 std::map<u16, StaticObject>::iterator i =
1665 block->m_static_objects.m_active.find(id);
1666 if(i != block->m_static_objects.m_active.end()){
1667 block->m_static_objects.m_stored.push_back(i->second);
1668 block->m_static_objects.m_active.erase(id);
1669 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1670 "removeRemovedObjects/deactivate");
1673 infostream<<"Failed to emerge block from which an object to "
1674 <<"be deactivated was loaded from. id="<<id<<std::endl;
1678 // Tell the object about removal
1679 obj->removingFromEnvironment();
1680 // Deregister in scripting api
1681 m_script->removeObjectReference(obj);
1684 if(obj->environmentDeletes())
1686 // Id to be removed from m_active_objects
1687 objects_to_remove.push_back(id);
1689 // Remove references from m_active_objects
1690 for(std::list<u16>::iterator i = objects_to_remove.begin();
1691 i != objects_to_remove.end(); ++i)
1693 m_active_objects.erase(*i);
1697 static void print_hexdump(std::ostream &o, const std::string &data)
1699 const int linelength = 16;
1700 for(int l=0; ; l++){
1701 int i0 = linelength * l;
1702 bool at_end = false;
1703 int thislinelength = linelength;
1704 if(i0 + thislinelength > (int)data.size()){
1705 thislinelength = data.size() - i0;
1708 for(int di=0; di<linelength; di++){
1711 if(di<thislinelength)
1712 snprintf(buf, 4, "%.2x ", data[i]);
1714 snprintf(buf, 4, " ");
1718 for(int di=0; di<thislinelength; di++){
1732 Convert stored objects from blocks near the players to active.
1734 void ServerEnvironment::activateObjects(MapBlock *block, u32 dtime_s)
1738 // Ignore if no stored objects (to not set changed flag)
1739 if(block->m_static_objects.m_stored.size() == 0)
1741 verbosestream<<"ServerEnvironment::activateObjects(): "
1742 <<"activating objects of block "<<PP(block->getPos())
1743 <<" ("<<block->m_static_objects.m_stored.size()
1744 <<" objects)"<<std::endl;
1745 bool large_amount = (block->m_static_objects.m_stored.size() > g_settings->getU16("max_objects_per_block"));
1747 errorstream<<"suspiciously large amount of objects detected: "
1748 <<block->m_static_objects.m_stored.size()<<" in "
1749 <<PP(block->getPos())
1750 <<"; removing all of them."<<std::endl;
1751 // Clear stored list
1752 block->m_static_objects.m_stored.clear();
1753 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1754 "stored list cleared in activateObjects due to "
1755 "large amount of objects");
1759 // Activate stored objects
1760 std::list<StaticObject> new_stored;
1761 for(std::list<StaticObject>::iterator
1762 i = block->m_static_objects.m_stored.begin();
1763 i != block->m_static_objects.m_stored.end(); ++i)
1765 /*infostream<<"Server: Creating an active object from "
1766 <<"static data"<<std::endl;*/
1767 StaticObject &s_obj = *i;
1768 // Create an active object from the data
1769 ServerActiveObject *obj = ServerActiveObject::create
1770 (s_obj.type, this, 0, s_obj.pos, s_obj.data);
1771 // If couldn't create object, store static data back.
1774 errorstream<<"ServerEnvironment::activateObjects(): "
1775 <<"failed to create active object from static object "
1776 <<"in block "<<PP(s_obj.pos/BS)
1777 <<" type="<<(int)s_obj.type<<" data:"<<std::endl;
1778 print_hexdump(verbosestream, s_obj.data);
1780 new_stored.push_back(s_obj);
1783 verbosestream<<"ServerEnvironment::activateObjects(): "
1784 <<"activated static object pos="<<PP(s_obj.pos/BS)
1785 <<" type="<<(int)s_obj.type<<std::endl;
1786 // This will also add the object to the active static list
1787 addActiveObjectRaw(obj, false, dtime_s);
1789 // Clear stored list
1790 block->m_static_objects.m_stored.clear();
1791 // Add leftover failed stuff to stored list
1792 for(std::list<StaticObject>::iterator
1793 i = new_stored.begin();
1794 i != new_stored.end(); ++i)
1796 StaticObject &s_obj = *i;
1797 block->m_static_objects.m_stored.push_back(s_obj);
1800 // Turn the active counterparts of activated objects not pending for
1802 for(std::map<u16, StaticObject>::iterator
1803 i = block->m_static_objects.m_active.begin();
1804 i != block->m_static_objects.m_active.end(); ++i)
1807 ServerActiveObject *object = getActiveObject(id);
1809 object->m_pending_deactivation = false;
1813 Note: Block hasn't really been modified here.
1814 The objects have just been activated and moved from the stored
1815 static list to the active static list.
1816 As such, the block is essentially the same.
1817 Thus, do not call block->raiseModified(MOD_STATE_WRITE_NEEDED).
1818 Otherwise there would be a huge amount of unnecessary I/O.
1823 Convert objects that are not standing inside active blocks to static.
1825 If m_known_by_count != 0, active object is not deleted, but static
1826 data is still updated.
1828 If force_delete is set, active object is deleted nevertheless. It
1829 shall only be set so in the destructor of the environment.
1831 If block wasn't generated (not in memory or on disk),
1833 void ServerEnvironment::deactivateFarObjects(bool force_delete)
1835 std::list<u16> objects_to_remove;
1836 for(std::map<u16, ServerActiveObject*>::iterator
1837 i = m_active_objects.begin();
1838 i != m_active_objects.end(); ++i)
1840 ServerActiveObject* obj = i->second;
1843 // Do not deactivate if static data creation not allowed
1844 if(!force_delete && !obj->isStaticAllowed())
1847 // If pending deactivation, let removeRemovedObjects() do it
1848 if(!force_delete && obj->m_pending_deactivation)
1852 v3f objectpos = obj->getBasePosition();
1854 // The block in which the object resides in
1855 v3s16 blockpos_o = getNodeBlockPos(floatToInt(objectpos, BS));
1857 // If object's static data is stored in a deactivated block and object
1858 // is actually located in an active block, re-save to the block in
1859 // which the object is actually located in.
1861 obj->m_static_exists &&
1862 !m_active_blocks.contains(obj->m_static_block) &&
1863 m_active_blocks.contains(blockpos_o))
1865 v3s16 old_static_block = obj->m_static_block;
1867 // Save to block where object is located
1868 MapBlock *block = m_map->emergeBlock(blockpos_o, false);
1870 errorstream<<"ServerEnvironment::deactivateFarObjects(): "
1871 <<"Could not save object id="<<id
1872 <<" to it's current block "<<PP(blockpos_o)
1876 std::string staticdata_new = obj->getStaticData();
1877 StaticObject s_obj(obj->getType(), objectpos, staticdata_new);
1878 block->m_static_objects.insert(id, s_obj);
1879 obj->m_static_block = blockpos_o;
1880 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1881 "deactivateFarObjects: Static data moved in");
1883 // Delete from block where object was located
1884 block = m_map->emergeBlock(old_static_block, false);
1886 errorstream<<"ServerEnvironment::deactivateFarObjects(): "
1887 <<"Could not delete object id="<<id
1888 <<" from it's previous block "<<PP(old_static_block)
1892 block->m_static_objects.remove(id);
1893 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1894 "deactivateFarObjects: Static data moved out");
1898 // If block is active, don't remove
1899 if(!force_delete && m_active_blocks.contains(blockpos_o))
1902 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
1903 <<"deactivating object id="<<id<<" on inactive block "
1904 <<PP(blockpos_o)<<std::endl;
1906 // If known by some client, don't immediately delete.
1907 bool pending_delete = (obj->m_known_by_count > 0 && !force_delete);
1910 Update the static data
1913 if(obj->isStaticAllowed())
1915 // Create new static object
1916 std::string staticdata_new = obj->getStaticData();
1917 StaticObject s_obj(obj->getType(), objectpos, staticdata_new);
1919 bool stays_in_same_block = false;
1920 bool data_changed = true;
1922 if(obj->m_static_exists){
1923 if(obj->m_static_block == blockpos_o)
1924 stays_in_same_block = true;
1926 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
1928 std::map<u16, StaticObject>::iterator n =
1929 block->m_static_objects.m_active.find(id);
1930 if(n != block->m_static_objects.m_active.end()){
1931 StaticObject static_old = n->second;
1933 float save_movem = obj->getMinimumSavedMovement();
1935 if(static_old.data == staticdata_new &&
1936 (static_old.pos - objectpos).getLength() < save_movem)
1937 data_changed = false;
1939 errorstream<<"ServerEnvironment::deactivateFarObjects(): "
1940 <<"id="<<id<<" m_static_exists=true but "
1941 <<"static data doesn't actually exist in "
1942 <<PP(obj->m_static_block)<<std::endl;
1946 bool shall_be_written = (!stays_in_same_block || data_changed);
1948 // Delete old static object
1949 if(obj->m_static_exists)
1951 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
1954 block->m_static_objects.remove(id);
1955 obj->m_static_exists = false;
1956 // Only mark block as modified if data changed considerably
1957 if(shall_be_written)
1958 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1959 "deactivateFarObjects: Static data "
1960 "changed considerably");
1964 // Add to the block where the object is located in
1965 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1966 // Get or generate the block
1967 MapBlock *block = NULL;
1969 block = m_map->emergeBlock(blockpos);
1970 } catch(InvalidPositionException &e){
1971 // Handled via NULL pointer
1972 // NOTE: emergeBlock's failure is usually determined by it
1973 // actually returning NULL
1978 if(block->m_static_objects.m_stored.size() >= g_settings->getU16("max_objects_per_block")){
1979 errorstream<<"ServerEnv: Trying to store id="<<obj->getId()
1980 <<" statically but block "<<PP(blockpos)
1981 <<" already contains "
1982 <<block->m_static_objects.m_stored.size()
1984 <<" Forcing delete."<<std::endl;
1985 force_delete = true;
1987 // If static counterpart already exists in target block,
1989 // This shouldn't happen because the object is removed from
1990 // the previous block before this according to
1991 // obj->m_static_block, but happens rarely for some unknown
1992 // reason. Unsuccessful attempts have been made to find
1994 if(id && block->m_static_objects.m_active.find(id) != block->m_static_objects.m_active.end()){
1995 infostream<<"ServerEnv: WARNING: Performing hack #83274"
1997 block->m_static_objects.remove(id);
1999 // Store static data
2000 u16 store_id = pending_delete ? id : 0;
2001 block->m_static_objects.insert(store_id, s_obj);
2003 // Only mark block as modified if data changed considerably
2004 if(shall_be_written)
2005 block->raiseModified(MOD_STATE_WRITE_NEEDED,
2006 "deactivateFarObjects: Static data "
2007 "changed considerably");
2009 obj->m_static_exists = true;
2010 obj->m_static_block = block->getPos();
2015 v3s16 p = floatToInt(objectpos, BS);
2016 errorstream<<"ServerEnv: Could not find or generate "
2017 <<"a block for storing id="<<obj->getId()
2018 <<" statically (pos="<<PP(p)<<")"<<std::endl;
2025 If known by some client, set pending deactivation.
2026 Otherwise delete it immediately.
2029 if(pending_delete && !force_delete)
2031 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
2032 <<"object id="<<id<<" is known by clients"
2033 <<"; not deleting yet"<<std::endl;
2035 obj->m_pending_deactivation = true;
2039 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
2040 <<"object id="<<id<<" is not known by clients"
2041 <<"; deleting"<<std::endl;
2043 // Tell the object about removal
2044 obj->removingFromEnvironment();
2045 // Deregister in scripting api
2046 m_script->removeObjectReference(obj);
2048 // Delete active object
2049 if(obj->environmentDeletes())
2051 // Id to be removed from m_active_objects
2052 objects_to_remove.push_back(id);
2055 // Remove references from m_active_objects
2056 for(std::list<u16>::iterator i = objects_to_remove.begin();
2057 i != objects_to_remove.end(); ++i)
2059 m_active_objects.erase(*i);
2066 #include "clientsimpleobject.h"
2072 ClientEnvironment::ClientEnvironment(ClientMap *map, scene::ISceneManager *smgr,
2073 ITextureSource *texturesource, IGameDef *gamedef,
2074 IrrlichtDevice *irr):
2077 m_texturesource(texturesource),
2082 memset(m_attachements, zero, sizeof(m_attachements));
2085 ClientEnvironment::~ClientEnvironment()
2087 // delete active objects
2088 for(std::map<u16, ClientActiveObject*>::iterator
2089 i = m_active_objects.begin();
2090 i != m_active_objects.end(); ++i)
2095 for(std::list<ClientSimpleObject*>::iterator
2096 i = m_simple_objects.begin(); i != m_simple_objects.end(); ++i)
2105 Map & ClientEnvironment::getMap()
2110 ClientMap & ClientEnvironment::getClientMap()
2115 void ClientEnvironment::addPlayer(Player *player)
2117 DSTACK(__FUNCTION_NAME);
2119 It is a failure if player is local and there already is a local
2122 assert(!(player->isLocal() == true && getLocalPlayer() != NULL));
2124 Environment::addPlayer(player);
2127 LocalPlayer * ClientEnvironment::getLocalPlayer()
2129 for(std::list<Player*>::iterator i = m_players.begin();
2130 i != m_players.end(); ++i)
2132 Player *player = *i;
2133 if(player->isLocal())
2134 return (LocalPlayer*)player;
2139 void ClientEnvironment::step(float dtime)
2141 DSTACK(__FUNCTION_NAME);
2143 /* Step time of day */
2144 stepTimeOfDay(dtime);
2146 // Get some settings
2147 bool fly_allowed = m_gamedef->checkLocalPrivilege("fly");
2148 bool free_move = fly_allowed && g_settings->getBool("free_move");
2151 LocalPlayer *lplayer = getLocalPlayer();
2153 // collision info queue
2154 std::list<CollisionInfo> player_collisions;
2157 Get the speed the player is going
2159 bool is_climbing = lplayer->is_climbing;
2161 f32 player_speed = lplayer->getSpeed().getLength();
2164 Maximum position increment
2166 //f32 position_max_increment = 0.05*BS;
2167 f32 position_max_increment = 0.1*BS;
2169 // Maximum time increment (for collision detection etc)
2170 // time = distance / speed
2171 f32 dtime_max_increment = 1;
2172 if(player_speed > 0.001)
2173 dtime_max_increment = position_max_increment / player_speed;
2175 // Maximum time increment is 10ms or lower
2176 if(dtime_max_increment > 0.01)
2177 dtime_max_increment = 0.01;
2179 // Don't allow overly huge dtime
2183 f32 dtime_downcount = dtime;
2186 Stuff that has a maximum time increment
2195 if(dtime_downcount > dtime_max_increment)
2197 dtime_part = dtime_max_increment;
2198 dtime_downcount -= dtime_part;
2202 dtime_part = dtime_downcount;
2204 Setting this to 0 (no -=dtime_part) disables an infinite loop
2205 when dtime_part is so small that dtime_downcount -= dtime_part
2208 dtime_downcount = 0;
2217 if(free_move == false && is_climbing == false)
2220 v3f speed = lplayer->getSpeed();
2221 if(lplayer->in_liquid == false)
2222 speed.Y -= lplayer->movement_gravity * lplayer->physics_override_gravity * dtime_part * 2;
2224 // Liquid floating / sinking
2225 if(lplayer->in_liquid && !lplayer->swimming_vertical)
2226 speed.Y -= lplayer->movement_liquid_sink * dtime_part * 2;
2228 // Liquid resistance
2229 if(lplayer->in_liquid_stable || lplayer->in_liquid)
2231 // How much the node's viscosity blocks movement, ranges between 0 and 1
2232 // Should match the scale at which viscosity increase affects other liquid attributes
2233 const f32 viscosity_factor = 0.3;
2235 v3f d_wanted = -speed / lplayer->movement_liquid_fluidity;
2236 f32 dl = d_wanted.getLength();
2237 if(dl > lplayer->movement_liquid_fluidity_smooth)
2238 dl = lplayer->movement_liquid_fluidity_smooth;
2239 dl *= (lplayer->liquid_viscosity * viscosity_factor) + (1 - viscosity_factor);
2241 v3f d = d_wanted.normalize() * dl;
2245 if(speed.X > lplayer->movement_liquid_fluidity + lplayer->movement_liquid_fluidity_smooth) speed.X -= lplayer->movement_liquid_fluidity_smooth;
2246 if(speed.X < -lplayer->movement_liquid_fluidity - lplayer->movement_liquid_fluidity_smooth) speed.X += lplayer->movement_liquid_fluidity_smooth;
2247 if(speed.Y > lplayer->movement_liquid_fluidity + lplayer->movement_liquid_fluidity_smooth) speed.Y -= lplayer->movement_liquid_fluidity_smooth;
2248 if(speed.Y < -lplayer->movement_liquid_fluidity - lplayer->movement_liquid_fluidity_smooth) speed.Y += lplayer->movement_liquid_fluidity_smooth;
2249 if(speed.Z > lplayer->movement_liquid_fluidity + lplayer->movement_liquid_fluidity_smooth) speed.Z -= lplayer->movement_liquid_fluidity_smooth;
2250 if(speed.Z < -lplayer->movement_liquid_fluidity - lplayer->movement_liquid_fluidity_smooth) speed.Z += lplayer->movement_liquid_fluidity_smooth;
2254 lplayer->setSpeed(speed);
2259 This also does collision detection.
2261 lplayer->move(dtime_part, this, position_max_increment,
2262 &player_collisions);
2265 while(dtime_downcount > 0.001);
2267 //std::cout<<"Looped "<<loopcount<<" times."<<std::endl;
2269 for(std::list<CollisionInfo>::iterator
2270 i = player_collisions.begin();
2271 i != player_collisions.end(); ++i)
2273 CollisionInfo &info = *i;
2274 v3f speed_diff = info.new_speed - info.old_speed;;
2275 // Handle only fall damage
2276 // (because otherwise walking against something in fast_move kills you)
2277 if(speed_diff.Y < 0 || info.old_speed.Y >= 0)
2279 // Get rid of other components
2282 f32 pre_factor = 1; // 1 hp per node/s
2283 f32 tolerance = BS*14; // 5 without damage
2284 f32 post_factor = 1; // 1 hp per node/s
2285 if(info.type == COLLISION_NODE)
2287 const ContentFeatures &f = m_gamedef->ndef()->
2288 get(m_map->getNodeNoEx(info.node_p));
2289 // Determine fall damage multiplier
2290 int addp = itemgroup_get(f.groups, "fall_damage_add_percent");
2291 pre_factor = 1.0 + (float)addp/100.0;
2293 float speed = pre_factor * speed_diff.getLength();
2294 if(speed > tolerance)
2296 f32 damage_f = (speed - tolerance)/BS * post_factor;
2297 u16 damage = (u16)(damage_f+0.5);
2299 damageLocalPlayer(damage, true);
2300 MtEvent *e = new SimpleTriggerEvent("PlayerFallingDamage");
2301 m_gamedef->event()->put(e);
2307 A quick draft of lava damage
2309 if(m_lava_hurt_interval.step(dtime, 1.0))
2311 v3f pf = lplayer->getPosition();
2313 // Feet, middle and head
2314 v3s16 p1 = floatToInt(pf + v3f(0, BS*0.1, 0), BS);
2315 MapNode n1 = m_map->getNodeNoEx(p1);
2316 v3s16 p2 = floatToInt(pf + v3f(0, BS*0.8, 0), BS);
2317 MapNode n2 = m_map->getNodeNoEx(p2);
2318 v3s16 p3 = floatToInt(pf + v3f(0, BS*1.6, 0), BS);
2319 MapNode n3 = m_map->getNodeNoEx(p3);
2321 u32 damage_per_second = 0;
2322 damage_per_second = MYMAX(damage_per_second,
2323 m_gamedef->ndef()->get(n1).damage_per_second);
2324 damage_per_second = MYMAX(damage_per_second,
2325 m_gamedef->ndef()->get(n2).damage_per_second);
2326 damage_per_second = MYMAX(damage_per_second,
2327 m_gamedef->ndef()->get(n3).damage_per_second);
2329 if(damage_per_second != 0)
2331 damageLocalPlayer(damage_per_second, true);
2338 if(m_drowning_interval.step(dtime, 2.0))
2340 v3f pf = lplayer->getPosition();
2343 v3s16 p = floatToInt(pf + v3f(0, BS*1.6, 0), BS);
2344 MapNode n = m_map->getNodeNoEx(p);
2345 ContentFeatures c = m_gamedef->ndef()->get(n);
2346 u8 drowning_damage = c.drowning;
2347 if(drowning_damage > 0 && lplayer->hp > 0){
2348 u16 breath = lplayer->getBreath();
2355 lplayer->setBreath(breath);
2356 updateLocalPlayerBreath(breath);
2359 if(lplayer->getBreath() == 0 && drowning_damage > 0){
2360 damageLocalPlayer(drowning_damage, true);
2363 if(m_breathing_interval.step(dtime, 0.5))
2365 v3f pf = lplayer->getPosition();
2368 v3s16 p = floatToInt(pf + v3f(0, BS*1.6, 0), BS);
2369 MapNode n = m_map->getNodeNoEx(p);
2370 ContentFeatures c = m_gamedef->ndef()->get(n);
2372 lplayer->setBreath(11);
2374 else if(c.drowning == 0){
2375 u16 breath = lplayer->getBreath();
2378 lplayer->setBreath(breath);
2379 updateLocalPlayerBreath(breath);
2385 Stuff that can be done in an arbitarily large dtime
2387 for(std::list<Player*>::iterator i = m_players.begin();
2388 i != m_players.end(); ++i)
2390 Player *player = *i;
2393 Handle non-local players
2395 if(player->isLocal() == false)
2398 player->move(dtime, this, 100*BS);
2402 // Update lighting on all players on client
2406 v3s16 p = player->getLightPosition();
2407 MapNode n = m_map->getNode(p);
2408 light = n.getLightBlendF1((float)getDayNightRatio()/1000, m_gamedef->ndef());
2410 catch(InvalidPositionException &e){
2411 light = blend_light_f1((float)getDayNightRatio()/1000, LIGHT_SUN, 0);
2413 player->light = light;
2417 Step active objects and update lighting of them
2420 g_profiler->avg("CEnv: num of objects", m_active_objects.size());
2421 bool update_lighting = m_active_object_light_update_interval.step(dtime, 0.21);
2422 for(std::map<u16, ClientActiveObject*>::iterator
2423 i = m_active_objects.begin();
2424 i != m_active_objects.end(); ++i)
2426 ClientActiveObject* obj = i->second;
2428 obj->step(dtime, this);
2436 v3s16 p = obj->getLightPosition();
2437 MapNode n = m_map->getNode(p);
2438 light = n.getLightBlend(getDayNightRatio(), m_gamedef->ndef());
2440 catch(InvalidPositionException &e){
2441 light = blend_light(getDayNightRatio(), LIGHT_SUN, 0);
2443 obj->updateLight(light);
2448 Step and handle simple objects
2450 g_profiler->avg("CEnv: num of simple objects", m_simple_objects.size());
2451 for(std::list<ClientSimpleObject*>::iterator
2452 i = m_simple_objects.begin(); i != m_simple_objects.end();)
2454 ClientSimpleObject *simple = *i;
2455 std::list<ClientSimpleObject*>::iterator cur = i;
2457 simple->step(dtime);
2458 if(simple->m_to_be_removed){
2460 m_simple_objects.erase(cur);
2465 void ClientEnvironment::addSimpleObject(ClientSimpleObject *simple)
2467 m_simple_objects.push_back(simple);
2470 ClientActiveObject* ClientEnvironment::getActiveObject(u16 id)
2472 std::map<u16, ClientActiveObject*>::iterator n;
2473 n = m_active_objects.find(id);
2474 if(n == m_active_objects.end())
2479 bool isFreeClientActiveObjectId(u16 id,
2480 std::map<u16, ClientActiveObject*> &objects)
2485 return objects.find(id) == objects.end();
2488 u16 getFreeClientActiveObjectId(
2489 std::map<u16, ClientActiveObject*> &objects)
2491 //try to reuse id's as late as possible
2492 static u16 last_used_id = 0;
2493 u16 startid = last_used_id;
2497 if(isFreeClientActiveObjectId(last_used_id, objects))
2498 return last_used_id;
2500 if(last_used_id == startid)
2505 u16 ClientEnvironment::addActiveObject(ClientActiveObject *object)
2508 if(object->getId() == 0)
2510 u16 new_id = getFreeClientActiveObjectId(m_active_objects);
2513 infostream<<"ClientEnvironment::addActiveObject(): "
2514 <<"no free ids available"<<std::endl;
2518 object->setId(new_id);
2520 if(isFreeClientActiveObjectId(object->getId(), m_active_objects) == false)
2522 infostream<<"ClientEnvironment::addActiveObject(): "
2523 <<"id is not free ("<<object->getId()<<")"<<std::endl;
2527 infostream<<"ClientEnvironment::addActiveObject(): "
2528 <<"added (id="<<object->getId()<<")"<<std::endl;
2529 m_active_objects[object->getId()] = object;
2530 object->addToScene(m_smgr, m_texturesource, m_irr);
2531 { // Update lighting immediately
2535 v3s16 p = object->getLightPosition();
2536 MapNode n = m_map->getNode(p);
2537 light = n.getLightBlend(getDayNightRatio(), m_gamedef->ndef());
2539 catch(InvalidPositionException &e){
2540 light = blend_light(getDayNightRatio(), LIGHT_SUN, 0);
2542 object->updateLight(light);
2544 return object->getId();
2547 void ClientEnvironment::addActiveObject(u16 id, u8 type,
2548 const std::string &init_data)
2550 ClientActiveObject* obj =
2551 ClientActiveObject::create(type, m_gamedef, this);
2554 infostream<<"ClientEnvironment::addActiveObject(): "
2555 <<"id="<<id<<" type="<<type<<": Couldn't create object"
2564 obj->initialize(init_data);
2566 catch(SerializationError &e)
2568 errorstream<<"ClientEnvironment::addActiveObject():"
2569 <<" id="<<id<<" type="<<type
2570 <<": SerializationError in initialize(): "
2572 <<": init_data="<<serializeJsonString(init_data)
2576 addActiveObject(obj);
2579 void ClientEnvironment::removeActiveObject(u16 id)
2581 verbosestream<<"ClientEnvironment::removeActiveObject(): "
2582 <<"id="<<id<<std::endl;
2583 ClientActiveObject* obj = getActiveObject(id);
2586 infostream<<"ClientEnvironment::removeActiveObject(): "
2587 <<"id="<<id<<" not found"<<std::endl;
2590 obj->removeFromScene(true);
2592 m_active_objects.erase(id);
2595 void ClientEnvironment::processActiveObjectMessage(u16 id,
2596 const std::string &data)
2598 ClientActiveObject* obj = getActiveObject(id);
2601 infostream<<"ClientEnvironment::processActiveObjectMessage():"
2602 <<" got message for id="<<id<<", which doesn't exist."
2608 obj->processMessage(data);
2610 catch(SerializationError &e)
2612 errorstream<<"ClientEnvironment::processActiveObjectMessage():"
2613 <<" id="<<id<<" type="<<obj->getType()
2614 <<" SerializationError in processMessage(),"
2615 <<" message="<<serializeJsonString(data)
2621 Callbacks for activeobjects
2624 void ClientEnvironment::damageLocalPlayer(u8 damage, bool handle_hp)
2626 LocalPlayer *lplayer = getLocalPlayer();
2630 if(lplayer->hp > damage)
2631 lplayer->hp -= damage;
2636 ClientEnvEvent event;
2637 event.type = CEE_PLAYER_DAMAGE;
2638 event.player_damage.amount = damage;
2639 event.player_damage.send_to_server = handle_hp;
2640 m_client_event_queue.push_back(event);
2643 void ClientEnvironment::updateLocalPlayerBreath(u16 breath)
2645 ClientEnvEvent event;
2646 event.type = CEE_PLAYER_BREATH;
2647 event.player_breath.amount = breath;
2648 m_client_event_queue.push_back(event);
2652 Client likes to call these
2655 void ClientEnvironment::getActiveObjects(v3f origin, f32 max_d,
2656 std::vector<DistanceSortedActiveObject> &dest)
2658 for(std::map<u16, ClientActiveObject*>::iterator
2659 i = m_active_objects.begin();
2660 i != m_active_objects.end(); ++i)
2662 ClientActiveObject* obj = i->second;
2664 f32 d = (obj->getPosition() - origin).getLength();
2669 DistanceSortedActiveObject dso(obj, d);
2671 dest.push_back(dso);
2675 ClientEnvEvent ClientEnvironment::getClientEvent()
2677 ClientEnvEvent event;
2678 if(m_client_event_queue.empty())
2679 event.type = CEE_NONE;
2681 event = m_client_event_queue.front();
2682 m_client_event_queue.pop_front();
2687 #endif // #ifndef SERVER