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),
56 Environment::~Environment()
59 for(std::list<Player*>::iterator i = m_players.begin();
60 i != m_players.end(); ++i)
66 void Environment::addPlayer(Player *player)
68 DSTACK(__FUNCTION_NAME);
70 Check that peer_ids are unique.
71 Also check that names are unique.
72 Exception: there can be multiple players with peer_id=0
74 // If peer id is non-zero, it has to be unique.
75 if(player->peer_id != 0)
76 assert(getPlayer(player->peer_id) == NULL);
77 // Name has to be unique.
78 assert(getPlayer(player->getName()) == NULL);
80 m_players.push_back(player);
83 void Environment::removePlayer(u16 peer_id)
85 DSTACK(__FUNCTION_NAME);
87 for(std::list<Player*>::iterator i = m_players.begin();
88 i != m_players.end(); ++i)
91 if(player->peer_id != peer_id)
96 // See if there is an another one
97 // (shouldn't be, but just to be sure)
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 bool smooth = g_settings->getBool("enable_shaders");
194 return time_to_daynight_ratio(m_time_of_day_f*24000, smooth);
197 void Environment::stepTimeOfDay(float dtime)
199 m_time_counter += dtime;
200 f32 speed = m_time_of_day_speed * 24000./(24.*3600);
201 u32 units = (u32)(m_time_counter*speed);
202 m_time_counter -= (f32)units / speed;
206 if(m_time_of_day + units >= 24000)
208 m_time_of_day = (m_time_of_day + units) % 24000;
210 m_time_of_day_f = (float)m_time_of_day / 24000.0;
213 m_time_of_day_f += m_time_of_day_speed/24/3600*dtime;
214 if(m_time_of_day_f > 1.0)
215 m_time_of_day_f -= 1.0;
216 if(m_time_of_day_f < 0.0)
217 m_time_of_day_f += 1.0;
225 ABMWithState::ABMWithState(ActiveBlockModifier *abm_):
229 // Initialize timer to random value to spread processing
230 float itv = abm->getTriggerInterval();
231 itv = MYMAX(0.001, itv); // No less than 1ms
232 int minval = MYMAX(-0.51*itv, -60); // Clamp to
233 int maxval = MYMIN(0.51*itv, 60); // +-60 seconds
234 timer = myrand_range(minval, maxval);
241 void fillRadiusBlock(v3s16 p0, s16 r, std::set<v3s16> &list)
244 for(p.X=p0.X-r; p.X<=p0.X+r; p.X++)
245 for(p.Y=p0.Y-r; p.Y<=p0.Y+r; p.Y++)
246 for(p.Z=p0.Z-r; p.Z<=p0.Z+r; p.Z++)
253 void ActiveBlockList::update(std::list<v3s16> &active_positions,
255 std::set<v3s16> &blocks_removed,
256 std::set<v3s16> &blocks_added)
261 std::set<v3s16> newlist = m_forceloaded_list;
262 for(std::list<v3s16>::iterator i = active_positions.begin();
263 i != active_positions.end(); ++i)
265 fillRadiusBlock(*i, radius, newlist);
269 Find out which blocks on the old list are not on the new list
271 // Go through old list
272 for(std::set<v3s16>::iterator i = m_list.begin();
273 i != m_list.end(); ++i)
276 // If not on new list, it's been removed
277 if(newlist.find(p) == newlist.end())
278 blocks_removed.insert(p);
282 Find out which blocks on the new list are not on the old list
284 // Go through new list
285 for(std::set<v3s16>::iterator i = newlist.begin();
286 i != newlist.end(); ++i)
289 // If not on old list, it's been added
290 if(m_list.find(p) == m_list.end())
291 blocks_added.insert(p);
298 for(std::set<v3s16>::iterator i = newlist.begin();
299 i != newlist.end(); ++i)
310 ServerEnvironment::ServerEnvironment(ServerMap *map,
311 GameScripting *scriptIface,
312 IGameDef *gamedef, IBackgroundBlockEmerger *emerger):
314 m_script(scriptIface),
317 m_random_spawn_timer(3),
318 m_send_recommended_timer(0),
319 m_active_block_interval_overload_skip(0),
321 m_game_time_fraction_counter(0),
322 m_recommended_send_interval(0.1),
323 m_max_lag_estimate(0.1)
325 m_use_weather = g_settings->getBool("weather");
328 ServerEnvironment::~ServerEnvironment()
330 // Clear active block list.
331 // This makes the next one delete all active objects.
332 m_active_blocks.clear();
334 // Convert all objects to static and delete the active objects
335 deactivateFarObjects(true);
340 // Delete ActiveBlockModifiers
341 for(std::list<ABMWithState>::iterator
342 i = m_abms.begin(); i != m_abms.end(); ++i){
347 Map & ServerEnvironment::getMap()
352 ServerMap & ServerEnvironment::getServerMap()
357 bool ServerEnvironment::line_of_sight(v3f pos1, v3f pos2, float stepsize, v3s16 *p)
359 float distance = pos1.getDistanceFrom(pos2);
361 //calculate normalized direction vector
362 v3f normalized_vector = v3f((pos2.X - pos1.X)/distance,
363 (pos2.Y - pos1.Y)/distance,
364 (pos2.Z - pos1.Z)/distance);
366 //find out if there's a node on path between pos1 and pos2
367 for (float i = 1; i < distance; i += stepsize) {
368 v3s16 pos = floatToInt(v3f(normalized_vector.X * i,
369 normalized_vector.Y * i,
370 normalized_vector.Z * i) +pos1,BS);
372 MapNode n = getMap().getNodeNoEx(pos);
374 if(n.param0 != CONTENT_AIR) {
384 void ServerEnvironment::serializePlayers(const std::string &savedir)
386 std::string players_path = savedir + "/players";
387 fs::CreateDir(players_path);
389 std::set<Player*> saved_players;
391 std::vector<fs::DirListNode> player_files = fs::GetDirListing(players_path);
392 for(u32 i=0; i<player_files.size(); i++)
394 if(player_files[i].dir || player_files[i].name[0] == '.')
397 // Full path to this file
398 std::string path = players_path + "/" + player_files[i].name;
400 //infostream<<"Checking player file "<<path<<std::endl;
402 // Load player to see what is its name
403 RemotePlayer testplayer(m_gamedef);
405 // Open file and deserialize
406 std::ifstream is(path.c_str(), std::ios_base::binary);
407 if(is.good() == false)
409 infostream<<"Failed to read "<<path<<std::endl;
412 testplayer.deSerialize(is, player_files[i].name);
415 //infostream<<"Loaded test player with name "<<testplayer.getName()<<std::endl;
417 // Search for the player
418 std::string playername = testplayer.getName();
419 Player *player = getPlayer(playername.c_str());
422 infostream<<"Didn't find matching player, ignoring file "<<path<<std::endl;
426 //infostream<<"Found matching player, overwriting."<<std::endl;
428 // OK, found. Save player there.
429 if(player->checkModified())
431 // Open file and serialize
432 std::ostringstream ss(std::ios_base::binary);
433 player->serialize(ss);
434 if(!fs::safeWriteToFile(path, ss.str()))
436 infostream<<"Failed to write "<<path<<std::endl;
439 saved_players.insert(player);
441 saved_players.insert(player);
445 for(std::list<Player*>::iterator i = m_players.begin();
446 i != m_players.end(); ++i)
449 if(saved_players.find(player) != saved_players.end())
451 /*infostream<<"Player "<<player->getName()
452 <<" was already saved."<<std::endl;*/
455 std::string playername = player->getName();
456 // Don't save unnamed player
459 //infostream<<"Not saving unnamed player."<<std::endl;
465 if(string_allowed(playername, PLAYERNAME_ALLOWED_CHARS) == false)
466 playername = "player";
467 std::string path = players_path + "/" + playername;
469 for(u32 i=0; i<1000; i++)
471 if(fs::PathExists(path) == false)
476 path = players_path + "/" + playername + itos(i);
480 infostream<<"Didn't find free file for player"<<std::endl;
485 /*infostream<<"Saving player "<<player->getName()<<" to "
487 // Open file and serialize
488 std::ostringstream ss(std::ios_base::binary);
489 player->serialize(ss);
490 if(!fs::safeWriteToFile(path, ss.str()))
492 infostream<<"Failed to write "<<path<<std::endl;
495 saved_players.insert(player);
499 //infostream<<"Saved "<<saved_players.size()<<" players."<<std::endl;
502 void ServerEnvironment::deSerializePlayers(const std::string &savedir)
504 std::string players_path = savedir + "/players";
506 std::vector<fs::DirListNode> player_files = fs::GetDirListing(players_path);
507 for(u32 i=0; i<player_files.size(); i++)
509 if(player_files[i].dir)
512 // Full path to this file
513 std::string path = players_path + "/" + player_files[i].name;
515 //infostream<<"Checking player file "<<path<<std::endl;
517 // Load player to see what is its name
518 RemotePlayer testplayer(m_gamedef);
520 // Open file and deserialize
521 std::ifstream is(path.c_str(), std::ios_base::binary);
522 if(is.good() == false)
524 infostream<<"Failed to read "<<path<<std::endl;
527 testplayer.deSerialize(is, player_files[i].name);
530 if(!string_allowed(testplayer.getName(), PLAYERNAME_ALLOWED_CHARS))
532 infostream<<"Not loading player with invalid name: "
533 <<testplayer.getName()<<std::endl;
536 /*infostream<<"Loaded test player with name "<<testplayer.getName()
539 // Search for the player
540 std::string playername = testplayer.getName();
541 Player *player = getPlayer(playername.c_str());
542 bool newplayer = false;
545 //infostream<<"Is a new player"<<std::endl;
546 player = new RemotePlayer(m_gamedef);
552 verbosestream<<"Reading player "<<testplayer.getName()<<" from "
554 // Open file and deserialize
555 std::ifstream is(path.c_str(), std::ios_base::binary);
556 if(is.good() == false)
558 infostream<<"Failed to read "<<path<<std::endl;
561 player->deSerialize(is, player_files[i].name);
571 void ServerEnvironment::saveMeta(const std::string &savedir)
573 std::string path = savedir + "/env_meta.txt";
575 // Open file and serialize
576 std::ostringstream ss(std::ios_base::binary);
579 args.setU64("game_time", m_game_time);
580 args.setU64("time_of_day", getTimeOfDay());
584 if(!fs::safeWriteToFile(path, ss.str()))
586 infostream<<"ServerEnvironment::saveMeta(): Failed to write "
588 throw SerializationError("Couldn't save env meta");
592 void ServerEnvironment::loadMeta(const std::string &savedir)
594 std::string path = savedir + "/env_meta.txt";
596 // Open file and deserialize
597 std::ifstream is(path.c_str(), std::ios_base::binary);
598 if(is.good() == false)
600 infostream<<"ServerEnvironment::loadMeta(): Failed to open "
602 throw SerializationError("Couldn't load env meta");
610 throw SerializationError
611 ("ServerEnvironment::loadMeta(): EnvArgsEnd not found");
613 std::getline(is, line);
614 std::string trimmedline = trim(line);
615 if(trimmedline == "EnvArgsEnd")
617 args.parseConfigLine(line);
621 m_game_time = args.getU64("game_time");
622 }catch(SettingNotFoundException &e){
623 // Getting this is crucial, otherwise timestamps are useless
624 throw SerializationError("Couldn't load env meta game_time");
628 m_time_of_day = args.getU64("time_of_day");
629 }catch(SettingNotFoundException &e){
630 // This is not as important
631 m_time_of_day = 9000;
637 ActiveBlockModifier *abm;
639 std::set<content_t> required_neighbors;
645 ServerEnvironment *m_env;
646 std::map<content_t, std::list<ActiveABM> > m_aabms;
648 ABMHandler(std::list<ABMWithState> &abms,
649 float dtime_s, ServerEnvironment *env,
655 INodeDefManager *ndef = env->getGameDef()->ndef();
656 for(std::list<ABMWithState>::iterator
657 i = abms.begin(); i != abms.end(); ++i){
658 ActiveBlockModifier *abm = i->abm;
659 float trigger_interval = abm->getTriggerInterval();
660 if(trigger_interval < 0.001)
661 trigger_interval = 0.001;
662 float actual_interval = dtime_s;
665 if(i->timer < trigger_interval)
667 i->timer -= trigger_interval;
668 actual_interval = trigger_interval;
670 float intervals = actual_interval / trigger_interval;
673 float chance = abm->getTriggerChance();
678 aabm.chance = chance / intervals;
682 std::set<std::string> required_neighbors_s
683 = abm->getRequiredNeighbors();
684 for(std::set<std::string>::iterator
685 i = required_neighbors_s.begin();
686 i != required_neighbors_s.end(); i++)
688 ndef->getIds(*i, aabm.required_neighbors);
691 std::set<std::string> contents_s = abm->getTriggerContents();
692 for(std::set<std::string>::iterator
693 i = contents_s.begin(); i != contents_s.end(); i++)
695 std::set<content_t> ids;
696 ndef->getIds(*i, ids);
697 for(std::set<content_t>::const_iterator k = ids.begin();
701 std::map<content_t, std::list<ActiveABM> >::iterator j;
703 if(j == m_aabms.end()){
704 std::list<ActiveABM> aabmlist;
705 m_aabms[c] = aabmlist;
708 j->second.push_back(aabm);
713 void apply(MapBlock *block)
718 ServerMap *map = &m_env->getServerMap();
721 for(p0.X=0; p0.X<MAP_BLOCKSIZE; p0.X++)
722 for(p0.Y=0; p0.Y<MAP_BLOCKSIZE; p0.Y++)
723 for(p0.Z=0; p0.Z<MAP_BLOCKSIZE; p0.Z++)
725 MapNode n = block->getNodeNoEx(p0);
726 content_t c = n.getContent();
727 v3s16 p = p0 + block->getPosRelative();
729 std::map<content_t, std::list<ActiveABM> >::iterator j;
731 if(j == m_aabms.end())
734 for(std::list<ActiveABM>::iterator
735 i = j->second.begin(); i != j->second.end(); i++)
737 if(myrand() % i->chance != 0)
741 if(!i->required_neighbors.empty())
744 for(p1.X = p.X-1; p1.X <= p.X+1; p1.X++)
745 for(p1.Y = p.Y-1; p1.Y <= p.Y+1; p1.Y++)
746 for(p1.Z = p.Z-1; p1.Z <= p.Z+1; p1.Z++)
750 MapNode n = map->getNodeNoEx(p1);
751 content_t c = n.getContent();
752 std::set<content_t>::const_iterator k;
753 k = i->required_neighbors.find(c);
754 if(k != i->required_neighbors.end()){
758 // No required neighbor found
763 // Find out how many objects the block contains
764 u32 active_object_count = block->m_static_objects.m_active.size();
765 // Find out how many objects this and all the neighbors contain
766 u32 active_object_count_wider = 0;
767 u32 wider_unknown_count = 0;
768 for(s16 x=-1; x<=1; x++)
769 for(s16 y=-1; y<=1; y++)
770 for(s16 z=-1; z<=1; z++)
772 MapBlock *block2 = map->getBlockNoCreateNoEx(
773 block->getPos() + v3s16(x,y,z));
775 wider_unknown_count = 0;
778 active_object_count_wider +=
779 block2->m_static_objects.m_active.size()
780 + block2->m_static_objects.m_stored.size();
783 u32 wider_known_count = 3*3*3 - wider_unknown_count;
784 active_object_count_wider += wider_unknown_count * active_object_count_wider / wider_known_count;
786 // Call all the trigger variations
787 i->abm->trigger(m_env, p, n);
788 i->abm->trigger(m_env, p, n,
789 active_object_count, active_object_count_wider);
795 void ServerEnvironment::activateBlock(MapBlock *block, u32 additional_dtime)
797 // Get time difference
799 u32 stamp = block->getTimestamp();
800 if(m_game_time > stamp && stamp != BLOCK_TIMESTAMP_UNDEFINED)
801 dtime_s = m_game_time - block->getTimestamp();
802 dtime_s += additional_dtime;
804 /*infostream<<"ServerEnvironment::activateBlock(): block timestamp: "
805 <<stamp<<", game time: "<<m_game_time<<std::endl;*/
807 // Set current time as timestamp
808 block->setTimestampNoChangedFlag(m_game_time);
810 /*infostream<<"ServerEnvironment::activateBlock(): block is "
811 <<dtime_s<<" seconds old."<<std::endl;*/
813 // Activate stored objects
814 activateObjects(block, dtime_s);
817 std::map<v3s16, NodeTimer> elapsed_timers =
818 block->m_node_timers.step((float)dtime_s);
819 if(!elapsed_timers.empty()){
821 for(std::map<v3s16, NodeTimer>::iterator
822 i = elapsed_timers.begin();
823 i != elapsed_timers.end(); i++){
824 n = block->getNodeNoEx(i->first);
825 v3s16 p = i->first + block->getPosRelative();
826 if(m_script->node_on_timer(p,n,i->second.elapsed))
827 block->setNodeTimer(i->first,NodeTimer(i->second.timeout,0));
831 /* Handle ActiveBlockModifiers */
832 ABMHandler abmhandler(m_abms, dtime_s, this, false);
833 abmhandler.apply(block);
836 void ServerEnvironment::addActiveBlockModifier(ActiveBlockModifier *abm)
838 m_abms.push_back(ABMWithState(abm));
841 bool ServerEnvironment::setNode(v3s16 p, const MapNode &n)
843 INodeDefManager *ndef = m_gamedef->ndef();
844 MapNode n_old = m_map->getNodeNoEx(p);
846 if(ndef->get(n_old).has_on_destruct)
847 m_script->node_on_destruct(p, n_old);
849 bool succeeded = m_map->addNodeWithEvent(p, n);
852 // Call post-destructor
853 if(ndef->get(n_old).has_after_destruct)
854 m_script->node_after_destruct(p, n_old);
856 if(ndef->get(n).has_on_construct)
857 m_script->node_on_construct(p, n);
861 bool ServerEnvironment::removeNode(v3s16 p)
863 INodeDefManager *ndef = m_gamedef->ndef();
864 MapNode n_old = m_map->getNodeNoEx(p);
866 if(ndef->get(n_old).has_on_destruct)
867 m_script->node_on_destruct(p, n_old);
869 // This is slightly optimized compared to addNodeWithEvent(air)
870 bool succeeded = m_map->removeNodeWithEvent(p);
873 // Call post-destructor
874 if(ndef->get(n_old).has_after_destruct)
875 m_script->node_after_destruct(p, n_old);
876 // Air doesn't require constructor
880 bool ServerEnvironment::swapNode(v3s16 p, const MapNode &n)
882 return m_map->addNodeWithEvent(p, n, false);
885 std::set<u16> ServerEnvironment::getObjectsInsideRadius(v3f pos, float radius)
887 std::set<u16> objects;
888 for(std::map<u16, ServerActiveObject*>::iterator
889 i = m_active_objects.begin();
890 i != m_active_objects.end(); ++i)
892 ServerActiveObject* obj = i->second;
894 v3f objectpos = obj->getBasePosition();
895 if(objectpos.getDistanceFrom(pos) > radius)
902 void ServerEnvironment::clearAllObjects()
904 infostream<<"ServerEnvironment::clearAllObjects(): "
905 <<"Removing all active objects"<<std::endl;
906 std::list<u16> objects_to_remove;
907 for(std::map<u16, ServerActiveObject*>::iterator
908 i = m_active_objects.begin();
909 i != m_active_objects.end(); ++i)
911 ServerActiveObject* obj = i->second;
912 if(obj->getType() == ACTIVEOBJECT_TYPE_PLAYER)
915 // Delete static object if block is loaded
916 if(obj->m_static_exists){
917 MapBlock *block = m_map->getBlockNoCreateNoEx(obj->m_static_block);
919 block->m_static_objects.remove(id);
920 block->raiseModified(MOD_STATE_WRITE_NEEDED,
922 obj->m_static_exists = false;
925 // If known by some client, don't delete immediately
926 if(obj->m_known_by_count > 0){
927 obj->m_pending_deactivation = true;
928 obj->m_removed = true;
932 // Tell the object about removal
933 obj->removingFromEnvironment();
934 // Deregister in scripting api
935 m_script->removeObjectReference(obj);
937 // Delete active object
938 if(obj->environmentDeletes())
940 // Id to be removed from m_active_objects
941 objects_to_remove.push_back(id);
943 // Remove references from m_active_objects
944 for(std::list<u16>::iterator i = objects_to_remove.begin();
945 i != objects_to_remove.end(); ++i)
947 m_active_objects.erase(*i);
950 // Get list of loaded blocks
951 std::list<v3s16> loaded_blocks;
952 infostream<<"ServerEnvironment::clearAllObjects(): "
953 <<"Listing all loaded blocks"<<std::endl;
954 m_map->listAllLoadedBlocks(loaded_blocks);
955 infostream<<"ServerEnvironment::clearAllObjects(): "
956 <<"Done listing all loaded blocks: "
957 <<loaded_blocks.size()<<std::endl;
959 // Get list of loadable blocks
960 std::list<v3s16> loadable_blocks;
961 infostream<<"ServerEnvironment::clearAllObjects(): "
962 <<"Listing all loadable blocks"<<std::endl;
963 m_map->listAllLoadableBlocks(loadable_blocks);
964 infostream<<"ServerEnvironment::clearAllObjects(): "
965 <<"Done listing all loadable blocks: "
966 <<loadable_blocks.size()
967 <<", now clearing"<<std::endl;
969 // Grab a reference on each loaded block to avoid unloading it
970 for(std::list<v3s16>::iterator i = loaded_blocks.begin();
971 i != loaded_blocks.end(); ++i)
974 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
979 // Remove objects in all loadable blocks
980 u32 unload_interval = g_settings->getS32("max_clearobjects_extra_loaded_blocks");
981 unload_interval = MYMAX(unload_interval, 1);
982 u32 report_interval = loadable_blocks.size() / 10;
983 u32 num_blocks_checked = 0;
984 u32 num_blocks_cleared = 0;
985 u32 num_objs_cleared = 0;
986 for(std::list<v3s16>::iterator i = loadable_blocks.begin();
987 i != loadable_blocks.end(); ++i)
990 MapBlock *block = m_map->emergeBlock(p, false);
992 errorstream<<"ServerEnvironment::clearAllObjects(): "
993 <<"Failed to emerge block "<<PP(p)<<std::endl;
996 u32 num_stored = block->m_static_objects.m_stored.size();
997 u32 num_active = block->m_static_objects.m_active.size();
998 if(num_stored != 0 || num_active != 0){
999 block->m_static_objects.m_stored.clear();
1000 block->m_static_objects.m_active.clear();
1001 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1003 num_objs_cleared += num_stored + num_active;
1004 num_blocks_cleared++;
1006 num_blocks_checked++;
1008 if(num_blocks_checked % report_interval == 0){
1009 float percent = 100.0 * (float)num_blocks_checked /
1010 loadable_blocks.size();
1011 infostream<<"ServerEnvironment::clearAllObjects(): "
1012 <<"Cleared "<<num_objs_cleared<<" objects"
1013 <<" in "<<num_blocks_cleared<<" blocks ("
1014 <<percent<<"%)"<<std::endl;
1016 if(num_blocks_checked % unload_interval == 0){
1017 m_map->unloadUnreferencedBlocks();
1020 m_map->unloadUnreferencedBlocks();
1022 // Drop references that were added above
1023 for(std::list<v3s16>::iterator i = loaded_blocks.begin();
1024 i != loaded_blocks.end(); ++i)
1027 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1032 infostream<<"ServerEnvironment::clearAllObjects(): "
1033 <<"Finished: Cleared "<<num_objs_cleared<<" objects"
1034 <<" in "<<num_blocks_cleared<<" blocks"<<std::endl;
1037 void ServerEnvironment::step(float dtime)
1039 DSTACK(__FUNCTION_NAME);
1041 //TimeTaker timer("ServerEnv step");
1043 /* Step time of day */
1044 stepTimeOfDay(dtime);
1047 // NOTE: This is kind of funny on a singleplayer game, but doesn't
1048 // really matter that much.
1049 m_recommended_send_interval = g_settings->getFloat("dedicated_server_step");
1055 m_game_time_fraction_counter += dtime;
1056 u32 inc_i = (u32)m_game_time_fraction_counter;
1057 m_game_time += inc_i;
1058 m_game_time_fraction_counter -= (float)inc_i;
1065 ScopeProfiler sp(g_profiler, "SEnv: handle players avg", SPT_AVG);
1066 for(std::list<Player*>::iterator i = m_players.begin();
1067 i != m_players.end(); ++i)
1069 Player *player = *i;
1071 // Ignore disconnected players
1072 if(player->peer_id == 0)
1076 player->move(dtime, *m_map, 100*BS);
1081 Manage active block list
1083 if(m_active_blocks_management_interval.step(dtime, 2.0))
1085 ScopeProfiler sp(g_profiler, "SEnv: manage act. block list avg /2s", SPT_AVG);
1087 Get player block positions
1089 std::list<v3s16> players_blockpos;
1090 for(std::list<Player*>::iterator
1091 i = m_players.begin();
1092 i != m_players.end(); ++i)
1094 Player *player = *i;
1095 // Ignore disconnected players
1096 if(player->peer_id == 0)
1098 v3s16 blockpos = getNodeBlockPos(
1099 floatToInt(player->getPosition(), BS));
1100 players_blockpos.push_back(blockpos);
1104 Update list of active blocks, collecting changes
1106 const s16 active_block_range = g_settings->getS16("active_block_range");
1107 std::set<v3s16> blocks_removed;
1108 std::set<v3s16> blocks_added;
1109 m_active_blocks.update(players_blockpos, active_block_range,
1110 blocks_removed, blocks_added);
1113 Handle removed blocks
1116 // Convert active objects that are no more in active blocks to static
1117 deactivateFarObjects(false);
1119 for(std::set<v3s16>::iterator
1120 i = blocks_removed.begin();
1121 i != blocks_removed.end(); ++i)
1125 /* infostream<<"Server: Block " << PP(p)
1126 << " became inactive"<<std::endl; */
1128 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1132 // Set current time as timestamp (and let it set ChangedFlag)
1133 block->setTimestamp(m_game_time);
1140 for(std::set<v3s16>::iterator
1141 i = blocks_added.begin();
1142 i != blocks_added.end(); ++i)
1146 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1148 // Block needs to be fetched first
1149 m_emerger->enqueueBlockEmerge(
1150 PEER_ID_INEXISTENT, p, false);
1151 m_active_blocks.m_list.erase(p);
1155 activateBlock(block);
1156 /* infostream<<"Server: Block " << PP(p)
1157 << " became active"<<std::endl; */
1162 Mess around in active blocks
1164 if(m_active_blocks_nodemetadata_interval.step(dtime, 1.0))
1166 ScopeProfiler sp(g_profiler, "SEnv: mess in act. blocks avg /1s", SPT_AVG);
1170 for(std::set<v3s16>::iterator
1171 i = m_active_blocks.m_list.begin();
1172 i != m_active_blocks.m_list.end(); ++i)
1176 /*infostream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
1177 <<") being handled"<<std::endl;*/
1179 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1183 // Reset block usage timer
1184 block->resetUsageTimer();
1186 // Set current time as timestamp
1187 block->setTimestampNoChangedFlag(m_game_time);
1188 // If time has changed much from the one on disk,
1189 // set block to be saved when it is unloaded
1190 if(block->getTimestamp() > block->getDiskTimestamp() + 60)
1191 block->raiseModified(MOD_STATE_WRITE_AT_UNLOAD,
1192 "Timestamp older than 60s (step)");
1195 std::map<v3s16, NodeTimer> elapsed_timers =
1196 block->m_node_timers.step((float)dtime);
1197 if(!elapsed_timers.empty()){
1199 for(std::map<v3s16, NodeTimer>::iterator
1200 i = elapsed_timers.begin();
1201 i != elapsed_timers.end(); i++){
1202 n = block->getNodeNoEx(i->first);
1203 p = i->first + block->getPosRelative();
1204 if(m_script->node_on_timer(p,n,i->second.elapsed))
1205 block->setNodeTimer(i->first,NodeTimer(i->second.timeout,0));
1211 const float abm_interval = 1.0;
1212 if(m_active_block_modifier_interval.step(dtime, abm_interval))
1214 if(m_active_block_interval_overload_skip > 0){
1215 ScopeProfiler sp(g_profiler, "SEnv: ABM overload skips");
1216 m_active_block_interval_overload_skip--;
1219 ScopeProfiler sp(g_profiler, "SEnv: modify in blocks avg /1s", SPT_AVG);
1220 TimeTaker timer("modify in active blocks");
1222 // Initialize handling of ActiveBlockModifiers
1223 ABMHandler abmhandler(m_abms, abm_interval, this, true);
1225 for(std::set<v3s16>::iterator
1226 i = m_active_blocks.m_list.begin();
1227 i != m_active_blocks.m_list.end(); ++i)
1231 /*infostream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
1232 <<") being handled"<<std::endl;*/
1234 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1238 // Set current time as timestamp
1239 block->setTimestampNoChangedFlag(m_game_time);
1241 /* Handle ActiveBlockModifiers */
1242 abmhandler.apply(block);
1245 u32 time_ms = timer.stop(true);
1246 u32 max_time_ms = 200;
1247 if(time_ms > max_time_ms){
1248 infostream<<"WARNING: active block modifiers took "
1249 <<time_ms<<"ms (longer than "
1250 <<max_time_ms<<"ms)"<<std::endl;
1251 m_active_block_interval_overload_skip = (time_ms / max_time_ms) + 1;
1256 Step script environment (run global on_step())
1258 m_script->environment_Step(dtime);
1264 ScopeProfiler sp(g_profiler, "SEnv: step act. objs avg", SPT_AVG);
1265 //TimeTaker timer("Step active objects");
1267 g_profiler->avg("SEnv: num of objects", m_active_objects.size());
1269 // This helps the objects to send data at the same time
1270 bool send_recommended = false;
1271 m_send_recommended_timer += dtime;
1272 if(m_send_recommended_timer > getSendRecommendedInterval())
1274 m_send_recommended_timer -= getSendRecommendedInterval();
1275 send_recommended = true;
1278 for(std::map<u16, ServerActiveObject*>::iterator
1279 i = m_active_objects.begin();
1280 i != m_active_objects.end(); ++i)
1282 ServerActiveObject* obj = i->second;
1283 // Remove non-peaceful mobs on peaceful mode
1284 if(g_settings->getBool("only_peaceful_mobs")){
1285 if(!obj->isPeaceful())
1286 obj->m_removed = true;
1288 // Don't step if is to be removed or stored statically
1289 if(obj->m_removed || obj->m_pending_deactivation)
1292 obj->step(dtime, send_recommended);
1293 // Read messages from object
1294 while(!obj->m_messages_out.empty())
1296 m_active_object_messages.push_back(
1297 obj->m_messages_out.pop_front());
1303 Manage active objects
1305 if(m_object_management_interval.step(dtime, 0.5))
1307 ScopeProfiler sp(g_profiler, "SEnv: remove removed objs avg /.5s", SPT_AVG);
1309 Remove objects that satisfy (m_removed && m_known_by_count==0)
1311 removeRemovedObjects();
1315 ServerActiveObject* ServerEnvironment::getActiveObject(u16 id)
1317 std::map<u16, ServerActiveObject*>::iterator n;
1318 n = m_active_objects.find(id);
1319 if(n == m_active_objects.end())
1324 bool isFreeServerActiveObjectId(u16 id,
1325 std::map<u16, ServerActiveObject*> &objects)
1330 return objects.find(id) == objects.end();
1333 u16 getFreeServerActiveObjectId(
1334 std::map<u16, ServerActiveObject*> &objects)
1336 //try to reuse id's as late as possible
1337 static u16 last_used_id = 0;
1338 u16 startid = last_used_id;
1342 if(isFreeServerActiveObjectId(last_used_id, objects))
1343 return last_used_id;
1345 if(last_used_id == startid)
1350 u16 ServerEnvironment::addActiveObject(ServerActiveObject *object)
1353 u16 id = addActiveObjectRaw(object, true, 0);
1358 bool ServerEnvironment::addActiveObjectAsStatic(ServerActiveObject *obj)
1362 v3f objectpos = obj->getBasePosition();
1364 // The block in which the object resides in
1365 v3s16 blockpos_o = getNodeBlockPos(floatToInt(objectpos, BS));
1368 Update the static data
1371 // Create new static object
1372 std::string staticdata = obj->getStaticData();
1373 StaticObject s_obj(obj->getType(), objectpos, staticdata);
1374 // Add to the block where the object is located in
1375 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1376 // Get or generate the block
1377 MapBlock *block = m_map->emergeBlock(blockpos);
1379 bool succeeded = false;
1383 block->m_static_objects.insert(0, s_obj);
1384 block->raiseModified(MOD_STATE_WRITE_AT_UNLOAD,
1385 "addActiveObjectAsStatic");
1389 infostream<<"ServerEnvironment::addActiveObjectAsStatic: "
1390 <<"Could not find or generate "
1391 <<"a block for storing static object"<<std::endl;
1395 if(obj->environmentDeletes())
1403 Finds out what new objects have been added to
1404 inside a radius around a position
1406 void ServerEnvironment::getAddedActiveObjects(v3s16 pos, s16 radius,
1407 std::set<u16> ¤t_objects,
1408 std::set<u16> &added_objects)
1410 v3f pos_f = intToFloat(pos, BS);
1411 f32 radius_f = radius * BS;
1413 Go through the object list,
1414 - discard m_removed objects,
1415 - discard objects that are too far away,
1416 - discard objects that are found in current_objects.
1417 - add remaining objects to added_objects
1419 for(std::map<u16, ServerActiveObject*>::iterator
1420 i = m_active_objects.begin();
1421 i != m_active_objects.end(); ++i)
1425 ServerActiveObject *object = i->second;
1428 // Discard if removed or deactivating
1429 if(object->m_removed || object->m_pending_deactivation)
1431 if(object->unlimitedTransferDistance() == false){
1432 // Discard if too far
1433 f32 distance_f = object->getBasePosition().getDistanceFrom(pos_f);
1434 if(distance_f > radius_f)
1437 // Discard if already on current_objects
1438 std::set<u16>::iterator n;
1439 n = current_objects.find(id);
1440 if(n != current_objects.end())
1442 // Add to added_objects
1443 added_objects.insert(id);
1448 Finds out what objects have been removed from
1449 inside a radius around a position
1451 void ServerEnvironment::getRemovedActiveObjects(v3s16 pos, s16 radius,
1452 std::set<u16> ¤t_objects,
1453 std::set<u16> &removed_objects)
1455 v3f pos_f = intToFloat(pos, BS);
1456 f32 radius_f = radius * BS;
1458 Go through current_objects; object is removed if:
1459 - object is not found in m_active_objects (this is actually an
1460 error condition; objects should be set m_removed=true and removed
1461 only after all clients have been informed about removal), or
1462 - object has m_removed=true, or
1463 - object is too far away
1465 for(std::set<u16>::iterator
1466 i = current_objects.begin();
1467 i != current_objects.end(); ++i)
1470 ServerActiveObject *object = getActiveObject(id);
1473 infostream<<"ServerEnvironment::getRemovedActiveObjects():"
1474 <<" object in current_objects is NULL"<<std::endl;
1475 removed_objects.insert(id);
1479 if(object->m_removed || object->m_pending_deactivation)
1481 removed_objects.insert(id);
1485 // If transfer distance is unlimited, don't remove
1486 if(object->unlimitedTransferDistance())
1489 f32 distance_f = object->getBasePosition().getDistanceFrom(pos_f);
1491 if(distance_f >= radius_f)
1493 removed_objects.insert(id);
1501 ActiveObjectMessage ServerEnvironment::getActiveObjectMessage()
1503 if(m_active_object_messages.empty())
1504 return ActiveObjectMessage(0);
1506 ActiveObjectMessage message = m_active_object_messages.front();
1507 m_active_object_messages.pop_front();
1512 ************ Private methods *************
1515 u16 ServerEnvironment::addActiveObjectRaw(ServerActiveObject *object,
1516 bool set_changed, u32 dtime_s)
1519 if(object->getId() == 0){
1520 u16 new_id = getFreeServerActiveObjectId(m_active_objects);
1523 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1524 <<"no free ids available"<<std::endl;
1525 if(object->environmentDeletes())
1529 object->setId(new_id);
1532 verbosestream<<"ServerEnvironment::addActiveObjectRaw(): "
1533 <<"supplied with id "<<object->getId()<<std::endl;
1535 if(isFreeServerActiveObjectId(object->getId(), m_active_objects) == false)
1537 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1538 <<"id is not free ("<<object->getId()<<")"<<std::endl;
1539 if(object->environmentDeletes())
1543 /*infostream<<"ServerEnvironment::addActiveObjectRaw(): "
1544 <<"added (id="<<object->getId()<<")"<<std::endl;*/
1546 m_active_objects[object->getId()] = object;
1548 verbosestream<<"ServerEnvironment::addActiveObjectRaw(): "
1549 <<"Added id="<<object->getId()<<"; there are now "
1550 <<m_active_objects.size()<<" active objects."
1553 // Register reference in scripting api (must be done before post-init)
1554 m_script->addObjectReference(object);
1555 // Post-initialize object
1556 object->addedToEnvironment(dtime_s);
1558 // Add static data to block
1559 if(object->isStaticAllowed())
1561 // Add static object to active static list of the block
1562 v3f objectpos = object->getBasePosition();
1563 std::string staticdata = object->getStaticData();
1564 StaticObject s_obj(object->getType(), objectpos, staticdata);
1565 // Add to the block where the object is located in
1566 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1567 MapBlock *block = m_map->emergeBlock(blockpos);
1569 block->m_static_objects.m_active[object->getId()] = s_obj;
1570 object->m_static_exists = true;
1571 object->m_static_block = blockpos;
1574 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1575 "addActiveObjectRaw");
1577 v3s16 p = floatToInt(objectpos, BS);
1578 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1579 <<"could not emerge block for storing id="<<object->getId()
1580 <<" statically (pos="<<PP(p)<<")"<<std::endl;
1584 return object->getId();
1588 Remove objects that satisfy (m_removed && m_known_by_count==0)
1590 void ServerEnvironment::removeRemovedObjects()
1592 std::list<u16> objects_to_remove;
1593 for(std::map<u16, ServerActiveObject*>::iterator
1594 i = m_active_objects.begin();
1595 i != m_active_objects.end(); ++i)
1598 ServerActiveObject* obj = i->second;
1599 // This shouldn't happen but check it
1602 infostream<<"NULL object found in ServerEnvironment"
1603 <<" while finding removed objects. id="<<id<<std::endl;
1604 // Id to be removed from m_active_objects
1605 objects_to_remove.push_back(id);
1610 We will delete objects that are marked as removed or thatare
1611 waiting for deletion after deactivation
1613 if(obj->m_removed == false && obj->m_pending_deactivation == false)
1617 Delete static data from block if is marked as removed
1619 if(obj->m_static_exists && obj->m_removed)
1621 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
1623 block->m_static_objects.remove(id);
1624 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1625 "removeRemovedObjects/remove");
1626 obj->m_static_exists = false;
1628 infostream<<"Failed to emerge block from which an object to "
1629 <<"be removed was loaded from. id="<<id<<std::endl;
1633 // If m_known_by_count > 0, don't actually remove. On some future
1634 // invocation this will be 0, which is when removal will continue.
1635 if(obj->m_known_by_count > 0)
1639 Move static data from active to stored if not marked as removed
1641 if(obj->m_static_exists && !obj->m_removed){
1642 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
1644 std::map<u16, StaticObject>::iterator i =
1645 block->m_static_objects.m_active.find(id);
1646 if(i != block->m_static_objects.m_active.end()){
1647 block->m_static_objects.m_stored.push_back(i->second);
1648 block->m_static_objects.m_active.erase(id);
1649 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1650 "removeRemovedObjects/deactivate");
1653 infostream<<"Failed to emerge block from which an object to "
1654 <<"be deactivated was loaded from. id="<<id<<std::endl;
1658 // Tell the object about removal
1659 obj->removingFromEnvironment();
1660 // Deregister in scripting api
1661 m_script->removeObjectReference(obj);
1664 if(obj->environmentDeletes())
1666 // Id to be removed from m_active_objects
1667 objects_to_remove.push_back(id);
1669 // Remove references from m_active_objects
1670 for(std::list<u16>::iterator i = objects_to_remove.begin();
1671 i != objects_to_remove.end(); ++i)
1673 m_active_objects.erase(*i);
1677 static void print_hexdump(std::ostream &o, const std::string &data)
1679 const int linelength = 16;
1680 for(int l=0; ; l++){
1681 int i0 = linelength * l;
1682 bool at_end = false;
1683 int thislinelength = linelength;
1684 if(i0 + thislinelength > (int)data.size()){
1685 thislinelength = data.size() - i0;
1688 for(int di=0; di<linelength; di++){
1691 if(di<thislinelength)
1692 snprintf(buf, 4, "%.2x ", data[i]);
1694 snprintf(buf, 4, " ");
1698 for(int di=0; di<thislinelength; di++){
1712 Convert stored objects from blocks near the players to active.
1714 void ServerEnvironment::activateObjects(MapBlock *block, u32 dtime_s)
1718 // Ignore if no stored objects (to not set changed flag)
1719 if(block->m_static_objects.m_stored.size() == 0)
1721 verbosestream<<"ServerEnvironment::activateObjects(): "
1722 <<"activating objects of block "<<PP(block->getPos())
1723 <<" ("<<block->m_static_objects.m_stored.size()
1724 <<" objects)"<<std::endl;
1725 bool large_amount = (block->m_static_objects.m_stored.size() > g_settings->getU16("max_objects_per_block"));
1727 errorstream<<"suspiciously large amount of objects detected: "
1728 <<block->m_static_objects.m_stored.size()<<" in "
1729 <<PP(block->getPos())
1730 <<"; removing all of them."<<std::endl;
1731 // Clear stored list
1732 block->m_static_objects.m_stored.clear();
1733 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1734 "stored list cleared in activateObjects due to "
1735 "large amount of objects");
1739 // Activate stored objects
1740 std::list<StaticObject> new_stored;
1741 for(std::list<StaticObject>::iterator
1742 i = block->m_static_objects.m_stored.begin();
1743 i != block->m_static_objects.m_stored.end(); ++i)
1745 /*infostream<<"Server: Creating an active object from "
1746 <<"static data"<<std::endl;*/
1747 StaticObject &s_obj = *i;
1748 // Create an active object from the data
1749 ServerActiveObject *obj = ServerActiveObject::create
1750 (s_obj.type, this, 0, s_obj.pos, s_obj.data);
1751 // If couldn't create object, store static data back.
1754 errorstream<<"ServerEnvironment::activateObjects(): "
1755 <<"failed to create active object from static object "
1756 <<"in block "<<PP(s_obj.pos/BS)
1757 <<" type="<<(int)s_obj.type<<" data:"<<std::endl;
1758 print_hexdump(verbosestream, s_obj.data);
1760 new_stored.push_back(s_obj);
1763 verbosestream<<"ServerEnvironment::activateObjects(): "
1764 <<"activated static object pos="<<PP(s_obj.pos/BS)
1765 <<" type="<<(int)s_obj.type<<std::endl;
1766 // This will also add the object to the active static list
1767 addActiveObjectRaw(obj, false, dtime_s);
1769 // Clear stored list
1770 block->m_static_objects.m_stored.clear();
1771 // Add leftover failed stuff to stored list
1772 for(std::list<StaticObject>::iterator
1773 i = new_stored.begin();
1774 i != new_stored.end(); ++i)
1776 StaticObject &s_obj = *i;
1777 block->m_static_objects.m_stored.push_back(s_obj);
1780 // Turn the active counterparts of activated objects not pending for
1782 for(std::map<u16, StaticObject>::iterator
1783 i = block->m_static_objects.m_active.begin();
1784 i != block->m_static_objects.m_active.end(); ++i)
1787 ServerActiveObject *object = getActiveObject(id);
1789 object->m_pending_deactivation = false;
1793 Note: Block hasn't really been modified here.
1794 The objects have just been activated and moved from the stored
1795 static list to the active static list.
1796 As such, the block is essentially the same.
1797 Thus, do not call block->raiseModified(MOD_STATE_WRITE_NEEDED).
1798 Otherwise there would be a huge amount of unnecessary I/O.
1803 Convert objects that are not standing inside active blocks to static.
1805 If m_known_by_count != 0, active object is not deleted, but static
1806 data is still updated.
1808 If force_delete is set, active object is deleted nevertheless. It
1809 shall only be set so in the destructor of the environment.
1811 If block wasn't generated (not in memory or on disk),
1813 void ServerEnvironment::deactivateFarObjects(bool force_delete)
1815 std::list<u16> objects_to_remove;
1816 for(std::map<u16, ServerActiveObject*>::iterator
1817 i = m_active_objects.begin();
1818 i != m_active_objects.end(); ++i)
1820 ServerActiveObject* obj = i->second;
1823 // Do not deactivate if static data creation not allowed
1824 if(!force_delete && !obj->isStaticAllowed())
1827 // If pending deactivation, let removeRemovedObjects() do it
1828 if(!force_delete && obj->m_pending_deactivation)
1832 v3f objectpos = obj->getBasePosition();
1834 // The block in which the object resides in
1835 v3s16 blockpos_o = getNodeBlockPos(floatToInt(objectpos, BS));
1837 // If object's static data is stored in a deactivated block and object
1838 // is actually located in an active block, re-save to the block in
1839 // which the object is actually located in.
1841 obj->m_static_exists &&
1842 !m_active_blocks.contains(obj->m_static_block) &&
1843 m_active_blocks.contains(blockpos_o))
1845 v3s16 old_static_block = obj->m_static_block;
1847 // Save to block where object is located
1848 MapBlock *block = m_map->emergeBlock(blockpos_o, false);
1850 errorstream<<"ServerEnvironment::deactivateFarObjects(): "
1851 <<"Could not save object id="<<id
1852 <<" to it's current block "<<PP(blockpos_o)
1856 std::string staticdata_new = obj->getStaticData();
1857 StaticObject s_obj(obj->getType(), objectpos, staticdata_new);
1858 block->m_static_objects.insert(id, s_obj);
1859 obj->m_static_block = blockpos_o;
1860 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1861 "deactivateFarObjects: Static data moved in");
1863 // Delete from block where object was located
1864 block = m_map->emergeBlock(old_static_block, false);
1866 errorstream<<"ServerEnvironment::deactivateFarObjects(): "
1867 <<"Could not delete object id="<<id
1868 <<" from it's previous block "<<PP(old_static_block)
1872 block->m_static_objects.remove(id);
1873 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1874 "deactivateFarObjects: Static data moved out");
1878 // If block is active, don't remove
1879 if(!force_delete && m_active_blocks.contains(blockpos_o))
1882 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
1883 <<"deactivating object id="<<id<<" on inactive block "
1884 <<PP(blockpos_o)<<std::endl;
1886 // If known by some client, don't immediately delete.
1887 bool pending_delete = (obj->m_known_by_count > 0 && !force_delete);
1890 Update the static data
1893 if(obj->isStaticAllowed())
1895 // Create new static object
1896 std::string staticdata_new = obj->getStaticData();
1897 StaticObject s_obj(obj->getType(), objectpos, staticdata_new);
1899 bool stays_in_same_block = false;
1900 bool data_changed = true;
1902 if(obj->m_static_exists){
1903 if(obj->m_static_block == blockpos_o)
1904 stays_in_same_block = true;
1906 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
1908 std::map<u16, StaticObject>::iterator n =
1909 block->m_static_objects.m_active.find(id);
1910 if(n != block->m_static_objects.m_active.end()){
1911 StaticObject static_old = n->second;
1913 float save_movem = obj->getMinimumSavedMovement();
1915 if(static_old.data == staticdata_new &&
1916 (static_old.pos - objectpos).getLength() < save_movem)
1917 data_changed = false;
1919 errorstream<<"ServerEnvironment::deactivateFarObjects(): "
1920 <<"id="<<id<<" m_static_exists=true but "
1921 <<"static data doesn't actually exist in "
1922 <<PP(obj->m_static_block)<<std::endl;
1926 bool shall_be_written = (!stays_in_same_block || data_changed);
1928 // Delete old static object
1929 if(obj->m_static_exists)
1931 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
1934 block->m_static_objects.remove(id);
1935 obj->m_static_exists = false;
1936 // Only mark block as modified if data changed considerably
1937 if(shall_be_written)
1938 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1939 "deactivateFarObjects: Static data "
1940 "changed considerably");
1944 // Add to the block where the object is located in
1945 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1946 // Get or generate the block
1947 MapBlock *block = NULL;
1949 block = m_map->emergeBlock(blockpos);
1950 } catch(InvalidPositionException &e){
1951 // Handled via NULL pointer
1952 // NOTE: emergeBlock's failure is usually determined by it
1953 // actually returning NULL
1958 if(block->m_static_objects.m_stored.size() >= g_settings->getU16("max_objects_per_block")){
1959 errorstream<<"ServerEnv: Trying to store id="<<obj->getId()
1960 <<" statically but block "<<PP(blockpos)
1961 <<" already contains "
1962 <<block->m_static_objects.m_stored.size()
1964 <<" Forcing delete."<<std::endl;
1965 force_delete = true;
1967 // If static counterpart already exists in target block,
1969 // This shouldn't happen because the object is removed from
1970 // the previous block before this according to
1971 // obj->m_static_block, but happens rarely for some unknown
1972 // reason. Unsuccessful attempts have been made to find
1974 if(id && block->m_static_objects.m_active.find(id) != block->m_static_objects.m_active.end()){
1975 infostream<<"ServerEnv: WARNING: Performing hack #83274"
1977 block->m_static_objects.remove(id);
1979 // Store static data
1980 u16 store_id = pending_delete ? id : 0;
1981 block->m_static_objects.insert(store_id, s_obj);
1983 // Only mark block as modified if data changed considerably
1984 if(shall_be_written)
1985 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1986 "deactivateFarObjects: Static data "
1987 "changed considerably");
1989 obj->m_static_exists = true;
1990 obj->m_static_block = block->getPos();
1995 v3s16 p = floatToInt(objectpos, BS);
1996 errorstream<<"ServerEnv: Could not find or generate "
1997 <<"a block for storing id="<<obj->getId()
1998 <<" statically (pos="<<PP(p)<<")"<<std::endl;
2005 If known by some client, set pending deactivation.
2006 Otherwise delete it immediately.
2009 if(pending_delete && !force_delete)
2011 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
2012 <<"object id="<<id<<" is known by clients"
2013 <<"; not deleting yet"<<std::endl;
2015 obj->m_pending_deactivation = true;
2019 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
2020 <<"object id="<<id<<" is not known by clients"
2021 <<"; deleting"<<std::endl;
2023 // Tell the object about removal
2024 obj->removingFromEnvironment();
2025 // Deregister in scripting api
2026 m_script->removeObjectReference(obj);
2028 // Delete active object
2029 if(obj->environmentDeletes())
2031 // Id to be removed from m_active_objects
2032 objects_to_remove.push_back(id);
2035 // Remove references from m_active_objects
2036 for(std::list<u16>::iterator i = objects_to_remove.begin();
2037 i != objects_to_remove.end(); ++i)
2039 m_active_objects.erase(*i);
2046 #include "clientsimpleobject.h"
2052 ClientEnvironment::ClientEnvironment(ClientMap *map, scene::ISceneManager *smgr,
2053 ITextureSource *texturesource, IGameDef *gamedef,
2054 IrrlichtDevice *irr):
2057 m_texturesource(texturesource),
2063 ClientEnvironment::~ClientEnvironment()
2065 // delete active objects
2066 for(std::map<u16, ClientActiveObject*>::iterator
2067 i = m_active_objects.begin();
2068 i != m_active_objects.end(); ++i)
2073 for(std::list<ClientSimpleObject*>::iterator
2074 i = m_simple_objects.begin(); i != m_simple_objects.end(); ++i)
2083 Map & ClientEnvironment::getMap()
2088 ClientMap & ClientEnvironment::getClientMap()
2093 void ClientEnvironment::addPlayer(Player *player)
2095 DSTACK(__FUNCTION_NAME);
2097 It is a failure if player is local and there already is a local
2100 assert(!(player->isLocal() == true && getLocalPlayer() != NULL));
2102 Environment::addPlayer(player);
2105 LocalPlayer * ClientEnvironment::getLocalPlayer()
2107 for(std::list<Player*>::iterator i = m_players.begin();
2108 i != m_players.end(); ++i)
2110 Player *player = *i;
2111 if(player->isLocal())
2112 return (LocalPlayer*)player;
2117 void ClientEnvironment::step(float dtime)
2119 DSTACK(__FUNCTION_NAME);
2121 /* Step time of day */
2122 stepTimeOfDay(dtime);
2124 // Get some settings
2125 bool fly_allowed = m_gamedef->checkLocalPrivilege("fly");
2126 bool free_move = fly_allowed && g_settings->getBool("free_move");
2129 LocalPlayer *lplayer = getLocalPlayer();
2131 // collision info queue
2132 std::list<CollisionInfo> player_collisions;
2135 Get the speed the player is going
2137 bool is_climbing = lplayer->is_climbing;
2139 f32 player_speed = lplayer->getSpeed().getLength();
2142 Maximum position increment
2144 //f32 position_max_increment = 0.05*BS;
2145 f32 position_max_increment = 0.1*BS;
2147 // Maximum time increment (for collision detection etc)
2148 // time = distance / speed
2149 f32 dtime_max_increment = 1;
2150 if(player_speed > 0.001)
2151 dtime_max_increment = position_max_increment / player_speed;
2153 // Maximum time increment is 10ms or lower
2154 if(dtime_max_increment > 0.01)
2155 dtime_max_increment = 0.01;
2157 // Don't allow overly huge dtime
2161 f32 dtime_downcount = dtime;
2164 Stuff that has a maximum time increment
2173 if(dtime_downcount > dtime_max_increment)
2175 dtime_part = dtime_max_increment;
2176 dtime_downcount -= dtime_part;
2180 dtime_part = dtime_downcount;
2182 Setting this to 0 (no -=dtime_part) disables an infinite loop
2183 when dtime_part is so small that dtime_downcount -= dtime_part
2186 dtime_downcount = 0;
2195 if(free_move == false && is_climbing == false)
2198 v3f speed = lplayer->getSpeed();
2199 if(lplayer->in_liquid == false)
2200 speed.Y -= lplayer->movement_gravity * lplayer->physics_override_gravity * dtime_part * 2;
2202 // Liquid floating / sinking
2203 if(lplayer->in_liquid && !lplayer->swimming_vertical)
2204 speed.Y -= lplayer->movement_liquid_sink * dtime_part * 2;
2206 // Liquid resistance
2207 if(lplayer->in_liquid_stable || lplayer->in_liquid)
2209 // How much the node's viscosity blocks movement, ranges between 0 and 1
2210 // Should match the scale at which viscosity increase affects other liquid attributes
2211 const f32 viscosity_factor = 0.3;
2213 v3f d_wanted = -speed / lplayer->movement_liquid_fluidity;
2214 f32 dl = d_wanted.getLength();
2215 if(dl > lplayer->movement_liquid_fluidity_smooth)
2216 dl = lplayer->movement_liquid_fluidity_smooth;
2217 dl *= (lplayer->liquid_viscosity * viscosity_factor) + (1 - viscosity_factor);
2219 v3f d = d_wanted.normalize() * dl;
2223 if(speed.X > lplayer->movement_liquid_fluidity + lplayer->movement_liquid_fluidity_smooth) speed.X -= lplayer->movement_liquid_fluidity_smooth;
2224 if(speed.X < -lplayer->movement_liquid_fluidity - lplayer->movement_liquid_fluidity_smooth) speed.X += lplayer->movement_liquid_fluidity_smooth;
2225 if(speed.Y > lplayer->movement_liquid_fluidity + lplayer->movement_liquid_fluidity_smooth) speed.Y -= lplayer->movement_liquid_fluidity_smooth;
2226 if(speed.Y < -lplayer->movement_liquid_fluidity - lplayer->movement_liquid_fluidity_smooth) speed.Y += lplayer->movement_liquid_fluidity_smooth;
2227 if(speed.Z > lplayer->movement_liquid_fluidity + lplayer->movement_liquid_fluidity_smooth) speed.Z -= lplayer->movement_liquid_fluidity_smooth;
2228 if(speed.Z < -lplayer->movement_liquid_fluidity - lplayer->movement_liquid_fluidity_smooth) speed.Z += lplayer->movement_liquid_fluidity_smooth;
2232 lplayer->setSpeed(speed);
2237 This also does collision detection.
2239 lplayer->move(dtime_part, this, position_max_increment,
2240 &player_collisions);
2243 while(dtime_downcount > 0.001);
2245 //std::cout<<"Looped "<<loopcount<<" times."<<std::endl;
2247 for(std::list<CollisionInfo>::iterator
2248 i = player_collisions.begin();
2249 i != player_collisions.end(); ++i)
2251 CollisionInfo &info = *i;
2252 v3f speed_diff = info.new_speed - info.old_speed;;
2253 // Handle only fall damage
2254 // (because otherwise walking against something in fast_move kills you)
2255 if(speed_diff.Y < 0 || info.old_speed.Y >= 0)
2257 // Get rid of other components
2260 f32 pre_factor = 1; // 1 hp per node/s
2261 f32 tolerance = BS*14; // 5 without damage
2262 f32 post_factor = 1; // 1 hp per node/s
2263 if(info.type == COLLISION_NODE)
2265 const ContentFeatures &f = m_gamedef->ndef()->
2266 get(m_map->getNodeNoEx(info.node_p));
2267 // Determine fall damage multiplier
2268 int addp = itemgroup_get(f.groups, "fall_damage_add_percent");
2269 pre_factor = 1.0 + (float)addp/100.0;
2271 float speed = pre_factor * speed_diff.getLength();
2272 if(speed > tolerance)
2274 f32 damage_f = (speed - tolerance)/BS * post_factor;
2275 u16 damage = (u16)(damage_f+0.5);
2277 damageLocalPlayer(damage, true);
2278 MtEvent *e = new SimpleTriggerEvent("PlayerFallingDamage");
2279 m_gamedef->event()->put(e);
2285 A quick draft of lava damage
2287 if(m_lava_hurt_interval.step(dtime, 1.0))
2289 v3f pf = lplayer->getPosition();
2291 // Feet, middle and head
2292 v3s16 p1 = floatToInt(pf + v3f(0, BS*0.1, 0), BS);
2293 MapNode n1 = m_map->getNodeNoEx(p1);
2294 v3s16 p2 = floatToInt(pf + v3f(0, BS*0.8, 0), BS);
2295 MapNode n2 = m_map->getNodeNoEx(p2);
2296 v3s16 p3 = floatToInt(pf + v3f(0, BS*1.6, 0), BS);
2297 MapNode n3 = m_map->getNodeNoEx(p3);
2299 u32 damage_per_second = 0;
2300 damage_per_second = MYMAX(damage_per_second,
2301 m_gamedef->ndef()->get(n1).damage_per_second);
2302 damage_per_second = MYMAX(damage_per_second,
2303 m_gamedef->ndef()->get(n2).damage_per_second);
2304 damage_per_second = MYMAX(damage_per_second,
2305 m_gamedef->ndef()->get(n3).damage_per_second);
2307 if(damage_per_second != 0)
2309 damageLocalPlayer(damage_per_second, true);
2316 if(m_drowning_interval.step(dtime, 2.0))
2318 v3f pf = lplayer->getPosition();
2321 v3s16 p = floatToInt(pf + v3f(0, BS*1.6, 0), BS);
2322 MapNode n = m_map->getNodeNoEx(p);
2323 ContentFeatures c = m_gamedef->ndef()->get(n);
2324 u8 drowning_damage = c.drowning;
2325 if(drowning_damage > 0 && lplayer->hp > 0){
2326 u16 breath = lplayer->getBreath();
2333 lplayer->setBreath(breath);
2334 updateLocalPlayerBreath(breath);
2337 if(lplayer->getBreath() == 0 && drowning_damage > 0){
2338 damageLocalPlayer(drowning_damage, true);
2341 if(m_breathing_interval.step(dtime, 0.5))
2343 v3f pf = lplayer->getPosition();
2346 v3s16 p = floatToInt(pf + v3f(0, BS*1.6, 0), BS);
2347 MapNode n = m_map->getNodeNoEx(p);
2348 ContentFeatures c = m_gamedef->ndef()->get(n);
2350 lplayer->setBreath(11);
2352 else if(c.drowning == 0){
2353 u16 breath = lplayer->getBreath();
2356 lplayer->setBreath(breath);
2357 updateLocalPlayerBreath(breath);
2363 Stuff that can be done in an arbitarily large dtime
2365 for(std::list<Player*>::iterator i = m_players.begin();
2366 i != m_players.end(); ++i)
2368 Player *player = *i;
2371 Handle non-local players
2373 if(player->isLocal() == false)
2376 player->move(dtime, *m_map, 100*BS);
2380 // Update lighting on all players on client
2384 v3s16 p = player->getLightPosition();
2385 MapNode n = m_map->getNode(p);
2386 light = n.getLightBlendF1((float)getDayNightRatio()/1000, m_gamedef->ndef());
2388 catch(InvalidPositionException &e){
2389 light = blend_light_f1((float)getDayNightRatio()/1000, LIGHT_SUN, 0);
2391 player->light = light;
2395 Step active objects and update lighting of them
2398 g_profiler->avg("CEnv: num of objects", m_active_objects.size());
2399 bool update_lighting = m_active_object_light_update_interval.step(dtime, 0.21);
2400 for(std::map<u16, ClientActiveObject*>::iterator
2401 i = m_active_objects.begin();
2402 i != m_active_objects.end(); ++i)
2404 ClientActiveObject* obj = i->second;
2406 obj->step(dtime, this);
2414 v3s16 p = obj->getLightPosition();
2415 MapNode n = m_map->getNode(p);
2416 light = n.getLightBlend(getDayNightRatio(), m_gamedef->ndef());
2418 catch(InvalidPositionException &e){
2419 light = blend_light(getDayNightRatio(), LIGHT_SUN, 0);
2421 obj->updateLight(light);
2426 Step and handle simple objects
2428 g_profiler->avg("CEnv: num of simple objects", m_simple_objects.size());
2429 for(std::list<ClientSimpleObject*>::iterator
2430 i = m_simple_objects.begin(); i != m_simple_objects.end();)
2432 ClientSimpleObject *simple = *i;
2433 std::list<ClientSimpleObject*>::iterator cur = i;
2435 simple->step(dtime);
2436 if(simple->m_to_be_removed){
2438 m_simple_objects.erase(cur);
2443 void ClientEnvironment::addSimpleObject(ClientSimpleObject *simple)
2445 m_simple_objects.push_back(simple);
2448 ClientActiveObject* ClientEnvironment::getActiveObject(u16 id)
2450 std::map<u16, ClientActiveObject*>::iterator n;
2451 n = m_active_objects.find(id);
2452 if(n == m_active_objects.end())
2457 bool isFreeClientActiveObjectId(u16 id,
2458 std::map<u16, ClientActiveObject*> &objects)
2463 return objects.find(id) == objects.end();
2466 u16 getFreeClientActiveObjectId(
2467 std::map<u16, ClientActiveObject*> &objects)
2469 //try to reuse id's as late as possible
2470 static u16 last_used_id = 0;
2471 u16 startid = last_used_id;
2475 if(isFreeClientActiveObjectId(last_used_id, objects))
2476 return last_used_id;
2478 if(last_used_id == startid)
2483 u16 ClientEnvironment::addActiveObject(ClientActiveObject *object)
2486 if(object->getId() == 0)
2488 u16 new_id = getFreeClientActiveObjectId(m_active_objects);
2491 infostream<<"ClientEnvironment::addActiveObject(): "
2492 <<"no free ids available"<<std::endl;
2496 object->setId(new_id);
2498 if(isFreeClientActiveObjectId(object->getId(), m_active_objects) == false)
2500 infostream<<"ClientEnvironment::addActiveObject(): "
2501 <<"id is not free ("<<object->getId()<<")"<<std::endl;
2505 infostream<<"ClientEnvironment::addActiveObject(): "
2506 <<"added (id="<<object->getId()<<")"<<std::endl;
2507 m_active_objects[object->getId()] = object;
2508 object->addToScene(m_smgr, m_texturesource, m_irr);
2509 { // Update lighting immediately
2513 v3s16 p = object->getLightPosition();
2514 MapNode n = m_map->getNode(p);
2515 light = n.getLightBlend(getDayNightRatio(), m_gamedef->ndef());
2517 catch(InvalidPositionException &e){
2518 light = blend_light(getDayNightRatio(), LIGHT_SUN, 0);
2520 object->updateLight(light);
2522 return object->getId();
2525 void ClientEnvironment::addActiveObject(u16 id, u8 type,
2526 const std::string &init_data)
2528 ClientActiveObject* obj =
2529 ClientActiveObject::create(type, m_gamedef, this);
2532 infostream<<"ClientEnvironment::addActiveObject(): "
2533 <<"id="<<id<<" type="<<type<<": Couldn't create object"
2542 obj->initialize(init_data);
2544 catch(SerializationError &e)
2546 errorstream<<"ClientEnvironment::addActiveObject():"
2547 <<" id="<<id<<" type="<<type
2548 <<": SerializationError in initialize(): "
2550 <<": init_data="<<serializeJsonString(init_data)
2554 addActiveObject(obj);
2557 void ClientEnvironment::removeActiveObject(u16 id)
2559 verbosestream<<"ClientEnvironment::removeActiveObject(): "
2560 <<"id="<<id<<std::endl;
2561 ClientActiveObject* obj = getActiveObject(id);
2564 infostream<<"ClientEnvironment::removeActiveObject(): "
2565 <<"id="<<id<<" not found"<<std::endl;
2568 obj->removeFromScene(true);
2570 m_active_objects.erase(id);
2573 void ClientEnvironment::processActiveObjectMessage(u16 id,
2574 const std::string &data)
2576 ClientActiveObject* obj = getActiveObject(id);
2579 infostream<<"ClientEnvironment::processActiveObjectMessage():"
2580 <<" got message for id="<<id<<", which doesn't exist."
2586 obj->processMessage(data);
2588 catch(SerializationError &e)
2590 errorstream<<"ClientEnvironment::processActiveObjectMessage():"
2591 <<" id="<<id<<" type="<<obj->getType()
2592 <<" SerializationError in processMessage(),"
2593 <<" message="<<serializeJsonString(data)
2599 Callbacks for activeobjects
2602 void ClientEnvironment::damageLocalPlayer(u8 damage, bool handle_hp)
2604 LocalPlayer *lplayer = getLocalPlayer();
2608 if(lplayer->hp > damage)
2609 lplayer->hp -= damage;
2614 ClientEnvEvent event;
2615 event.type = CEE_PLAYER_DAMAGE;
2616 event.player_damage.amount = damage;
2617 event.player_damage.send_to_server = handle_hp;
2618 m_client_event_queue.push_back(event);
2621 void ClientEnvironment::updateLocalPlayerBreath(u16 breath)
2623 ClientEnvEvent event;
2624 event.type = CEE_PLAYER_BREATH;
2625 event.player_breath.amount = breath;
2626 m_client_event_queue.push_back(event);
2630 Client likes to call these
2633 void ClientEnvironment::getActiveObjects(v3f origin, f32 max_d,
2634 std::vector<DistanceSortedActiveObject> &dest)
2636 for(std::map<u16, ClientActiveObject*>::iterator
2637 i = m_active_objects.begin();
2638 i != m_active_objects.end(); ++i)
2640 ClientActiveObject* obj = i->second;
2642 f32 d = (obj->getPosition() - origin).getLength();
2647 DistanceSortedActiveObject dso(obj, d);
2649 dest.push_back(dso);
2653 ClientEnvEvent ClientEnvironment::getClientEvent()
2655 ClientEnvEvent event;
2656 if(m_client_event_queue.empty())
2657 event.type = CEE_NONE;
2659 event = m_client_event_queue.front();
2660 m_client_event_queue.pop_front();
2665 #endif // #ifndef SERVER