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)
324 m_use_weather = g_settings->getBool("weather");
327 ServerEnvironment::~ServerEnvironment()
329 // Clear active block list.
330 // This makes the next one delete all active objects.
331 m_active_blocks.clear();
333 // Convert all objects to static and delete the active objects
334 deactivateFarObjects(true);
339 // Delete ActiveBlockModifiers
340 for(std::list<ABMWithState>::iterator
341 i = m_abms.begin(); i != m_abms.end(); ++i){
346 Map & ServerEnvironment::getMap()
351 ServerMap & ServerEnvironment::getServerMap()
356 bool ServerEnvironment::line_of_sight(v3f pos1, v3f pos2, float stepsize, v3s16 *p)
358 float distance = pos1.getDistanceFrom(pos2);
360 //calculate normalized direction vector
361 v3f normalized_vector = v3f((pos2.X - pos1.X)/distance,
362 (pos2.Y - pos1.Y)/distance,
363 (pos2.Z - pos1.Z)/distance);
365 //find out if there's a node on path between pos1 and pos2
366 for (float i = 1; i < distance; i += stepsize) {
367 v3s16 pos = floatToInt(v3f(normalized_vector.X * i,
368 normalized_vector.Y * i,
369 normalized_vector.Z * i) +pos1,BS);
371 MapNode n = getMap().getNodeNoEx(pos);
373 if(n.param0 != CONTENT_AIR) {
383 void ServerEnvironment::serializePlayers(const std::string &savedir)
385 std::string players_path = savedir + "/players";
386 fs::CreateDir(players_path);
388 std::set<Player*> saved_players;
390 std::vector<fs::DirListNode> player_files = fs::GetDirListing(players_path);
391 for(u32 i=0; i<player_files.size(); i++)
393 if(player_files[i].dir || player_files[i].name[0] == '.')
396 // Full path to this file
397 std::string path = players_path + "/" + player_files[i].name;
399 //infostream<<"Checking player file "<<path<<std::endl;
401 // Load player to see what is its name
402 RemotePlayer testplayer(m_gamedef);
404 // Open file and deserialize
405 std::ifstream is(path.c_str(), std::ios_base::binary);
406 if(is.good() == false)
408 infostream<<"Failed to read "<<path<<std::endl;
411 testplayer.deSerialize(is, player_files[i].name);
414 //infostream<<"Loaded test player with name "<<testplayer.getName()<<std::endl;
416 // Search for the player
417 std::string playername = testplayer.getName();
418 Player *player = getPlayer(playername.c_str());
421 infostream<<"Didn't find matching player, ignoring file "<<path<<std::endl;
425 //infostream<<"Found matching player, overwriting."<<std::endl;
427 // OK, found. Save player there.
428 if(player->checkModified())
430 // Open file and serialize
431 std::ostringstream ss(std::ios_base::binary);
432 player->serialize(ss);
433 if(!fs::safeWriteToFile(path, ss.str()))
435 infostream<<"Failed to write "<<path<<std::endl;
438 saved_players.insert(player);
440 saved_players.insert(player);
444 for(std::list<Player*>::iterator i = m_players.begin();
445 i != m_players.end(); ++i)
448 if(saved_players.find(player) != saved_players.end())
450 /*infostream<<"Player "<<player->getName()
451 <<" was already saved."<<std::endl;*/
454 std::string playername = player->getName();
455 // Don't save unnamed player
458 //infostream<<"Not saving unnamed player."<<std::endl;
464 if(string_allowed(playername, PLAYERNAME_ALLOWED_CHARS) == false)
465 playername = "player";
466 std::string path = players_path + "/" + playername;
468 for(u32 i=0; i<1000; i++)
470 if(fs::PathExists(path) == false)
475 path = players_path + "/" + playername + itos(i);
479 infostream<<"Didn't find free file for player"<<std::endl;
484 /*infostream<<"Saving player "<<player->getName()<<" to "
486 // Open file and serialize
487 std::ostringstream ss(std::ios_base::binary);
488 player->serialize(ss);
489 if(!fs::safeWriteToFile(path, ss.str()))
491 infostream<<"Failed to write "<<path<<std::endl;
494 saved_players.insert(player);
498 //infostream<<"Saved "<<saved_players.size()<<" players."<<std::endl;
501 void ServerEnvironment::deSerializePlayers(const std::string &savedir)
503 std::string players_path = savedir + "/players";
505 std::vector<fs::DirListNode> player_files = fs::GetDirListing(players_path);
506 for(u32 i=0; i<player_files.size(); i++)
508 if(player_files[i].dir)
511 // Full path to this file
512 std::string path = players_path + "/" + player_files[i].name;
514 //infostream<<"Checking player file "<<path<<std::endl;
516 // Load player to see what is its name
517 RemotePlayer testplayer(m_gamedef);
519 // Open file and deserialize
520 std::ifstream is(path.c_str(), std::ios_base::binary);
521 if(is.good() == false)
523 infostream<<"Failed to read "<<path<<std::endl;
526 testplayer.deSerialize(is, player_files[i].name);
529 if(!string_allowed(testplayer.getName(), PLAYERNAME_ALLOWED_CHARS))
531 infostream<<"Not loading player with invalid name: "
532 <<testplayer.getName()<<std::endl;
535 /*infostream<<"Loaded test player with name "<<testplayer.getName()
538 // Search for the player
539 std::string playername = testplayer.getName();
540 Player *player = getPlayer(playername.c_str());
541 bool newplayer = false;
544 //infostream<<"Is a new player"<<std::endl;
545 player = new RemotePlayer(m_gamedef);
551 verbosestream<<"Reading player "<<testplayer.getName()<<" from "
553 // Open file and deserialize
554 std::ifstream is(path.c_str(), std::ios_base::binary);
555 if(is.good() == false)
557 infostream<<"Failed to read "<<path<<std::endl;
560 player->deSerialize(is, player_files[i].name);
570 void ServerEnvironment::saveMeta(const std::string &savedir)
572 std::string path = savedir + "/env_meta.txt";
574 // Open file and serialize
575 std::ostringstream ss(std::ios_base::binary);
578 args.setU64("game_time", m_game_time);
579 args.setU64("time_of_day", getTimeOfDay());
583 if(!fs::safeWriteToFile(path, ss.str()))
585 infostream<<"ServerEnvironment::saveMeta(): Failed to write "
587 throw SerializationError("Couldn't save env meta");
591 void ServerEnvironment::loadMeta(const std::string &savedir)
593 std::string path = savedir + "/env_meta.txt";
595 // Open file and deserialize
596 std::ifstream is(path.c_str(), std::ios_base::binary);
597 if(is.good() == false)
599 infostream<<"ServerEnvironment::loadMeta(): Failed to open "
601 throw SerializationError("Couldn't load env meta");
609 throw SerializationError
610 ("ServerEnvironment::loadMeta(): EnvArgsEnd not found");
612 std::getline(is, line);
613 std::string trimmedline = trim(line);
614 if(trimmedline == "EnvArgsEnd")
616 args.parseConfigLine(line);
620 m_game_time = args.getU64("game_time");
621 }catch(SettingNotFoundException &e){
622 // Getting this is crucial, otherwise timestamps are useless
623 throw SerializationError("Couldn't load env meta game_time");
627 m_time_of_day = args.getU64("time_of_day");
628 }catch(SettingNotFoundException &e){
629 // This is not as important
630 m_time_of_day = 9000;
636 ActiveBlockModifier *abm;
638 std::set<content_t> required_neighbors;
644 ServerEnvironment *m_env;
645 std::map<content_t, std::list<ActiveABM> > m_aabms;
647 ABMHandler(std::list<ABMWithState> &abms,
648 float dtime_s, ServerEnvironment *env,
654 INodeDefManager *ndef = env->getGameDef()->ndef();
655 for(std::list<ABMWithState>::iterator
656 i = abms.begin(); i != abms.end(); ++i){
657 ActiveBlockModifier *abm = i->abm;
658 float trigger_interval = abm->getTriggerInterval();
659 if(trigger_interval < 0.001)
660 trigger_interval = 0.001;
661 float actual_interval = dtime_s;
664 if(i->timer < trigger_interval)
666 i->timer -= trigger_interval;
667 actual_interval = trigger_interval;
669 float intervals = actual_interval / trigger_interval;
672 float chance = abm->getTriggerChance();
677 aabm.chance = chance / intervals;
681 std::set<std::string> required_neighbors_s
682 = abm->getRequiredNeighbors();
683 for(std::set<std::string>::iterator
684 i = required_neighbors_s.begin();
685 i != required_neighbors_s.end(); i++)
687 ndef->getIds(*i, aabm.required_neighbors);
690 std::set<std::string> contents_s = abm->getTriggerContents();
691 for(std::set<std::string>::iterator
692 i = contents_s.begin(); i != contents_s.end(); i++)
694 std::set<content_t> ids;
695 ndef->getIds(*i, ids);
696 for(std::set<content_t>::const_iterator k = ids.begin();
700 std::map<content_t, std::list<ActiveABM> >::iterator j;
702 if(j == m_aabms.end()){
703 std::list<ActiveABM> aabmlist;
704 m_aabms[c] = aabmlist;
707 j->second.push_back(aabm);
712 // Find out how many objects the given block and its neighbours contain.
713 // Returns the number of objects in the block, and also in 'wider' the
714 // number of objects in the block and all its neighbours. The latter
715 // may an estimate if any neighbours are unloaded.
716 u32 countObjects(MapBlock *block, ServerMap * map, u32 &wider)
719 u32 wider_unknown_count = 0;
720 for(s16 x=-1; x<=1; x++)
721 for(s16 y=-1; y<=1; y++)
722 for(s16 z=-1; z<=1; z++)
724 MapBlock *block2 = map->getBlockNoCreateNoEx(
725 block->getPos() + v3s16(x,y,z));
727 wider_unknown_count++;
730 wider += block2->m_static_objects.m_active.size()
731 + block2->m_static_objects.m_stored.size();
734 u32 active_object_count = block->m_static_objects.m_active.size();
735 u32 wider_known_count = 3*3*3 - wider_unknown_count;
736 wider += wider_unknown_count * wider / wider_known_count;
737 return active_object_count;
740 void apply(MapBlock *block)
745 ServerMap *map = &m_env->getServerMap();
747 u32 active_object_count_wider;
748 u32 active_object_count = this->countObjects(block, map, active_object_count_wider);
749 m_env->m_added_objects = 0;
752 for(p0.X=0; p0.X<MAP_BLOCKSIZE; p0.X++)
753 for(p0.Y=0; p0.Y<MAP_BLOCKSIZE; p0.Y++)
754 for(p0.Z=0; p0.Z<MAP_BLOCKSIZE; p0.Z++)
756 MapNode n = block->getNodeNoEx(p0);
757 content_t c = n.getContent();
758 v3s16 p = p0 + block->getPosRelative();
760 std::map<content_t, std::list<ActiveABM> >::iterator j;
762 if(j == m_aabms.end())
765 for(std::list<ActiveABM>::iterator
766 i = j->second.begin(); i != j->second.end(); i++)
768 if(myrand() % i->chance != 0)
772 if(!i->required_neighbors.empty())
775 for(p1.X = p.X-1; p1.X <= p.X+1; p1.X++)
776 for(p1.Y = p.Y-1; p1.Y <= p.Y+1; p1.Y++)
777 for(p1.Z = p.Z-1; p1.Z <= p.Z+1; p1.Z++)
781 MapNode n = map->getNodeNoEx(p1);
782 content_t c = n.getContent();
783 std::set<content_t>::const_iterator k;
784 k = i->required_neighbors.find(c);
785 if(k != i->required_neighbors.end()){
789 // No required neighbor found
794 // Call all the trigger variations
795 i->abm->trigger(m_env, p, n);
796 i->abm->trigger(m_env, p, n,
797 active_object_count, active_object_count_wider);
799 // Count surrounding objects again if the abms added any
800 if(m_env->m_added_objects > 0) {
801 active_object_count = countObjects(block, map, active_object_count_wider);
802 m_env->m_added_objects = 0;
809 void ServerEnvironment::activateBlock(MapBlock *block, u32 additional_dtime)
811 // Reset usage timer immediately, otherwise a block that becomes active
812 // again at around the same time as it would normally be unloaded will
813 // get unloaded incorrectly. (I think this still leaves a small possibility
814 // of a race condition between this and server::AsyncRunStep, which only
815 // some kind of synchronisation will fix, but it at least reduces the window
816 // of opportunity for it to break from seconds to nanoseconds)
817 block->resetUsageTimer();
819 // Get time difference
821 u32 stamp = block->getTimestamp();
822 if(m_game_time > stamp && stamp != BLOCK_TIMESTAMP_UNDEFINED)
823 dtime_s = m_game_time - block->getTimestamp();
824 dtime_s += additional_dtime;
826 /*infostream<<"ServerEnvironment::activateBlock(): block timestamp: "
827 <<stamp<<", game time: "<<m_game_time<<std::endl;*/
829 // Set current time as timestamp
830 block->setTimestampNoChangedFlag(m_game_time);
832 /*infostream<<"ServerEnvironment::activateBlock(): block is "
833 <<dtime_s<<" seconds old."<<std::endl;*/
835 // Activate stored objects
836 activateObjects(block, dtime_s);
839 std::map<v3s16, NodeTimer> elapsed_timers =
840 block->m_node_timers.step((float)dtime_s);
841 if(!elapsed_timers.empty()){
843 for(std::map<v3s16, NodeTimer>::iterator
844 i = elapsed_timers.begin();
845 i != elapsed_timers.end(); i++){
846 n = block->getNodeNoEx(i->first);
847 v3s16 p = i->first + block->getPosRelative();
848 if(m_script->node_on_timer(p,n,i->second.elapsed))
849 block->setNodeTimer(i->first,NodeTimer(i->second.timeout,0));
853 /* Handle ActiveBlockModifiers */
854 ABMHandler abmhandler(m_abms, dtime_s, this, false);
855 abmhandler.apply(block);
858 void ServerEnvironment::addActiveBlockModifier(ActiveBlockModifier *abm)
860 m_abms.push_back(ABMWithState(abm));
863 bool ServerEnvironment::setNode(v3s16 p, const MapNode &n)
865 INodeDefManager *ndef = m_gamedef->ndef();
866 MapNode n_old = m_map->getNodeNoEx(p);
868 if(ndef->get(n_old).has_on_destruct)
869 m_script->node_on_destruct(p, n_old);
871 bool succeeded = m_map->addNodeWithEvent(p, n);
874 // Call post-destructor
875 if(ndef->get(n_old).has_after_destruct)
876 m_script->node_after_destruct(p, n_old);
878 if(ndef->get(n).has_on_construct)
879 m_script->node_on_construct(p, n);
883 bool ServerEnvironment::removeNode(v3s16 p)
885 INodeDefManager *ndef = m_gamedef->ndef();
886 MapNode n_old = m_map->getNodeNoEx(p);
888 if(ndef->get(n_old).has_on_destruct)
889 m_script->node_on_destruct(p, n_old);
891 // This is slightly optimized compared to addNodeWithEvent(air)
892 bool succeeded = m_map->removeNodeWithEvent(p);
895 // Call post-destructor
896 if(ndef->get(n_old).has_after_destruct)
897 m_script->node_after_destruct(p, n_old);
898 // Air doesn't require constructor
902 bool ServerEnvironment::swapNode(v3s16 p, const MapNode &n)
904 return m_map->addNodeWithEvent(p, n, false);
907 std::set<u16> ServerEnvironment::getObjectsInsideRadius(v3f pos, float radius)
909 std::set<u16> objects;
910 for(std::map<u16, ServerActiveObject*>::iterator
911 i = m_active_objects.begin();
912 i != m_active_objects.end(); ++i)
914 ServerActiveObject* obj = i->second;
916 v3f objectpos = obj->getBasePosition();
917 if(objectpos.getDistanceFrom(pos) > radius)
924 void ServerEnvironment::clearAllObjects()
926 infostream<<"ServerEnvironment::clearAllObjects(): "
927 <<"Removing all active objects"<<std::endl;
928 std::list<u16> objects_to_remove;
929 for(std::map<u16, ServerActiveObject*>::iterator
930 i = m_active_objects.begin();
931 i != m_active_objects.end(); ++i)
933 ServerActiveObject* obj = i->second;
934 if(obj->getType() == ACTIVEOBJECT_TYPE_PLAYER)
937 // Delete static object if block is loaded
938 if(obj->m_static_exists){
939 MapBlock *block = m_map->getBlockNoCreateNoEx(obj->m_static_block);
941 block->m_static_objects.remove(id);
942 block->raiseModified(MOD_STATE_WRITE_NEEDED,
944 obj->m_static_exists = false;
947 // If known by some client, don't delete immediately
948 if(obj->m_known_by_count > 0){
949 obj->m_pending_deactivation = true;
950 obj->m_removed = true;
954 // Tell the object about removal
955 obj->removingFromEnvironment();
956 // Deregister in scripting api
957 m_script->removeObjectReference(obj);
959 // Delete active object
960 if(obj->environmentDeletes())
962 // Id to be removed from m_active_objects
963 objects_to_remove.push_back(id);
965 // Remove references from m_active_objects
966 for(std::list<u16>::iterator i = objects_to_remove.begin();
967 i != objects_to_remove.end(); ++i)
969 m_active_objects.erase(*i);
972 // Get list of loaded blocks
973 std::list<v3s16> loaded_blocks;
974 infostream<<"ServerEnvironment::clearAllObjects(): "
975 <<"Listing all loaded blocks"<<std::endl;
976 m_map->listAllLoadedBlocks(loaded_blocks);
977 infostream<<"ServerEnvironment::clearAllObjects(): "
978 <<"Done listing all loaded blocks: "
979 <<loaded_blocks.size()<<std::endl;
981 // Get list of loadable blocks
982 std::list<v3s16> loadable_blocks;
983 infostream<<"ServerEnvironment::clearAllObjects(): "
984 <<"Listing all loadable blocks"<<std::endl;
985 m_map->listAllLoadableBlocks(loadable_blocks);
986 infostream<<"ServerEnvironment::clearAllObjects(): "
987 <<"Done listing all loadable blocks: "
988 <<loadable_blocks.size()
989 <<", now clearing"<<std::endl;
991 // Grab a reference on each loaded block to avoid unloading it
992 for(std::list<v3s16>::iterator i = loaded_blocks.begin();
993 i != loaded_blocks.end(); ++i)
996 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1001 // Remove objects in all loadable blocks
1002 u32 unload_interval = g_settings->getS32("max_clearobjects_extra_loaded_blocks");
1003 unload_interval = MYMAX(unload_interval, 1);
1004 u32 report_interval = loadable_blocks.size() / 10;
1005 u32 num_blocks_checked = 0;
1006 u32 num_blocks_cleared = 0;
1007 u32 num_objs_cleared = 0;
1008 for(std::list<v3s16>::iterator i = loadable_blocks.begin();
1009 i != loadable_blocks.end(); ++i)
1012 MapBlock *block = m_map->emergeBlock(p, false);
1014 errorstream<<"ServerEnvironment::clearAllObjects(): "
1015 <<"Failed to emerge block "<<PP(p)<<std::endl;
1018 u32 num_stored = block->m_static_objects.m_stored.size();
1019 u32 num_active = block->m_static_objects.m_active.size();
1020 if(num_stored != 0 || num_active != 0){
1021 block->m_static_objects.m_stored.clear();
1022 block->m_static_objects.m_active.clear();
1023 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1025 num_objs_cleared += num_stored + num_active;
1026 num_blocks_cleared++;
1028 num_blocks_checked++;
1030 if(report_interval != 0 &&
1031 num_blocks_checked % report_interval == 0){
1032 float percent = 100.0 * (float)num_blocks_checked /
1033 loadable_blocks.size();
1034 infostream<<"ServerEnvironment::clearAllObjects(): "
1035 <<"Cleared "<<num_objs_cleared<<" objects"
1036 <<" in "<<num_blocks_cleared<<" blocks ("
1037 <<percent<<"%)"<<std::endl;
1039 if(num_blocks_checked % unload_interval == 0){
1040 m_map->unloadUnreferencedBlocks();
1043 m_map->unloadUnreferencedBlocks();
1045 // Drop references that were added above
1046 for(std::list<v3s16>::iterator i = loaded_blocks.begin();
1047 i != loaded_blocks.end(); ++i)
1050 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1055 infostream<<"ServerEnvironment::clearAllObjects(): "
1056 <<"Finished: Cleared "<<num_objs_cleared<<" objects"
1057 <<" in "<<num_blocks_cleared<<" blocks"<<std::endl;
1060 void ServerEnvironment::step(float dtime)
1062 DSTACK(__FUNCTION_NAME);
1064 //TimeTaker timer("ServerEnv step");
1066 /* Step time of day */
1067 stepTimeOfDay(dtime);
1070 // NOTE: This is kind of funny on a singleplayer game, but doesn't
1071 // really matter that much.
1072 m_recommended_send_interval = g_settings->getFloat("dedicated_server_step");
1078 m_game_time_fraction_counter += dtime;
1079 u32 inc_i = (u32)m_game_time_fraction_counter;
1080 m_game_time += inc_i;
1081 m_game_time_fraction_counter -= (float)inc_i;
1088 ScopeProfiler sp(g_profiler, "SEnv: handle players avg", SPT_AVG);
1089 for(std::list<Player*>::iterator i = m_players.begin();
1090 i != m_players.end(); ++i)
1092 Player *player = *i;
1094 // Ignore disconnected players
1095 if(player->peer_id == 0)
1099 player->move(dtime, this, 100*BS);
1104 Manage active block list
1106 if(m_active_blocks_management_interval.step(dtime, 2.0))
1108 ScopeProfiler sp(g_profiler, "SEnv: manage act. block list avg /2s", SPT_AVG);
1110 Get player block positions
1112 std::list<v3s16> players_blockpos;
1113 for(std::list<Player*>::iterator
1114 i = m_players.begin();
1115 i != m_players.end(); ++i)
1117 Player *player = *i;
1118 // Ignore disconnected players
1119 if(player->peer_id == 0)
1121 v3s16 blockpos = getNodeBlockPos(
1122 floatToInt(player->getPosition(), BS));
1123 players_blockpos.push_back(blockpos);
1127 Update list of active blocks, collecting changes
1129 const s16 active_block_range = g_settings->getS16("active_block_range");
1130 std::set<v3s16> blocks_removed;
1131 std::set<v3s16> blocks_added;
1132 m_active_blocks.update(players_blockpos, active_block_range,
1133 blocks_removed, blocks_added);
1136 Handle removed blocks
1139 // Convert active objects that are no more in active blocks to static
1140 deactivateFarObjects(false);
1142 for(std::set<v3s16>::iterator
1143 i = blocks_removed.begin();
1144 i != blocks_removed.end(); ++i)
1148 /* infostream<<"Server: Block " << PP(p)
1149 << " became inactive"<<std::endl; */
1151 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1155 // Set current time as timestamp (and let it set ChangedFlag)
1156 block->setTimestamp(m_game_time);
1163 for(std::set<v3s16>::iterator
1164 i = blocks_added.begin();
1165 i != blocks_added.end(); ++i)
1169 MapBlock *block = m_map->getBlockOrEmerge(p);
1171 m_active_blocks.m_list.erase(p);
1175 activateBlock(block);
1176 /* infostream<<"Server: Block " << PP(p)
1177 << " became active"<<std::endl; */
1182 Mess around in active blocks
1184 if(m_active_blocks_nodemetadata_interval.step(dtime, 1.0))
1186 ScopeProfiler sp(g_profiler, "SEnv: mess in act. blocks avg /1s", SPT_AVG);
1190 for(std::set<v3s16>::iterator
1191 i = m_active_blocks.m_list.begin();
1192 i != m_active_blocks.m_list.end(); ++i)
1196 /*infostream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
1197 <<") being handled"<<std::endl;*/
1199 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1203 // Reset block usage timer
1204 block->resetUsageTimer();
1206 // Set current time as timestamp
1207 block->setTimestampNoChangedFlag(m_game_time);
1208 // If time has changed much from the one on disk,
1209 // set block to be saved when it is unloaded
1210 if(block->getTimestamp() > block->getDiskTimestamp() + 60)
1211 block->raiseModified(MOD_STATE_WRITE_AT_UNLOAD,
1212 "Timestamp older than 60s (step)");
1215 std::map<v3s16, NodeTimer> elapsed_timers =
1216 block->m_node_timers.step((float)dtime);
1217 if(!elapsed_timers.empty()){
1219 for(std::map<v3s16, NodeTimer>::iterator
1220 i = elapsed_timers.begin();
1221 i != elapsed_timers.end(); i++){
1222 n = block->getNodeNoEx(i->first);
1223 p = i->first + block->getPosRelative();
1224 if(m_script->node_on_timer(p,n,i->second.elapsed))
1225 block->setNodeTimer(i->first,NodeTimer(i->second.timeout,0));
1231 const float abm_interval = 1.0;
1232 if(m_active_block_modifier_interval.step(dtime, abm_interval))
1234 if(m_active_block_interval_overload_skip > 0){
1235 ScopeProfiler sp(g_profiler, "SEnv: ABM overload skips");
1236 m_active_block_interval_overload_skip--;
1239 ScopeProfiler sp(g_profiler, "SEnv: modify in blocks avg /1s", SPT_AVG);
1240 TimeTaker timer("modify in active blocks");
1242 // Initialize handling of ActiveBlockModifiers
1243 ABMHandler abmhandler(m_abms, abm_interval, this, true);
1245 for(std::set<v3s16>::iterator
1246 i = m_active_blocks.m_list.begin();
1247 i != m_active_blocks.m_list.end(); ++i)
1251 /*infostream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
1252 <<") being handled"<<std::endl;*/
1254 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1258 // Set current time as timestamp
1259 block->setTimestampNoChangedFlag(m_game_time);
1261 /* Handle ActiveBlockModifiers */
1262 abmhandler.apply(block);
1265 u32 time_ms = timer.stop(true);
1266 u32 max_time_ms = 200;
1267 if(time_ms > max_time_ms){
1268 infostream<<"WARNING: active block modifiers took "
1269 <<time_ms<<"ms (longer than "
1270 <<max_time_ms<<"ms)"<<std::endl;
1271 m_active_block_interval_overload_skip = (time_ms / max_time_ms) + 1;
1276 Step script environment (run global on_step())
1278 m_script->environment_Step(dtime);
1284 ScopeProfiler sp(g_profiler, "SEnv: step act. objs avg", SPT_AVG);
1285 //TimeTaker timer("Step active objects");
1287 g_profiler->avg("SEnv: num of objects", m_active_objects.size());
1289 // This helps the objects to send data at the same time
1290 bool send_recommended = false;
1291 m_send_recommended_timer += dtime;
1292 if(m_send_recommended_timer > getSendRecommendedInterval())
1294 m_send_recommended_timer -= getSendRecommendedInterval();
1295 send_recommended = true;
1298 for(std::map<u16, ServerActiveObject*>::iterator
1299 i = m_active_objects.begin();
1300 i != m_active_objects.end(); ++i)
1302 ServerActiveObject* obj = i->second;
1303 // Remove non-peaceful mobs on peaceful mode
1304 if(g_settings->getBool("only_peaceful_mobs")){
1305 if(!obj->isPeaceful())
1306 obj->m_removed = true;
1308 // Don't step if is to be removed or stored statically
1309 if(obj->m_removed || obj->m_pending_deactivation)
1312 obj->step(dtime, send_recommended);
1313 // Read messages from object
1314 while(!obj->m_messages_out.empty())
1316 m_active_object_messages.push_back(
1317 obj->m_messages_out.pop_front());
1323 Manage active objects
1325 if(m_object_management_interval.step(dtime, 0.5))
1327 ScopeProfiler sp(g_profiler, "SEnv: remove removed objs avg /.5s", SPT_AVG);
1329 Remove objects that satisfy (m_removed && m_known_by_count==0)
1331 removeRemovedObjects();
1335 ServerActiveObject* ServerEnvironment::getActiveObject(u16 id)
1337 std::map<u16, ServerActiveObject*>::iterator n;
1338 n = m_active_objects.find(id);
1339 if(n == m_active_objects.end())
1344 bool isFreeServerActiveObjectId(u16 id,
1345 std::map<u16, ServerActiveObject*> &objects)
1350 return objects.find(id) == objects.end();
1353 u16 getFreeServerActiveObjectId(
1354 std::map<u16, ServerActiveObject*> &objects)
1356 //try to reuse id's as late as possible
1357 static u16 last_used_id = 0;
1358 u16 startid = last_used_id;
1362 if(isFreeServerActiveObjectId(last_used_id, objects))
1363 return last_used_id;
1365 if(last_used_id == startid)
1370 u16 ServerEnvironment::addActiveObject(ServerActiveObject *object)
1374 u16 id = addActiveObjectRaw(object, true, 0);
1379 bool ServerEnvironment::addActiveObjectAsStatic(ServerActiveObject *obj)
1383 v3f objectpos = obj->getBasePosition();
1385 // The block in which the object resides in
1386 v3s16 blockpos_o = getNodeBlockPos(floatToInt(objectpos, BS));
1389 Update the static data
1392 // Create new static object
1393 std::string staticdata = obj->getStaticData();
1394 StaticObject s_obj(obj->getType(), objectpos, staticdata);
1395 // Add to the block where the object is located in
1396 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1397 // Get or generate the block
1398 MapBlock *block = m_map->emergeBlock(blockpos);
1400 bool succeeded = false;
1404 block->m_static_objects.insert(0, s_obj);
1405 block->raiseModified(MOD_STATE_WRITE_AT_UNLOAD,
1406 "addActiveObjectAsStatic");
1410 infostream<<"ServerEnvironment::addActiveObjectAsStatic: "
1411 <<"Could not find or generate "
1412 <<"a block for storing static object"<<std::endl;
1416 if(obj->environmentDeletes())
1424 Finds out what new objects have been added to
1425 inside a radius around a position
1427 void ServerEnvironment::getAddedActiveObjects(v3s16 pos, s16 radius,
1428 std::set<u16> ¤t_objects,
1429 std::set<u16> &added_objects)
1431 v3f pos_f = intToFloat(pos, BS);
1432 f32 radius_f = radius * BS;
1434 Go through the object list,
1435 - discard m_removed objects,
1436 - discard objects that are too far away,
1437 - discard objects that are found in current_objects.
1438 - add remaining objects to added_objects
1440 for(std::map<u16, ServerActiveObject*>::iterator
1441 i = m_active_objects.begin();
1442 i != m_active_objects.end(); ++i)
1446 ServerActiveObject *object = i->second;
1449 // Discard if removed or deactivating
1450 if(object->m_removed || object->m_pending_deactivation)
1452 if(object->unlimitedTransferDistance() == false){
1453 // Discard if too far
1454 f32 distance_f = object->getBasePosition().getDistanceFrom(pos_f);
1455 if(distance_f > radius_f)
1458 // Discard if already on current_objects
1459 std::set<u16>::iterator n;
1460 n = current_objects.find(id);
1461 if(n != current_objects.end())
1463 // Add to added_objects
1464 added_objects.insert(id);
1469 Finds out what objects have been removed from
1470 inside a radius around a position
1472 void ServerEnvironment::getRemovedActiveObjects(v3s16 pos, s16 radius,
1473 std::set<u16> ¤t_objects,
1474 std::set<u16> &removed_objects)
1476 v3f pos_f = intToFloat(pos, BS);
1477 f32 radius_f = radius * BS;
1479 Go through current_objects; object is removed if:
1480 - object is not found in m_active_objects (this is actually an
1481 error condition; objects should be set m_removed=true and removed
1482 only after all clients have been informed about removal), or
1483 - object has m_removed=true, or
1484 - object is too far away
1486 for(std::set<u16>::iterator
1487 i = current_objects.begin();
1488 i != current_objects.end(); ++i)
1491 ServerActiveObject *object = getActiveObject(id);
1494 infostream<<"ServerEnvironment::getRemovedActiveObjects():"
1495 <<" object in current_objects is NULL"<<std::endl;
1496 removed_objects.insert(id);
1500 if(object->m_removed || object->m_pending_deactivation)
1502 removed_objects.insert(id);
1506 // If transfer distance is unlimited, don't remove
1507 if(object->unlimitedTransferDistance())
1510 f32 distance_f = object->getBasePosition().getDistanceFrom(pos_f);
1512 if(distance_f >= radius_f)
1514 removed_objects.insert(id);
1522 ActiveObjectMessage ServerEnvironment::getActiveObjectMessage()
1524 if(m_active_object_messages.empty())
1525 return ActiveObjectMessage(0);
1527 ActiveObjectMessage message = m_active_object_messages.front();
1528 m_active_object_messages.pop_front();
1533 ************ Private methods *************
1536 u16 ServerEnvironment::addActiveObjectRaw(ServerActiveObject *object,
1537 bool set_changed, u32 dtime_s)
1540 if(object->getId() == 0){
1541 u16 new_id = getFreeServerActiveObjectId(m_active_objects);
1544 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1545 <<"no free ids available"<<std::endl;
1546 if(object->environmentDeletes())
1550 object->setId(new_id);
1553 verbosestream<<"ServerEnvironment::addActiveObjectRaw(): "
1554 <<"supplied with id "<<object->getId()<<std::endl;
1556 if(isFreeServerActiveObjectId(object->getId(), m_active_objects) == false)
1558 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1559 <<"id is not free ("<<object->getId()<<")"<<std::endl;
1560 if(object->environmentDeletes())
1564 /*infostream<<"ServerEnvironment::addActiveObjectRaw(): "
1565 <<"added (id="<<object->getId()<<")"<<std::endl;*/
1567 m_active_objects[object->getId()] = object;
1569 verbosestream<<"ServerEnvironment::addActiveObjectRaw(): "
1570 <<"Added id="<<object->getId()<<"; there are now "
1571 <<m_active_objects.size()<<" active objects."
1574 // Register reference in scripting api (must be done before post-init)
1575 m_script->addObjectReference(object);
1576 // Post-initialize object
1577 object->addedToEnvironment(dtime_s);
1579 // Add static data to block
1580 if(object->isStaticAllowed())
1582 // Add static object to active static list of the block
1583 v3f objectpos = object->getBasePosition();
1584 std::string staticdata = object->getStaticData();
1585 StaticObject s_obj(object->getType(), objectpos, staticdata);
1586 // Add to the block where the object is located in
1587 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1588 MapBlock *block = m_map->emergeBlock(blockpos);
1590 block->m_static_objects.m_active[object->getId()] = s_obj;
1591 object->m_static_exists = true;
1592 object->m_static_block = blockpos;
1595 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1596 "addActiveObjectRaw");
1598 v3s16 p = floatToInt(objectpos, BS);
1599 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1600 <<"could not emerge block for storing id="<<object->getId()
1601 <<" statically (pos="<<PP(p)<<")"<<std::endl;
1605 return object->getId();
1609 Remove objects that satisfy (m_removed && m_known_by_count==0)
1611 void ServerEnvironment::removeRemovedObjects()
1613 std::list<u16> objects_to_remove;
1614 for(std::map<u16, ServerActiveObject*>::iterator
1615 i = m_active_objects.begin();
1616 i != m_active_objects.end(); ++i)
1619 ServerActiveObject* obj = i->second;
1620 // This shouldn't happen but check it
1623 infostream<<"NULL object found in ServerEnvironment"
1624 <<" while finding removed objects. id="<<id<<std::endl;
1625 // Id to be removed from m_active_objects
1626 objects_to_remove.push_back(id);
1631 We will delete objects that are marked as removed or thatare
1632 waiting for deletion after deactivation
1634 if(obj->m_removed == false && obj->m_pending_deactivation == false)
1638 Delete static data from block if is marked as removed
1640 if(obj->m_static_exists && obj->m_removed)
1642 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
1644 block->m_static_objects.remove(id);
1645 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1646 "removeRemovedObjects/remove");
1647 obj->m_static_exists = false;
1649 infostream<<"Failed to emerge block from which an object to "
1650 <<"be removed was loaded from. id="<<id<<std::endl;
1654 // If m_known_by_count > 0, don't actually remove. On some future
1655 // invocation this will be 0, which is when removal will continue.
1656 if(obj->m_known_by_count > 0)
1660 Move static data from active to stored if not marked as removed
1662 if(obj->m_static_exists && !obj->m_removed){
1663 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
1665 std::map<u16, StaticObject>::iterator i =
1666 block->m_static_objects.m_active.find(id);
1667 if(i != block->m_static_objects.m_active.end()){
1668 block->m_static_objects.m_stored.push_back(i->second);
1669 block->m_static_objects.m_active.erase(id);
1670 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1671 "removeRemovedObjects/deactivate");
1674 infostream<<"Failed to emerge block from which an object to "
1675 <<"be deactivated was loaded from. id="<<id<<std::endl;
1679 // Tell the object about removal
1680 obj->removingFromEnvironment();
1681 // Deregister in scripting api
1682 m_script->removeObjectReference(obj);
1685 if(obj->environmentDeletes())
1687 // Id to be removed from m_active_objects
1688 objects_to_remove.push_back(id);
1690 // Remove references from m_active_objects
1691 for(std::list<u16>::iterator i = objects_to_remove.begin();
1692 i != objects_to_remove.end(); ++i)
1694 m_active_objects.erase(*i);
1698 static void print_hexdump(std::ostream &o, const std::string &data)
1700 const int linelength = 16;
1701 for(int l=0; ; l++){
1702 int i0 = linelength * l;
1703 bool at_end = false;
1704 int thislinelength = linelength;
1705 if(i0 + thislinelength > (int)data.size()){
1706 thislinelength = data.size() - i0;
1709 for(int di=0; di<linelength; di++){
1712 if(di<thislinelength)
1713 snprintf(buf, 4, "%.2x ", data[i]);
1715 snprintf(buf, 4, " ");
1719 for(int di=0; di<thislinelength; di++){
1733 Convert stored objects from blocks near the players to active.
1735 void ServerEnvironment::activateObjects(MapBlock *block, u32 dtime_s)
1739 // Ignore if no stored objects (to not set changed flag)
1740 if(block->m_static_objects.m_stored.size() == 0)
1742 verbosestream<<"ServerEnvironment::activateObjects(): "
1743 <<"activating objects of block "<<PP(block->getPos())
1744 <<" ("<<block->m_static_objects.m_stored.size()
1745 <<" objects)"<<std::endl;
1746 bool large_amount = (block->m_static_objects.m_stored.size() > g_settings->getU16("max_objects_per_block"));
1748 errorstream<<"suspiciously large amount of objects detected: "
1749 <<block->m_static_objects.m_stored.size()<<" in "
1750 <<PP(block->getPos())
1751 <<"; removing all of them."<<std::endl;
1752 // Clear stored list
1753 block->m_static_objects.m_stored.clear();
1754 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1755 "stored list cleared in activateObjects due to "
1756 "large amount of objects");
1760 // Activate stored objects
1761 std::list<StaticObject> new_stored;
1762 for(std::list<StaticObject>::iterator
1763 i = block->m_static_objects.m_stored.begin();
1764 i != block->m_static_objects.m_stored.end(); ++i)
1766 /*infostream<<"Server: Creating an active object from "
1767 <<"static data"<<std::endl;*/
1768 StaticObject &s_obj = *i;
1769 // Create an active object from the data
1770 ServerActiveObject *obj = ServerActiveObject::create
1771 (s_obj.type, this, 0, s_obj.pos, s_obj.data);
1772 // If couldn't create object, store static data back.
1775 errorstream<<"ServerEnvironment::activateObjects(): "
1776 <<"failed to create active object from static object "
1777 <<"in block "<<PP(s_obj.pos/BS)
1778 <<" type="<<(int)s_obj.type<<" data:"<<std::endl;
1779 print_hexdump(verbosestream, s_obj.data);
1781 new_stored.push_back(s_obj);
1784 verbosestream<<"ServerEnvironment::activateObjects(): "
1785 <<"activated static object pos="<<PP(s_obj.pos/BS)
1786 <<" type="<<(int)s_obj.type<<std::endl;
1787 // This will also add the object to the active static list
1788 addActiveObjectRaw(obj, false, dtime_s);
1790 // Clear stored list
1791 block->m_static_objects.m_stored.clear();
1792 // Add leftover failed stuff to stored list
1793 for(std::list<StaticObject>::iterator
1794 i = new_stored.begin();
1795 i != new_stored.end(); ++i)
1797 StaticObject &s_obj = *i;
1798 block->m_static_objects.m_stored.push_back(s_obj);
1801 // Turn the active counterparts of activated objects not pending for
1803 for(std::map<u16, StaticObject>::iterator
1804 i = block->m_static_objects.m_active.begin();
1805 i != block->m_static_objects.m_active.end(); ++i)
1808 ServerActiveObject *object = getActiveObject(id);
1810 object->m_pending_deactivation = false;
1814 Note: Block hasn't really been modified here.
1815 The objects have just been activated and moved from the stored
1816 static list to the active static list.
1817 As such, the block is essentially the same.
1818 Thus, do not call block->raiseModified(MOD_STATE_WRITE_NEEDED).
1819 Otherwise there would be a huge amount of unnecessary I/O.
1824 Convert objects that are not standing inside active blocks to static.
1826 If m_known_by_count != 0, active object is not deleted, but static
1827 data is still updated.
1829 If force_delete is set, active object is deleted nevertheless. It
1830 shall only be set so in the destructor of the environment.
1832 If block wasn't generated (not in memory or on disk),
1834 void ServerEnvironment::deactivateFarObjects(bool force_delete)
1836 std::list<u16> objects_to_remove;
1837 for(std::map<u16, ServerActiveObject*>::iterator
1838 i = m_active_objects.begin();
1839 i != m_active_objects.end(); ++i)
1841 ServerActiveObject* obj = i->second;
1844 // Do not deactivate if static data creation not allowed
1845 if(!force_delete && !obj->isStaticAllowed())
1848 // If pending deactivation, let removeRemovedObjects() do it
1849 if(!force_delete && obj->m_pending_deactivation)
1853 v3f objectpos = obj->getBasePosition();
1855 // The block in which the object resides in
1856 v3s16 blockpos_o = getNodeBlockPos(floatToInt(objectpos, BS));
1858 // If object's static data is stored in a deactivated block and object
1859 // is actually located in an active block, re-save to the block in
1860 // which the object is actually located in.
1862 obj->m_static_exists &&
1863 !m_active_blocks.contains(obj->m_static_block) &&
1864 m_active_blocks.contains(blockpos_o))
1866 v3s16 old_static_block = obj->m_static_block;
1868 // Save to block where object is located
1869 MapBlock *block = m_map->emergeBlock(blockpos_o, false);
1871 errorstream<<"ServerEnvironment::deactivateFarObjects(): "
1872 <<"Could not save object id="<<id
1873 <<" to it's current block "<<PP(blockpos_o)
1877 std::string staticdata_new = obj->getStaticData();
1878 StaticObject s_obj(obj->getType(), objectpos, staticdata_new);
1879 block->m_static_objects.insert(id, s_obj);
1880 obj->m_static_block = blockpos_o;
1881 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1882 "deactivateFarObjects: Static data moved in");
1884 // Delete from block where object was located
1885 block = m_map->emergeBlock(old_static_block, false);
1887 errorstream<<"ServerEnvironment::deactivateFarObjects(): "
1888 <<"Could not delete object id="<<id
1889 <<" from it's previous block "<<PP(old_static_block)
1893 block->m_static_objects.remove(id);
1894 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1895 "deactivateFarObjects: Static data moved out");
1899 // If block is active, don't remove
1900 if(!force_delete && m_active_blocks.contains(blockpos_o))
1903 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
1904 <<"deactivating object id="<<id<<" on inactive block "
1905 <<PP(blockpos_o)<<std::endl;
1907 // If known by some client, don't immediately delete.
1908 bool pending_delete = (obj->m_known_by_count > 0 && !force_delete);
1911 Update the static data
1914 if(obj->isStaticAllowed())
1916 // Create new static object
1917 std::string staticdata_new = obj->getStaticData();
1918 StaticObject s_obj(obj->getType(), objectpos, staticdata_new);
1920 bool stays_in_same_block = false;
1921 bool data_changed = true;
1923 if(obj->m_static_exists){
1924 if(obj->m_static_block == blockpos_o)
1925 stays_in_same_block = true;
1927 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
1929 std::map<u16, StaticObject>::iterator n =
1930 block->m_static_objects.m_active.find(id);
1931 if(n != block->m_static_objects.m_active.end()){
1932 StaticObject static_old = n->second;
1934 float save_movem = obj->getMinimumSavedMovement();
1936 if(static_old.data == staticdata_new &&
1937 (static_old.pos - objectpos).getLength() < save_movem)
1938 data_changed = false;
1940 errorstream<<"ServerEnvironment::deactivateFarObjects(): "
1941 <<"id="<<id<<" m_static_exists=true but "
1942 <<"static data doesn't actually exist in "
1943 <<PP(obj->m_static_block)<<std::endl;
1947 bool shall_be_written = (!stays_in_same_block || data_changed);
1949 // Delete old static object
1950 if(obj->m_static_exists)
1952 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
1955 block->m_static_objects.remove(id);
1956 obj->m_static_exists = false;
1957 // Only mark block as modified if data changed considerably
1958 if(shall_be_written)
1959 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1960 "deactivateFarObjects: Static data "
1961 "changed considerably");
1965 // Add to the block where the object is located in
1966 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1967 // Get or generate the block
1968 MapBlock *block = NULL;
1970 block = m_map->emergeBlock(blockpos);
1971 } catch(InvalidPositionException &e){
1972 // Handled via NULL pointer
1973 // NOTE: emergeBlock's failure is usually determined by it
1974 // actually returning NULL
1979 if(block->m_static_objects.m_stored.size() >= g_settings->getU16("max_objects_per_block")){
1980 errorstream<<"ServerEnv: Trying to store id="<<obj->getId()
1981 <<" statically but block "<<PP(blockpos)
1982 <<" already contains "
1983 <<block->m_static_objects.m_stored.size()
1985 <<" Forcing delete."<<std::endl;
1986 force_delete = true;
1988 // If static counterpart already exists in target block,
1990 // This shouldn't happen because the object is removed from
1991 // the previous block before this according to
1992 // obj->m_static_block, but happens rarely for some unknown
1993 // reason. Unsuccessful attempts have been made to find
1995 if(id && block->m_static_objects.m_active.find(id) != block->m_static_objects.m_active.end()){
1996 infostream<<"ServerEnv: WARNING: Performing hack #83274"
1998 block->m_static_objects.remove(id);
2000 // Store static data
2001 u16 store_id = pending_delete ? id : 0;
2002 block->m_static_objects.insert(store_id, s_obj);
2004 // Only mark block as modified if data changed considerably
2005 if(shall_be_written)
2006 block->raiseModified(MOD_STATE_WRITE_NEEDED,
2007 "deactivateFarObjects: Static data "
2008 "changed considerably");
2010 obj->m_static_exists = true;
2011 obj->m_static_block = block->getPos();
2016 v3s16 p = floatToInt(objectpos, BS);
2017 errorstream<<"ServerEnv: Could not find or generate "
2018 <<"a block for storing id="<<obj->getId()
2019 <<" statically (pos="<<PP(p)<<")"<<std::endl;
2026 If known by some client, set pending deactivation.
2027 Otherwise delete it immediately.
2030 if(pending_delete && !force_delete)
2032 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
2033 <<"object id="<<id<<" is known by clients"
2034 <<"; not deleting yet"<<std::endl;
2036 obj->m_pending_deactivation = true;
2040 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
2041 <<"object id="<<id<<" is not known by clients"
2042 <<"; deleting"<<std::endl;
2044 // Tell the object about removal
2045 obj->removingFromEnvironment();
2046 // Deregister in scripting api
2047 m_script->removeObjectReference(obj);
2049 // Delete active object
2050 if(obj->environmentDeletes())
2052 // Id to be removed from m_active_objects
2053 objects_to_remove.push_back(id);
2056 // Remove references from m_active_objects
2057 for(std::list<u16>::iterator i = objects_to_remove.begin();
2058 i != objects_to_remove.end(); ++i)
2060 m_active_objects.erase(*i);
2067 #include "clientsimpleobject.h"
2073 ClientEnvironment::ClientEnvironment(ClientMap *map, scene::ISceneManager *smgr,
2074 ITextureSource *texturesource, IGameDef *gamedef,
2075 IrrlichtDevice *irr):
2078 m_texturesource(texturesource),
2084 ClientEnvironment::~ClientEnvironment()
2086 // delete active objects
2087 for(std::map<u16, ClientActiveObject*>::iterator
2088 i = m_active_objects.begin();
2089 i != m_active_objects.end(); ++i)
2094 for(std::list<ClientSimpleObject*>::iterator
2095 i = m_simple_objects.begin(); i != m_simple_objects.end(); ++i)
2104 Map & ClientEnvironment::getMap()
2109 ClientMap & ClientEnvironment::getClientMap()
2114 void ClientEnvironment::addPlayer(Player *player)
2116 DSTACK(__FUNCTION_NAME);
2118 It is a failure if player is local and there already is a local
2121 assert(!(player->isLocal() == true && getLocalPlayer() != NULL));
2123 Environment::addPlayer(player);
2126 LocalPlayer * ClientEnvironment::getLocalPlayer()
2128 for(std::list<Player*>::iterator i = m_players.begin();
2129 i != m_players.end(); ++i)
2131 Player *player = *i;
2132 if(player->isLocal())
2133 return (LocalPlayer*)player;
2138 void ClientEnvironment::step(float dtime)
2140 DSTACK(__FUNCTION_NAME);
2142 /* Step time of day */
2143 stepTimeOfDay(dtime);
2145 // Get some settings
2146 bool fly_allowed = m_gamedef->checkLocalPrivilege("fly");
2147 bool free_move = fly_allowed && g_settings->getBool("free_move");
2150 LocalPlayer *lplayer = getLocalPlayer();
2152 // collision info queue
2153 std::list<CollisionInfo> player_collisions;
2156 Get the speed the player is going
2158 bool is_climbing = lplayer->is_climbing;
2160 f32 player_speed = lplayer->getSpeed().getLength();
2163 Maximum position increment
2165 //f32 position_max_increment = 0.05*BS;
2166 f32 position_max_increment = 0.1*BS;
2168 // Maximum time increment (for collision detection etc)
2169 // time = distance / speed
2170 f32 dtime_max_increment = 1;
2171 if(player_speed > 0.001)
2172 dtime_max_increment = position_max_increment / player_speed;
2174 // Maximum time increment is 10ms or lower
2175 if(dtime_max_increment > 0.01)
2176 dtime_max_increment = 0.01;
2178 // Don't allow overly huge dtime
2182 f32 dtime_downcount = dtime;
2185 Stuff that has a maximum time increment
2194 if(dtime_downcount > dtime_max_increment)
2196 dtime_part = dtime_max_increment;
2197 dtime_downcount -= dtime_part;
2201 dtime_part = dtime_downcount;
2203 Setting this to 0 (no -=dtime_part) disables an infinite loop
2204 when dtime_part is so small that dtime_downcount -= dtime_part
2207 dtime_downcount = 0;
2216 if(free_move == false && is_climbing == false)
2219 v3f speed = lplayer->getSpeed();
2220 if(lplayer->in_liquid == false)
2221 speed.Y -= lplayer->movement_gravity * lplayer->physics_override_gravity * dtime_part * 2;
2223 // Liquid floating / sinking
2224 if(lplayer->in_liquid && !lplayer->swimming_vertical)
2225 speed.Y -= lplayer->movement_liquid_sink * dtime_part * 2;
2227 // Liquid resistance
2228 if(lplayer->in_liquid_stable || lplayer->in_liquid)
2230 // How much the node's viscosity blocks movement, ranges between 0 and 1
2231 // Should match the scale at which viscosity increase affects other liquid attributes
2232 const f32 viscosity_factor = 0.3;
2234 v3f d_wanted = -speed / lplayer->movement_liquid_fluidity;
2235 f32 dl = d_wanted.getLength();
2236 if(dl > lplayer->movement_liquid_fluidity_smooth)
2237 dl = lplayer->movement_liquid_fluidity_smooth;
2238 dl *= (lplayer->liquid_viscosity * viscosity_factor) + (1 - viscosity_factor);
2240 v3f d = d_wanted.normalize() * dl;
2244 if(speed.X > lplayer->movement_liquid_fluidity + lplayer->movement_liquid_fluidity_smooth) speed.X -= lplayer->movement_liquid_fluidity_smooth;
2245 if(speed.X < -lplayer->movement_liquid_fluidity - lplayer->movement_liquid_fluidity_smooth) speed.X += 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.Y < -lplayer->movement_liquid_fluidity - lplayer->movement_liquid_fluidity_smooth) speed.Y += 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;
2249 if(speed.Z < -lplayer->movement_liquid_fluidity - lplayer->movement_liquid_fluidity_smooth) speed.Z += lplayer->movement_liquid_fluidity_smooth;
2253 lplayer->setSpeed(speed);
2258 This also does collision detection.
2260 lplayer->move(dtime_part, this, position_max_increment,
2261 &player_collisions);
2264 while(dtime_downcount > 0.001);
2266 //std::cout<<"Looped "<<loopcount<<" times."<<std::endl;
2268 for(std::list<CollisionInfo>::iterator
2269 i = player_collisions.begin();
2270 i != player_collisions.end(); ++i)
2272 CollisionInfo &info = *i;
2273 v3f speed_diff = info.new_speed - info.old_speed;;
2274 // Handle only fall damage
2275 // (because otherwise walking against something in fast_move kills you)
2276 if(speed_diff.Y < 0 || info.old_speed.Y >= 0)
2278 // Get rid of other components
2281 f32 pre_factor = 1; // 1 hp per node/s
2282 f32 tolerance = BS*14; // 5 without damage
2283 f32 post_factor = 1; // 1 hp per node/s
2284 if(info.type == COLLISION_NODE)
2286 const ContentFeatures &f = m_gamedef->ndef()->
2287 get(m_map->getNodeNoEx(info.node_p));
2288 // Determine fall damage multiplier
2289 int addp = itemgroup_get(f.groups, "fall_damage_add_percent");
2290 pre_factor = 1.0 + (float)addp/100.0;
2292 float speed = pre_factor * speed_diff.getLength();
2293 if(speed > tolerance)
2295 f32 damage_f = (speed - tolerance)/BS * post_factor;
2296 u16 damage = (u16)(damage_f+0.5);
2298 damageLocalPlayer(damage, true);
2299 MtEvent *e = new SimpleTriggerEvent("PlayerFallingDamage");
2300 m_gamedef->event()->put(e);
2306 A quick draft of lava damage
2308 if(m_lava_hurt_interval.step(dtime, 1.0))
2310 v3f pf = lplayer->getPosition();
2312 // Feet, middle and head
2313 v3s16 p1 = floatToInt(pf + v3f(0, BS*0.1, 0), BS);
2314 MapNode n1 = m_map->getNodeNoEx(p1);
2315 v3s16 p2 = floatToInt(pf + v3f(0, BS*0.8, 0), BS);
2316 MapNode n2 = m_map->getNodeNoEx(p2);
2317 v3s16 p3 = floatToInt(pf + v3f(0, BS*1.6, 0), BS);
2318 MapNode n3 = m_map->getNodeNoEx(p3);
2320 u32 damage_per_second = 0;
2321 damage_per_second = MYMAX(damage_per_second,
2322 m_gamedef->ndef()->get(n1).damage_per_second);
2323 damage_per_second = MYMAX(damage_per_second,
2324 m_gamedef->ndef()->get(n2).damage_per_second);
2325 damage_per_second = MYMAX(damage_per_second,
2326 m_gamedef->ndef()->get(n3).damage_per_second);
2328 if(damage_per_second != 0)
2330 damageLocalPlayer(damage_per_second, true);
2337 if(m_drowning_interval.step(dtime, 2.0))
2339 v3f pf = lplayer->getPosition();
2342 v3s16 p = floatToInt(pf + v3f(0, BS*1.6, 0), BS);
2343 MapNode n = m_map->getNodeNoEx(p);
2344 ContentFeatures c = m_gamedef->ndef()->get(n);
2345 u8 drowning_damage = c.drowning;
2346 if(drowning_damage > 0 && lplayer->hp > 0){
2347 u16 breath = lplayer->getBreath();
2354 lplayer->setBreath(breath);
2355 updateLocalPlayerBreath(breath);
2358 if(lplayer->getBreath() == 0 && drowning_damage > 0){
2359 damageLocalPlayer(drowning_damage, true);
2362 if(m_breathing_interval.step(dtime, 0.5))
2364 v3f pf = lplayer->getPosition();
2367 v3s16 p = floatToInt(pf + v3f(0, BS*1.6, 0), BS);
2368 MapNode n = m_map->getNodeNoEx(p);
2369 ContentFeatures c = m_gamedef->ndef()->get(n);
2371 lplayer->setBreath(11);
2373 else if(c.drowning == 0){
2374 u16 breath = lplayer->getBreath();
2377 lplayer->setBreath(breath);
2378 updateLocalPlayerBreath(breath);
2384 Stuff that can be done in an arbitarily large dtime
2386 for(std::list<Player*>::iterator i = m_players.begin();
2387 i != m_players.end(); ++i)
2389 Player *player = *i;
2392 Handle non-local players
2394 if(player->isLocal() == false)
2397 player->move(dtime, this, 100*BS);
2401 // Update lighting on all players on client
2405 v3s16 p = player->getLightPosition();
2406 MapNode n = m_map->getNode(p);
2407 light = n.getLightBlendF1((float)getDayNightRatio()/1000, m_gamedef->ndef());
2409 catch(InvalidPositionException &e){
2410 light = blend_light_f1((float)getDayNightRatio()/1000, LIGHT_SUN, 0);
2412 player->light = light;
2416 Step active objects and update lighting of them
2419 g_profiler->avg("CEnv: num of objects", m_active_objects.size());
2420 bool update_lighting = m_active_object_light_update_interval.step(dtime, 0.21);
2421 for(std::map<u16, ClientActiveObject*>::iterator
2422 i = m_active_objects.begin();
2423 i != m_active_objects.end(); ++i)
2425 ClientActiveObject* obj = i->second;
2427 obj->step(dtime, this);
2435 v3s16 p = obj->getLightPosition();
2436 MapNode n = m_map->getNode(p);
2437 light = n.getLightBlend(getDayNightRatio(), m_gamedef->ndef());
2439 catch(InvalidPositionException &e){
2440 light = blend_light(getDayNightRatio(), LIGHT_SUN, 0);
2442 obj->updateLight(light);
2447 Step and handle simple objects
2449 g_profiler->avg("CEnv: num of simple objects", m_simple_objects.size());
2450 for(std::list<ClientSimpleObject*>::iterator
2451 i = m_simple_objects.begin(); i != m_simple_objects.end();)
2453 ClientSimpleObject *simple = *i;
2454 std::list<ClientSimpleObject*>::iterator cur = i;
2456 simple->step(dtime);
2457 if(simple->m_to_be_removed){
2459 m_simple_objects.erase(cur);
2464 void ClientEnvironment::addSimpleObject(ClientSimpleObject *simple)
2466 m_simple_objects.push_back(simple);
2469 ClientActiveObject* ClientEnvironment::getActiveObject(u16 id)
2471 std::map<u16, ClientActiveObject*>::iterator n;
2472 n = m_active_objects.find(id);
2473 if(n == m_active_objects.end())
2478 bool isFreeClientActiveObjectId(u16 id,
2479 std::map<u16, ClientActiveObject*> &objects)
2484 return objects.find(id) == objects.end();
2487 u16 getFreeClientActiveObjectId(
2488 std::map<u16, ClientActiveObject*> &objects)
2490 //try to reuse id's as late as possible
2491 static u16 last_used_id = 0;
2492 u16 startid = last_used_id;
2496 if(isFreeClientActiveObjectId(last_used_id, objects))
2497 return last_used_id;
2499 if(last_used_id == startid)
2504 u16 ClientEnvironment::addActiveObject(ClientActiveObject *object)
2507 if(object->getId() == 0)
2509 u16 new_id = getFreeClientActiveObjectId(m_active_objects);
2512 infostream<<"ClientEnvironment::addActiveObject(): "
2513 <<"no free ids available"<<std::endl;
2517 object->setId(new_id);
2519 if(isFreeClientActiveObjectId(object->getId(), m_active_objects) == false)
2521 infostream<<"ClientEnvironment::addActiveObject(): "
2522 <<"id is not free ("<<object->getId()<<")"<<std::endl;
2526 infostream<<"ClientEnvironment::addActiveObject(): "
2527 <<"added (id="<<object->getId()<<")"<<std::endl;
2528 m_active_objects[object->getId()] = object;
2529 object->addToScene(m_smgr, m_texturesource, m_irr);
2530 { // Update lighting immediately
2534 v3s16 p = object->getLightPosition();
2535 MapNode n = m_map->getNode(p);
2536 light = n.getLightBlend(getDayNightRatio(), m_gamedef->ndef());
2538 catch(InvalidPositionException &e){
2539 light = blend_light(getDayNightRatio(), LIGHT_SUN, 0);
2541 object->updateLight(light);
2543 return object->getId();
2546 void ClientEnvironment::addActiveObject(u16 id, u8 type,
2547 const std::string &init_data)
2549 ClientActiveObject* obj =
2550 ClientActiveObject::create(type, m_gamedef, this);
2553 infostream<<"ClientEnvironment::addActiveObject(): "
2554 <<"id="<<id<<" type="<<type<<": Couldn't create object"
2563 obj->initialize(init_data);
2565 catch(SerializationError &e)
2567 errorstream<<"ClientEnvironment::addActiveObject():"
2568 <<" id="<<id<<" type="<<type
2569 <<": SerializationError in initialize(): "
2571 <<": init_data="<<serializeJsonString(init_data)
2575 addActiveObject(obj);
2578 void ClientEnvironment::removeActiveObject(u16 id)
2580 verbosestream<<"ClientEnvironment::removeActiveObject(): "
2581 <<"id="<<id<<std::endl;
2582 ClientActiveObject* obj = getActiveObject(id);
2585 infostream<<"ClientEnvironment::removeActiveObject(): "
2586 <<"id="<<id<<" not found"<<std::endl;
2589 obj->removeFromScene(true);
2591 m_active_objects.erase(id);
2594 void ClientEnvironment::processActiveObjectMessage(u16 id,
2595 const std::string &data)
2597 ClientActiveObject* obj = getActiveObject(id);
2600 infostream<<"ClientEnvironment::processActiveObjectMessage():"
2601 <<" got message for id="<<id<<", which doesn't exist."
2607 obj->processMessage(data);
2609 catch(SerializationError &e)
2611 errorstream<<"ClientEnvironment::processActiveObjectMessage():"
2612 <<" id="<<id<<" type="<<obj->getType()
2613 <<" SerializationError in processMessage(),"
2614 <<" message="<<serializeJsonString(data)
2620 Callbacks for activeobjects
2623 void ClientEnvironment::damageLocalPlayer(u8 damage, bool handle_hp)
2625 LocalPlayer *lplayer = getLocalPlayer();
2629 if(lplayer->hp > damage)
2630 lplayer->hp -= damage;
2635 ClientEnvEvent event;
2636 event.type = CEE_PLAYER_DAMAGE;
2637 event.player_damage.amount = damage;
2638 event.player_damage.send_to_server = handle_hp;
2639 m_client_event_queue.push_back(event);
2642 void ClientEnvironment::updateLocalPlayerBreath(u16 breath)
2644 ClientEnvEvent event;
2645 event.type = CEE_PLAYER_BREATH;
2646 event.player_breath.amount = breath;
2647 m_client_event_queue.push_back(event);
2651 Client likes to call these
2654 void ClientEnvironment::getActiveObjects(v3f origin, f32 max_d,
2655 std::vector<DistanceSortedActiveObject> &dest)
2657 for(std::map<u16, ClientActiveObject*>::iterator
2658 i = m_active_objects.begin();
2659 i != m_active_objects.end(); ++i)
2661 ClientActiveObject* obj = i->second;
2663 f32 d = (obj->getPosition() - origin).getLength();
2668 DistanceSortedActiveObject dso(obj, d);
2670 dest.push_back(dso);
2674 ClientEnvEvent ClientEnvironment::getClientEvent()
2676 ClientEnvEvent event;
2677 if(m_client_event_queue.empty())
2678 event.type = CEE_NONE;
2680 event = m_client_event_queue.front();
2681 m_client_event_queue.pop_front();
2686 #endif // #ifndef SERVER