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),
2083 ClientEnvironment::~ClientEnvironment()
2085 // delete active objects
2086 for(std::map<u16, ClientActiveObject*>::iterator
2087 i = m_active_objects.begin();
2088 i != m_active_objects.end(); ++i)
2093 for(std::list<ClientSimpleObject*>::iterator
2094 i = m_simple_objects.begin(); i != m_simple_objects.end(); ++i)
2103 Map & ClientEnvironment::getMap()
2108 ClientMap & ClientEnvironment::getClientMap()
2113 void ClientEnvironment::addPlayer(Player *player)
2115 DSTACK(__FUNCTION_NAME);
2117 It is a failure if player is local and there already is a local
2120 assert(!(player->isLocal() == true && getLocalPlayer() != NULL));
2122 Environment::addPlayer(player);
2125 LocalPlayer * ClientEnvironment::getLocalPlayer()
2127 for(std::list<Player*>::iterator i = m_players.begin();
2128 i != m_players.end(); ++i)
2130 Player *player = *i;
2131 if(player->isLocal())
2132 return (LocalPlayer*)player;
2137 void ClientEnvironment::step(float dtime)
2139 DSTACK(__FUNCTION_NAME);
2141 /* Step time of day */
2142 stepTimeOfDay(dtime);
2144 // Get some settings
2145 bool fly_allowed = m_gamedef->checkLocalPrivilege("fly");
2146 bool free_move = fly_allowed && g_settings->getBool("free_move");
2149 LocalPlayer *lplayer = getLocalPlayer();
2151 // collision info queue
2152 std::list<CollisionInfo> player_collisions;
2155 Get the speed the player is going
2157 bool is_climbing = lplayer->is_climbing;
2159 f32 player_speed = lplayer->getSpeed().getLength();
2162 Maximum position increment
2164 //f32 position_max_increment = 0.05*BS;
2165 f32 position_max_increment = 0.1*BS;
2167 // Maximum time increment (for collision detection etc)
2168 // time = distance / speed
2169 f32 dtime_max_increment = 1;
2170 if(player_speed > 0.001)
2171 dtime_max_increment = position_max_increment / player_speed;
2173 // Maximum time increment is 10ms or lower
2174 if(dtime_max_increment > 0.01)
2175 dtime_max_increment = 0.01;
2177 // Don't allow overly huge dtime
2181 f32 dtime_downcount = dtime;
2184 Stuff that has a maximum time increment
2193 if(dtime_downcount > dtime_max_increment)
2195 dtime_part = dtime_max_increment;
2196 dtime_downcount -= dtime_part;
2200 dtime_part = dtime_downcount;
2202 Setting this to 0 (no -=dtime_part) disables an infinite loop
2203 when dtime_part is so small that dtime_downcount -= dtime_part
2206 dtime_downcount = 0;
2215 if(free_move == false && is_climbing == false)
2218 v3f speed = lplayer->getSpeed();
2219 if(lplayer->in_liquid == false)
2220 speed.Y -= lplayer->movement_gravity * lplayer->physics_override_gravity * dtime_part * 2;
2222 // Liquid floating / sinking
2223 if(lplayer->in_liquid && !lplayer->swimming_vertical)
2224 speed.Y -= lplayer->movement_liquid_sink * dtime_part * 2;
2226 // Liquid resistance
2227 if(lplayer->in_liquid_stable || lplayer->in_liquid)
2229 // How much the node's viscosity blocks movement, ranges between 0 and 1
2230 // Should match the scale at which viscosity increase affects other liquid attributes
2231 const f32 viscosity_factor = 0.3;
2233 v3f d_wanted = -speed / lplayer->movement_liquid_fluidity;
2234 f32 dl = d_wanted.getLength();
2235 if(dl > lplayer->movement_liquid_fluidity_smooth)
2236 dl = lplayer->movement_liquid_fluidity_smooth;
2237 dl *= (lplayer->liquid_viscosity * viscosity_factor) + (1 - viscosity_factor);
2239 v3f d = d_wanted.normalize() * dl;
2243 if(speed.X > lplayer->movement_liquid_fluidity + lplayer->movement_liquid_fluidity_smooth) speed.X -= lplayer->movement_liquid_fluidity_smooth;
2244 if(speed.X < -lplayer->movement_liquid_fluidity - lplayer->movement_liquid_fluidity_smooth) speed.X += lplayer->movement_liquid_fluidity_smooth;
2245 if(speed.Y > lplayer->movement_liquid_fluidity + lplayer->movement_liquid_fluidity_smooth) speed.Y -= lplayer->movement_liquid_fluidity_smooth;
2246 if(speed.Y < -lplayer->movement_liquid_fluidity - lplayer->movement_liquid_fluidity_smooth) speed.Y += lplayer->movement_liquid_fluidity_smooth;
2247 if(speed.Z > lplayer->movement_liquid_fluidity + lplayer->movement_liquid_fluidity_smooth) speed.Z -= lplayer->movement_liquid_fluidity_smooth;
2248 if(speed.Z < -lplayer->movement_liquid_fluidity - lplayer->movement_liquid_fluidity_smooth) speed.Z += lplayer->movement_liquid_fluidity_smooth;
2252 lplayer->setSpeed(speed);
2257 This also does collision detection.
2259 lplayer->move(dtime_part, this, position_max_increment,
2260 &player_collisions);
2263 while(dtime_downcount > 0.001);
2265 //std::cout<<"Looped "<<loopcount<<" times."<<std::endl;
2267 for(std::list<CollisionInfo>::iterator
2268 i = player_collisions.begin();
2269 i != player_collisions.end(); ++i)
2271 CollisionInfo &info = *i;
2272 v3f speed_diff = info.new_speed - info.old_speed;;
2273 // Handle only fall damage
2274 // (because otherwise walking against something in fast_move kills you)
2275 if(speed_diff.Y < 0 || info.old_speed.Y >= 0)
2277 // Get rid of other components
2280 f32 pre_factor = 1; // 1 hp per node/s
2281 f32 tolerance = BS*14; // 5 without damage
2282 f32 post_factor = 1; // 1 hp per node/s
2283 if(info.type == COLLISION_NODE)
2285 const ContentFeatures &f = m_gamedef->ndef()->
2286 get(m_map->getNodeNoEx(info.node_p));
2287 // Determine fall damage multiplier
2288 int addp = itemgroup_get(f.groups, "fall_damage_add_percent");
2289 pre_factor = 1.0 + (float)addp/100.0;
2291 float speed = pre_factor * speed_diff.getLength();
2292 if(speed > tolerance)
2294 f32 damage_f = (speed - tolerance)/BS * post_factor;
2295 u16 damage = (u16)(damage_f+0.5);
2297 damageLocalPlayer(damage, true);
2298 MtEvent *e = new SimpleTriggerEvent("PlayerFallingDamage");
2299 m_gamedef->event()->put(e);
2305 A quick draft of lava damage
2307 if(m_lava_hurt_interval.step(dtime, 1.0))
2309 v3f pf = lplayer->getPosition();
2311 // Feet, middle and head
2312 v3s16 p1 = floatToInt(pf + v3f(0, BS*0.1, 0), BS);
2313 MapNode n1 = m_map->getNodeNoEx(p1);
2314 v3s16 p2 = floatToInt(pf + v3f(0, BS*0.8, 0), BS);
2315 MapNode n2 = m_map->getNodeNoEx(p2);
2316 v3s16 p3 = floatToInt(pf + v3f(0, BS*1.6, 0), BS);
2317 MapNode n3 = m_map->getNodeNoEx(p3);
2319 u32 damage_per_second = 0;
2320 damage_per_second = MYMAX(damage_per_second,
2321 m_gamedef->ndef()->get(n1).damage_per_second);
2322 damage_per_second = MYMAX(damage_per_second,
2323 m_gamedef->ndef()->get(n2).damage_per_second);
2324 damage_per_second = MYMAX(damage_per_second,
2325 m_gamedef->ndef()->get(n3).damage_per_second);
2327 if(damage_per_second != 0)
2329 damageLocalPlayer(damage_per_second, true);
2336 if(m_drowning_interval.step(dtime, 2.0))
2338 v3f pf = lplayer->getPosition();
2341 v3s16 p = floatToInt(pf + v3f(0, BS*1.6, 0), BS);
2342 MapNode n = m_map->getNodeNoEx(p);
2343 ContentFeatures c = m_gamedef->ndef()->get(n);
2344 u8 drowning_damage = c.drowning;
2345 if(drowning_damage > 0 && lplayer->hp > 0){
2346 u16 breath = lplayer->getBreath();
2353 lplayer->setBreath(breath);
2354 updateLocalPlayerBreath(breath);
2357 if(lplayer->getBreath() == 0 && drowning_damage > 0){
2358 damageLocalPlayer(drowning_damage, true);
2361 if(m_breathing_interval.step(dtime, 0.5))
2363 v3f pf = lplayer->getPosition();
2366 v3s16 p = floatToInt(pf + v3f(0, BS*1.6, 0), BS);
2367 MapNode n = m_map->getNodeNoEx(p);
2368 ContentFeatures c = m_gamedef->ndef()->get(n);
2370 lplayer->setBreath(11);
2372 else if(c.drowning == 0){
2373 u16 breath = lplayer->getBreath();
2376 lplayer->setBreath(breath);
2377 updateLocalPlayerBreath(breath);
2383 Stuff that can be done in an arbitarily large dtime
2385 for(std::list<Player*>::iterator i = m_players.begin();
2386 i != m_players.end(); ++i)
2388 Player *player = *i;
2391 Handle non-local players
2393 if(player->isLocal() == false)
2396 player->move(dtime, this, 100*BS);
2400 // Update lighting on all players on client
2404 v3s16 p = player->getLightPosition();
2405 MapNode n = m_map->getNode(p);
2406 light = n.getLightBlendF1((float)getDayNightRatio()/1000, m_gamedef->ndef());
2408 catch(InvalidPositionException &e){
2409 light = blend_light_f1((float)getDayNightRatio()/1000, LIGHT_SUN, 0);
2411 player->light = light;
2415 Step active objects and update lighting of them
2418 g_profiler->avg("CEnv: num of objects", m_active_objects.size());
2419 bool update_lighting = m_active_object_light_update_interval.step(dtime, 0.21);
2420 for(std::map<u16, ClientActiveObject*>::iterator
2421 i = m_active_objects.begin();
2422 i != m_active_objects.end(); ++i)
2424 ClientActiveObject* obj = i->second;
2426 obj->step(dtime, this);
2434 v3s16 p = obj->getLightPosition();
2435 MapNode n = m_map->getNode(p);
2436 light = n.getLightBlend(getDayNightRatio(), m_gamedef->ndef());
2438 catch(InvalidPositionException &e){
2439 light = blend_light(getDayNightRatio(), LIGHT_SUN, 0);
2441 obj->updateLight(light);
2446 Step and handle simple objects
2448 g_profiler->avg("CEnv: num of simple objects", m_simple_objects.size());
2449 for(std::list<ClientSimpleObject*>::iterator
2450 i = m_simple_objects.begin(); i != m_simple_objects.end();)
2452 ClientSimpleObject *simple = *i;
2453 std::list<ClientSimpleObject*>::iterator cur = i;
2455 simple->step(dtime);
2456 if(simple->m_to_be_removed){
2458 m_simple_objects.erase(cur);
2463 void ClientEnvironment::addSimpleObject(ClientSimpleObject *simple)
2465 m_simple_objects.push_back(simple);
2468 ClientActiveObject* ClientEnvironment::getActiveObject(u16 id)
2470 std::map<u16, ClientActiveObject*>::iterator n;
2471 n = m_active_objects.find(id);
2472 if(n == m_active_objects.end())
2477 bool isFreeClientActiveObjectId(u16 id,
2478 std::map<u16, ClientActiveObject*> &objects)
2483 return objects.find(id) == objects.end();
2486 u16 getFreeClientActiveObjectId(
2487 std::map<u16, ClientActiveObject*> &objects)
2489 //try to reuse id's as late as possible
2490 static u16 last_used_id = 0;
2491 u16 startid = last_used_id;
2495 if(isFreeClientActiveObjectId(last_used_id, objects))
2496 return last_used_id;
2498 if(last_used_id == startid)
2503 u16 ClientEnvironment::addActiveObject(ClientActiveObject *object)
2506 if(object->getId() == 0)
2508 u16 new_id = getFreeClientActiveObjectId(m_active_objects);
2511 infostream<<"ClientEnvironment::addActiveObject(): "
2512 <<"no free ids available"<<std::endl;
2516 object->setId(new_id);
2518 if(isFreeClientActiveObjectId(object->getId(), m_active_objects) == false)
2520 infostream<<"ClientEnvironment::addActiveObject(): "
2521 <<"id is not free ("<<object->getId()<<")"<<std::endl;
2525 infostream<<"ClientEnvironment::addActiveObject(): "
2526 <<"added (id="<<object->getId()<<")"<<std::endl;
2527 m_active_objects[object->getId()] = object;
2528 object->addToScene(m_smgr, m_texturesource, m_irr);
2529 { // Update lighting immediately
2533 v3s16 p = object->getLightPosition();
2534 MapNode n = m_map->getNode(p);
2535 light = n.getLightBlend(getDayNightRatio(), m_gamedef->ndef());
2537 catch(InvalidPositionException &e){
2538 light = blend_light(getDayNightRatio(), LIGHT_SUN, 0);
2540 object->updateLight(light);
2542 return object->getId();
2545 void ClientEnvironment::addActiveObject(u16 id, u8 type,
2546 const std::string &init_data)
2548 ClientActiveObject* obj =
2549 ClientActiveObject::create(type, m_gamedef, this);
2552 infostream<<"ClientEnvironment::addActiveObject(): "
2553 <<"id="<<id<<" type="<<type<<": Couldn't create object"
2562 obj->initialize(init_data);
2564 catch(SerializationError &e)
2566 errorstream<<"ClientEnvironment::addActiveObject():"
2567 <<" id="<<id<<" type="<<type
2568 <<": SerializationError in initialize(): "
2570 <<": init_data="<<serializeJsonString(init_data)
2574 addActiveObject(obj);
2577 void ClientEnvironment::removeActiveObject(u16 id)
2579 verbosestream<<"ClientEnvironment::removeActiveObject(): "
2580 <<"id="<<id<<std::endl;
2581 ClientActiveObject* obj = getActiveObject(id);
2584 infostream<<"ClientEnvironment::removeActiveObject(): "
2585 <<"id="<<id<<" not found"<<std::endl;
2588 obj->removeFromScene(true);
2590 m_active_objects.erase(id);
2593 void ClientEnvironment::processActiveObjectMessage(u16 id,
2594 const std::string &data)
2596 ClientActiveObject* obj = getActiveObject(id);
2599 infostream<<"ClientEnvironment::processActiveObjectMessage():"
2600 <<" got message for id="<<id<<", which doesn't exist."
2606 obj->processMessage(data);
2608 catch(SerializationError &e)
2610 errorstream<<"ClientEnvironment::processActiveObjectMessage():"
2611 <<" id="<<id<<" type="<<obj->getType()
2612 <<" SerializationError in processMessage(),"
2613 <<" message="<<serializeJsonString(data)
2619 Callbacks for activeobjects
2622 void ClientEnvironment::damageLocalPlayer(u8 damage, bool handle_hp)
2624 LocalPlayer *lplayer = getLocalPlayer();
2628 if(lplayer->hp > damage)
2629 lplayer->hp -= damage;
2634 ClientEnvEvent event;
2635 event.type = CEE_PLAYER_DAMAGE;
2636 event.player_damage.amount = damage;
2637 event.player_damage.send_to_server = handle_hp;
2638 m_client_event_queue.push_back(event);
2641 void ClientEnvironment::updateLocalPlayerBreath(u16 breath)
2643 ClientEnvEvent event;
2644 event.type = CEE_PLAYER_BREATH;
2645 event.player_breath.amount = breath;
2646 m_client_event_queue.push_back(event);
2650 Client likes to call these
2653 void ClientEnvironment::getActiveObjects(v3f origin, f32 max_d,
2654 std::vector<DistanceSortedActiveObject> &dest)
2656 for(std::map<u16, ClientActiveObject*>::iterator
2657 i = m_active_objects.begin();
2658 i != m_active_objects.end(); ++i)
2660 ClientActiveObject* obj = i->second;
2662 f32 d = (obj->getPosition() - origin).getLength();
2667 DistanceSortedActiveObject dso(obj, d);
2669 dest.push_back(dso);
2673 ClientEnvEvent ClientEnvironment::getClientEvent()
2675 ClientEnvEvent event;
2676 if(m_client_event_queue.empty())
2677 event.type = CEE_NONE;
2679 event = m_client_event_queue.front();
2680 m_client_event_queue.pop_front();
2685 #endif // #ifndef SERVER