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(report_interval != 0 &&
1009 num_blocks_checked % report_interval == 0){
1010 float percent = 100.0 * (float)num_blocks_checked /
1011 loadable_blocks.size();
1012 infostream<<"ServerEnvironment::clearAllObjects(): "
1013 <<"Cleared "<<num_objs_cleared<<" objects"
1014 <<" in "<<num_blocks_cleared<<" blocks ("
1015 <<percent<<"%)"<<std::endl;
1017 if(num_blocks_checked % unload_interval == 0){
1018 m_map->unloadUnreferencedBlocks();
1021 m_map->unloadUnreferencedBlocks();
1023 // Drop references that were added above
1024 for(std::list<v3s16>::iterator i = loaded_blocks.begin();
1025 i != loaded_blocks.end(); ++i)
1028 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1033 infostream<<"ServerEnvironment::clearAllObjects(): "
1034 <<"Finished: Cleared "<<num_objs_cleared<<" objects"
1035 <<" in "<<num_blocks_cleared<<" blocks"<<std::endl;
1038 void ServerEnvironment::step(float dtime)
1040 DSTACK(__FUNCTION_NAME);
1042 //TimeTaker timer("ServerEnv step");
1044 /* Step time of day */
1045 stepTimeOfDay(dtime);
1048 // NOTE: This is kind of funny on a singleplayer game, but doesn't
1049 // really matter that much.
1050 m_recommended_send_interval = g_settings->getFloat("dedicated_server_step");
1056 m_game_time_fraction_counter += dtime;
1057 u32 inc_i = (u32)m_game_time_fraction_counter;
1058 m_game_time += inc_i;
1059 m_game_time_fraction_counter -= (float)inc_i;
1066 ScopeProfiler sp(g_profiler, "SEnv: handle players avg", SPT_AVG);
1067 for(std::list<Player*>::iterator i = m_players.begin();
1068 i != m_players.end(); ++i)
1070 Player *player = *i;
1072 // Ignore disconnected players
1073 if(player->peer_id == 0)
1077 player->move(dtime, *m_map, 100*BS);
1082 Manage active block list
1084 if(m_active_blocks_management_interval.step(dtime, 2.0))
1086 ScopeProfiler sp(g_profiler, "SEnv: manage act. block list avg /2s", SPT_AVG);
1088 Get player block positions
1090 std::list<v3s16> players_blockpos;
1091 for(std::list<Player*>::iterator
1092 i = m_players.begin();
1093 i != m_players.end(); ++i)
1095 Player *player = *i;
1096 // Ignore disconnected players
1097 if(player->peer_id == 0)
1099 v3s16 blockpos = getNodeBlockPos(
1100 floatToInt(player->getPosition(), BS));
1101 players_blockpos.push_back(blockpos);
1105 Update list of active blocks, collecting changes
1107 const s16 active_block_range = g_settings->getS16("active_block_range");
1108 std::set<v3s16> blocks_removed;
1109 std::set<v3s16> blocks_added;
1110 m_active_blocks.update(players_blockpos, active_block_range,
1111 blocks_removed, blocks_added);
1114 Handle removed blocks
1117 // Convert active objects that are no more in active blocks to static
1118 deactivateFarObjects(false);
1120 for(std::set<v3s16>::iterator
1121 i = blocks_removed.begin();
1122 i != blocks_removed.end(); ++i)
1126 /* infostream<<"Server: Block " << PP(p)
1127 << " became inactive"<<std::endl; */
1129 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1133 // Set current time as timestamp (and let it set ChangedFlag)
1134 block->setTimestamp(m_game_time);
1141 for(std::set<v3s16>::iterator
1142 i = blocks_added.begin();
1143 i != blocks_added.end(); ++i)
1147 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1149 // Block needs to be fetched first
1150 m_emerger->enqueueBlockEmerge(
1151 PEER_ID_INEXISTENT, p, false);
1152 m_active_blocks.m_list.erase(p);
1156 activateBlock(block);
1157 /* infostream<<"Server: Block " << PP(p)
1158 << " became active"<<std::endl; */
1163 Mess around in active blocks
1165 if(m_active_blocks_nodemetadata_interval.step(dtime, 1.0))
1167 ScopeProfiler sp(g_profiler, "SEnv: mess in act. blocks avg /1s", SPT_AVG);
1171 for(std::set<v3s16>::iterator
1172 i = m_active_blocks.m_list.begin();
1173 i != m_active_blocks.m_list.end(); ++i)
1177 /*infostream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
1178 <<") being handled"<<std::endl;*/
1180 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1184 // Reset block usage timer
1185 block->resetUsageTimer();
1187 // Set current time as timestamp
1188 block->setTimestampNoChangedFlag(m_game_time);
1189 // If time has changed much from the one on disk,
1190 // set block to be saved when it is unloaded
1191 if(block->getTimestamp() > block->getDiskTimestamp() + 60)
1192 block->raiseModified(MOD_STATE_WRITE_AT_UNLOAD,
1193 "Timestamp older than 60s (step)");
1196 std::map<v3s16, NodeTimer> elapsed_timers =
1197 block->m_node_timers.step((float)dtime);
1198 if(!elapsed_timers.empty()){
1200 for(std::map<v3s16, NodeTimer>::iterator
1201 i = elapsed_timers.begin();
1202 i != elapsed_timers.end(); i++){
1203 n = block->getNodeNoEx(i->first);
1204 p = i->first + block->getPosRelative();
1205 if(m_script->node_on_timer(p,n,i->second.elapsed))
1206 block->setNodeTimer(i->first,NodeTimer(i->second.timeout,0));
1212 const float abm_interval = 1.0;
1213 if(m_active_block_modifier_interval.step(dtime, abm_interval))
1215 if(m_active_block_interval_overload_skip > 0){
1216 ScopeProfiler sp(g_profiler, "SEnv: ABM overload skips");
1217 m_active_block_interval_overload_skip--;
1220 ScopeProfiler sp(g_profiler, "SEnv: modify in blocks avg /1s", SPT_AVG);
1221 TimeTaker timer("modify in active blocks");
1223 // Initialize handling of ActiveBlockModifiers
1224 ABMHandler abmhandler(m_abms, abm_interval, this, true);
1226 for(std::set<v3s16>::iterator
1227 i = m_active_blocks.m_list.begin();
1228 i != m_active_blocks.m_list.end(); ++i)
1232 /*infostream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
1233 <<") being handled"<<std::endl;*/
1235 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1239 // Set current time as timestamp
1240 block->setTimestampNoChangedFlag(m_game_time);
1242 /* Handle ActiveBlockModifiers */
1243 abmhandler.apply(block);
1246 u32 time_ms = timer.stop(true);
1247 u32 max_time_ms = 200;
1248 if(time_ms > max_time_ms){
1249 infostream<<"WARNING: active block modifiers took "
1250 <<time_ms<<"ms (longer than "
1251 <<max_time_ms<<"ms)"<<std::endl;
1252 m_active_block_interval_overload_skip = (time_ms / max_time_ms) + 1;
1257 Step script environment (run global on_step())
1259 m_script->environment_Step(dtime);
1265 ScopeProfiler sp(g_profiler, "SEnv: step act. objs avg", SPT_AVG);
1266 //TimeTaker timer("Step active objects");
1268 g_profiler->avg("SEnv: num of objects", m_active_objects.size());
1270 // This helps the objects to send data at the same time
1271 bool send_recommended = false;
1272 m_send_recommended_timer += dtime;
1273 if(m_send_recommended_timer > getSendRecommendedInterval())
1275 m_send_recommended_timer -= getSendRecommendedInterval();
1276 send_recommended = true;
1279 for(std::map<u16, ServerActiveObject*>::iterator
1280 i = m_active_objects.begin();
1281 i != m_active_objects.end(); ++i)
1283 ServerActiveObject* obj = i->second;
1284 // Remove non-peaceful mobs on peaceful mode
1285 if(g_settings->getBool("only_peaceful_mobs")){
1286 if(!obj->isPeaceful())
1287 obj->m_removed = true;
1289 // Don't step if is to be removed or stored statically
1290 if(obj->m_removed || obj->m_pending_deactivation)
1293 obj->step(dtime, send_recommended);
1294 // Read messages from object
1295 while(!obj->m_messages_out.empty())
1297 m_active_object_messages.push_back(
1298 obj->m_messages_out.pop_front());
1304 Manage active objects
1306 if(m_object_management_interval.step(dtime, 0.5))
1308 ScopeProfiler sp(g_profiler, "SEnv: remove removed objs avg /.5s", SPT_AVG);
1310 Remove objects that satisfy (m_removed && m_known_by_count==0)
1312 removeRemovedObjects();
1316 ServerActiveObject* ServerEnvironment::getActiveObject(u16 id)
1318 std::map<u16, ServerActiveObject*>::iterator n;
1319 n = m_active_objects.find(id);
1320 if(n == m_active_objects.end())
1325 bool isFreeServerActiveObjectId(u16 id,
1326 std::map<u16, ServerActiveObject*> &objects)
1331 return objects.find(id) == objects.end();
1334 u16 getFreeServerActiveObjectId(
1335 std::map<u16, ServerActiveObject*> &objects)
1337 //try to reuse id's as late as possible
1338 static u16 last_used_id = 0;
1339 u16 startid = last_used_id;
1343 if(isFreeServerActiveObjectId(last_used_id, objects))
1344 return last_used_id;
1346 if(last_used_id == startid)
1351 u16 ServerEnvironment::addActiveObject(ServerActiveObject *object)
1354 u16 id = addActiveObjectRaw(object, true, 0);
1359 bool ServerEnvironment::addActiveObjectAsStatic(ServerActiveObject *obj)
1363 v3f objectpos = obj->getBasePosition();
1365 // The block in which the object resides in
1366 v3s16 blockpos_o = getNodeBlockPos(floatToInt(objectpos, BS));
1369 Update the static data
1372 // Create new static object
1373 std::string staticdata = obj->getStaticData();
1374 StaticObject s_obj(obj->getType(), objectpos, staticdata);
1375 // Add to the block where the object is located in
1376 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1377 // Get or generate the block
1378 MapBlock *block = m_map->emergeBlock(blockpos);
1380 bool succeeded = false;
1384 block->m_static_objects.insert(0, s_obj);
1385 block->raiseModified(MOD_STATE_WRITE_AT_UNLOAD,
1386 "addActiveObjectAsStatic");
1390 infostream<<"ServerEnvironment::addActiveObjectAsStatic: "
1391 <<"Could not find or generate "
1392 <<"a block for storing static object"<<std::endl;
1396 if(obj->environmentDeletes())
1404 Finds out what new objects have been added to
1405 inside a radius around a position
1407 void ServerEnvironment::getAddedActiveObjects(v3s16 pos, s16 radius,
1408 std::set<u16> ¤t_objects,
1409 std::set<u16> &added_objects)
1411 v3f pos_f = intToFloat(pos, BS);
1412 f32 radius_f = radius * BS;
1414 Go through the object list,
1415 - discard m_removed objects,
1416 - discard objects that are too far away,
1417 - discard objects that are found in current_objects.
1418 - add remaining objects to added_objects
1420 for(std::map<u16, ServerActiveObject*>::iterator
1421 i = m_active_objects.begin();
1422 i != m_active_objects.end(); ++i)
1426 ServerActiveObject *object = i->second;
1429 // Discard if removed or deactivating
1430 if(object->m_removed || object->m_pending_deactivation)
1432 if(object->unlimitedTransferDistance() == false){
1433 // Discard if too far
1434 f32 distance_f = object->getBasePosition().getDistanceFrom(pos_f);
1435 if(distance_f > radius_f)
1438 // Discard if already on current_objects
1439 std::set<u16>::iterator n;
1440 n = current_objects.find(id);
1441 if(n != current_objects.end())
1443 // Add to added_objects
1444 added_objects.insert(id);
1449 Finds out what objects have been removed from
1450 inside a radius around a position
1452 void ServerEnvironment::getRemovedActiveObjects(v3s16 pos, s16 radius,
1453 std::set<u16> ¤t_objects,
1454 std::set<u16> &removed_objects)
1456 v3f pos_f = intToFloat(pos, BS);
1457 f32 radius_f = radius * BS;
1459 Go through current_objects; object is removed if:
1460 - object is not found in m_active_objects (this is actually an
1461 error condition; objects should be set m_removed=true and removed
1462 only after all clients have been informed about removal), or
1463 - object has m_removed=true, or
1464 - object is too far away
1466 for(std::set<u16>::iterator
1467 i = current_objects.begin();
1468 i != current_objects.end(); ++i)
1471 ServerActiveObject *object = getActiveObject(id);
1474 infostream<<"ServerEnvironment::getRemovedActiveObjects():"
1475 <<" object in current_objects is NULL"<<std::endl;
1476 removed_objects.insert(id);
1480 if(object->m_removed || object->m_pending_deactivation)
1482 removed_objects.insert(id);
1486 // If transfer distance is unlimited, don't remove
1487 if(object->unlimitedTransferDistance())
1490 f32 distance_f = object->getBasePosition().getDistanceFrom(pos_f);
1492 if(distance_f >= radius_f)
1494 removed_objects.insert(id);
1502 ActiveObjectMessage ServerEnvironment::getActiveObjectMessage()
1504 if(m_active_object_messages.empty())
1505 return ActiveObjectMessage(0);
1507 ActiveObjectMessage message = m_active_object_messages.front();
1508 m_active_object_messages.pop_front();
1513 ************ Private methods *************
1516 u16 ServerEnvironment::addActiveObjectRaw(ServerActiveObject *object,
1517 bool set_changed, u32 dtime_s)
1520 if(object->getId() == 0){
1521 u16 new_id = getFreeServerActiveObjectId(m_active_objects);
1524 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1525 <<"no free ids available"<<std::endl;
1526 if(object->environmentDeletes())
1530 object->setId(new_id);
1533 verbosestream<<"ServerEnvironment::addActiveObjectRaw(): "
1534 <<"supplied with id "<<object->getId()<<std::endl;
1536 if(isFreeServerActiveObjectId(object->getId(), m_active_objects) == false)
1538 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1539 <<"id is not free ("<<object->getId()<<")"<<std::endl;
1540 if(object->environmentDeletes())
1544 /*infostream<<"ServerEnvironment::addActiveObjectRaw(): "
1545 <<"added (id="<<object->getId()<<")"<<std::endl;*/
1547 m_active_objects[object->getId()] = object;
1549 verbosestream<<"ServerEnvironment::addActiveObjectRaw(): "
1550 <<"Added id="<<object->getId()<<"; there are now "
1551 <<m_active_objects.size()<<" active objects."
1554 // Register reference in scripting api (must be done before post-init)
1555 m_script->addObjectReference(object);
1556 // Post-initialize object
1557 object->addedToEnvironment(dtime_s);
1559 // Add static data to block
1560 if(object->isStaticAllowed())
1562 // Add static object to active static list of the block
1563 v3f objectpos = object->getBasePosition();
1564 std::string staticdata = object->getStaticData();
1565 StaticObject s_obj(object->getType(), objectpos, staticdata);
1566 // Add to the block where the object is located in
1567 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1568 MapBlock *block = m_map->emergeBlock(blockpos);
1570 block->m_static_objects.m_active[object->getId()] = s_obj;
1571 object->m_static_exists = true;
1572 object->m_static_block = blockpos;
1575 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1576 "addActiveObjectRaw");
1578 v3s16 p = floatToInt(objectpos, BS);
1579 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1580 <<"could not emerge block for storing id="<<object->getId()
1581 <<" statically (pos="<<PP(p)<<")"<<std::endl;
1585 return object->getId();
1589 Remove objects that satisfy (m_removed && m_known_by_count==0)
1591 void ServerEnvironment::removeRemovedObjects()
1593 std::list<u16> objects_to_remove;
1594 for(std::map<u16, ServerActiveObject*>::iterator
1595 i = m_active_objects.begin();
1596 i != m_active_objects.end(); ++i)
1599 ServerActiveObject* obj = i->second;
1600 // This shouldn't happen but check it
1603 infostream<<"NULL object found in ServerEnvironment"
1604 <<" while finding removed objects. id="<<id<<std::endl;
1605 // Id to be removed from m_active_objects
1606 objects_to_remove.push_back(id);
1611 We will delete objects that are marked as removed or thatare
1612 waiting for deletion after deactivation
1614 if(obj->m_removed == false && obj->m_pending_deactivation == false)
1618 Delete static data from block if is marked as removed
1620 if(obj->m_static_exists && obj->m_removed)
1622 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
1624 block->m_static_objects.remove(id);
1625 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1626 "removeRemovedObjects/remove");
1627 obj->m_static_exists = false;
1629 infostream<<"Failed to emerge block from which an object to "
1630 <<"be removed was loaded from. id="<<id<<std::endl;
1634 // If m_known_by_count > 0, don't actually remove. On some future
1635 // invocation this will be 0, which is when removal will continue.
1636 if(obj->m_known_by_count > 0)
1640 Move static data from active to stored if not marked as removed
1642 if(obj->m_static_exists && !obj->m_removed){
1643 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
1645 std::map<u16, StaticObject>::iterator i =
1646 block->m_static_objects.m_active.find(id);
1647 if(i != block->m_static_objects.m_active.end()){
1648 block->m_static_objects.m_stored.push_back(i->second);
1649 block->m_static_objects.m_active.erase(id);
1650 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1651 "removeRemovedObjects/deactivate");
1654 infostream<<"Failed to emerge block from which an object to "
1655 <<"be deactivated was loaded from. id="<<id<<std::endl;
1659 // Tell the object about removal
1660 obj->removingFromEnvironment();
1661 // Deregister in scripting api
1662 m_script->removeObjectReference(obj);
1665 if(obj->environmentDeletes())
1667 // Id to be removed from m_active_objects
1668 objects_to_remove.push_back(id);
1670 // Remove references from m_active_objects
1671 for(std::list<u16>::iterator i = objects_to_remove.begin();
1672 i != objects_to_remove.end(); ++i)
1674 m_active_objects.erase(*i);
1678 static void print_hexdump(std::ostream &o, const std::string &data)
1680 const int linelength = 16;
1681 for(int l=0; ; l++){
1682 int i0 = linelength * l;
1683 bool at_end = false;
1684 int thislinelength = linelength;
1685 if(i0 + thislinelength > (int)data.size()){
1686 thislinelength = data.size() - i0;
1689 for(int di=0; di<linelength; di++){
1692 if(di<thislinelength)
1693 snprintf(buf, 4, "%.2x ", data[i]);
1695 snprintf(buf, 4, " ");
1699 for(int di=0; di<thislinelength; di++){
1713 Convert stored objects from blocks near the players to active.
1715 void ServerEnvironment::activateObjects(MapBlock *block, u32 dtime_s)
1719 // Ignore if no stored objects (to not set changed flag)
1720 if(block->m_static_objects.m_stored.size() == 0)
1722 verbosestream<<"ServerEnvironment::activateObjects(): "
1723 <<"activating objects of block "<<PP(block->getPos())
1724 <<" ("<<block->m_static_objects.m_stored.size()
1725 <<" objects)"<<std::endl;
1726 bool large_amount = (block->m_static_objects.m_stored.size() > g_settings->getU16("max_objects_per_block"));
1728 errorstream<<"suspiciously large amount of objects detected: "
1729 <<block->m_static_objects.m_stored.size()<<" in "
1730 <<PP(block->getPos())
1731 <<"; removing all of them."<<std::endl;
1732 // Clear stored list
1733 block->m_static_objects.m_stored.clear();
1734 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1735 "stored list cleared in activateObjects due to "
1736 "large amount of objects");
1740 // Activate stored objects
1741 std::list<StaticObject> new_stored;
1742 for(std::list<StaticObject>::iterator
1743 i = block->m_static_objects.m_stored.begin();
1744 i != block->m_static_objects.m_stored.end(); ++i)
1746 /*infostream<<"Server: Creating an active object from "
1747 <<"static data"<<std::endl;*/
1748 StaticObject &s_obj = *i;
1749 // Create an active object from the data
1750 ServerActiveObject *obj = ServerActiveObject::create
1751 (s_obj.type, this, 0, s_obj.pos, s_obj.data);
1752 // If couldn't create object, store static data back.
1755 errorstream<<"ServerEnvironment::activateObjects(): "
1756 <<"failed to create active object from static object "
1757 <<"in block "<<PP(s_obj.pos/BS)
1758 <<" type="<<(int)s_obj.type<<" data:"<<std::endl;
1759 print_hexdump(verbosestream, s_obj.data);
1761 new_stored.push_back(s_obj);
1764 verbosestream<<"ServerEnvironment::activateObjects(): "
1765 <<"activated static object pos="<<PP(s_obj.pos/BS)
1766 <<" type="<<(int)s_obj.type<<std::endl;
1767 // This will also add the object to the active static list
1768 addActiveObjectRaw(obj, false, dtime_s);
1770 // Clear stored list
1771 block->m_static_objects.m_stored.clear();
1772 // Add leftover failed stuff to stored list
1773 for(std::list<StaticObject>::iterator
1774 i = new_stored.begin();
1775 i != new_stored.end(); ++i)
1777 StaticObject &s_obj = *i;
1778 block->m_static_objects.m_stored.push_back(s_obj);
1781 // Turn the active counterparts of activated objects not pending for
1783 for(std::map<u16, StaticObject>::iterator
1784 i = block->m_static_objects.m_active.begin();
1785 i != block->m_static_objects.m_active.end(); ++i)
1788 ServerActiveObject *object = getActiveObject(id);
1790 object->m_pending_deactivation = false;
1794 Note: Block hasn't really been modified here.
1795 The objects have just been activated and moved from the stored
1796 static list to the active static list.
1797 As such, the block is essentially the same.
1798 Thus, do not call block->raiseModified(MOD_STATE_WRITE_NEEDED).
1799 Otherwise there would be a huge amount of unnecessary I/O.
1804 Convert objects that are not standing inside active blocks to static.
1806 If m_known_by_count != 0, active object is not deleted, but static
1807 data is still updated.
1809 If force_delete is set, active object is deleted nevertheless. It
1810 shall only be set so in the destructor of the environment.
1812 If block wasn't generated (not in memory or on disk),
1814 void ServerEnvironment::deactivateFarObjects(bool force_delete)
1816 std::list<u16> objects_to_remove;
1817 for(std::map<u16, ServerActiveObject*>::iterator
1818 i = m_active_objects.begin();
1819 i != m_active_objects.end(); ++i)
1821 ServerActiveObject* obj = i->second;
1824 // Do not deactivate if static data creation not allowed
1825 if(!force_delete && !obj->isStaticAllowed())
1828 // If pending deactivation, let removeRemovedObjects() do it
1829 if(!force_delete && obj->m_pending_deactivation)
1833 v3f objectpos = obj->getBasePosition();
1835 // The block in which the object resides in
1836 v3s16 blockpos_o = getNodeBlockPos(floatToInt(objectpos, BS));
1838 // If object's static data is stored in a deactivated block and object
1839 // is actually located in an active block, re-save to the block in
1840 // which the object is actually located in.
1842 obj->m_static_exists &&
1843 !m_active_blocks.contains(obj->m_static_block) &&
1844 m_active_blocks.contains(blockpos_o))
1846 v3s16 old_static_block = obj->m_static_block;
1848 // Save to block where object is located
1849 MapBlock *block = m_map->emergeBlock(blockpos_o, false);
1851 errorstream<<"ServerEnvironment::deactivateFarObjects(): "
1852 <<"Could not save object id="<<id
1853 <<" to it's current block "<<PP(blockpos_o)
1857 std::string staticdata_new = obj->getStaticData();
1858 StaticObject s_obj(obj->getType(), objectpos, staticdata_new);
1859 block->m_static_objects.insert(id, s_obj);
1860 obj->m_static_block = blockpos_o;
1861 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1862 "deactivateFarObjects: Static data moved in");
1864 // Delete from block where object was located
1865 block = m_map->emergeBlock(old_static_block, false);
1867 errorstream<<"ServerEnvironment::deactivateFarObjects(): "
1868 <<"Could not delete object id="<<id
1869 <<" from it's previous block "<<PP(old_static_block)
1873 block->m_static_objects.remove(id);
1874 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1875 "deactivateFarObjects: Static data moved out");
1879 // If block is active, don't remove
1880 if(!force_delete && m_active_blocks.contains(blockpos_o))
1883 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
1884 <<"deactivating object id="<<id<<" on inactive block "
1885 <<PP(blockpos_o)<<std::endl;
1887 // If known by some client, don't immediately delete.
1888 bool pending_delete = (obj->m_known_by_count > 0 && !force_delete);
1891 Update the static data
1894 if(obj->isStaticAllowed())
1896 // Create new static object
1897 std::string staticdata_new = obj->getStaticData();
1898 StaticObject s_obj(obj->getType(), objectpos, staticdata_new);
1900 bool stays_in_same_block = false;
1901 bool data_changed = true;
1903 if(obj->m_static_exists){
1904 if(obj->m_static_block == blockpos_o)
1905 stays_in_same_block = true;
1907 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
1909 std::map<u16, StaticObject>::iterator n =
1910 block->m_static_objects.m_active.find(id);
1911 if(n != block->m_static_objects.m_active.end()){
1912 StaticObject static_old = n->second;
1914 float save_movem = obj->getMinimumSavedMovement();
1916 if(static_old.data == staticdata_new &&
1917 (static_old.pos - objectpos).getLength() < save_movem)
1918 data_changed = false;
1920 errorstream<<"ServerEnvironment::deactivateFarObjects(): "
1921 <<"id="<<id<<" m_static_exists=true but "
1922 <<"static data doesn't actually exist in "
1923 <<PP(obj->m_static_block)<<std::endl;
1927 bool shall_be_written = (!stays_in_same_block || data_changed);
1929 // Delete old static object
1930 if(obj->m_static_exists)
1932 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
1935 block->m_static_objects.remove(id);
1936 obj->m_static_exists = false;
1937 // Only mark block as modified if data changed considerably
1938 if(shall_be_written)
1939 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1940 "deactivateFarObjects: Static data "
1941 "changed considerably");
1945 // Add to the block where the object is located in
1946 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1947 // Get or generate the block
1948 MapBlock *block = NULL;
1950 block = m_map->emergeBlock(blockpos);
1951 } catch(InvalidPositionException &e){
1952 // Handled via NULL pointer
1953 // NOTE: emergeBlock's failure is usually determined by it
1954 // actually returning NULL
1959 if(block->m_static_objects.m_stored.size() >= g_settings->getU16("max_objects_per_block")){
1960 errorstream<<"ServerEnv: Trying to store id="<<obj->getId()
1961 <<" statically but block "<<PP(blockpos)
1962 <<" already contains "
1963 <<block->m_static_objects.m_stored.size()
1965 <<" Forcing delete."<<std::endl;
1966 force_delete = true;
1968 // If static counterpart already exists in target block,
1970 // This shouldn't happen because the object is removed from
1971 // the previous block before this according to
1972 // obj->m_static_block, but happens rarely for some unknown
1973 // reason. Unsuccessful attempts have been made to find
1975 if(id && block->m_static_objects.m_active.find(id) != block->m_static_objects.m_active.end()){
1976 infostream<<"ServerEnv: WARNING: Performing hack #83274"
1978 block->m_static_objects.remove(id);
1980 // Store static data
1981 u16 store_id = pending_delete ? id : 0;
1982 block->m_static_objects.insert(store_id, s_obj);
1984 // Only mark block as modified if data changed considerably
1985 if(shall_be_written)
1986 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1987 "deactivateFarObjects: Static data "
1988 "changed considerably");
1990 obj->m_static_exists = true;
1991 obj->m_static_block = block->getPos();
1996 v3s16 p = floatToInt(objectpos, BS);
1997 errorstream<<"ServerEnv: Could not find or generate "
1998 <<"a block for storing id="<<obj->getId()
1999 <<" statically (pos="<<PP(p)<<")"<<std::endl;
2006 If known by some client, set pending deactivation.
2007 Otherwise delete it immediately.
2010 if(pending_delete && !force_delete)
2012 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
2013 <<"object id="<<id<<" is known by clients"
2014 <<"; not deleting yet"<<std::endl;
2016 obj->m_pending_deactivation = true;
2020 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
2021 <<"object id="<<id<<" is not known by clients"
2022 <<"; deleting"<<std::endl;
2024 // Tell the object about removal
2025 obj->removingFromEnvironment();
2026 // Deregister in scripting api
2027 m_script->removeObjectReference(obj);
2029 // Delete active object
2030 if(obj->environmentDeletes())
2032 // Id to be removed from m_active_objects
2033 objects_to_remove.push_back(id);
2036 // Remove references from m_active_objects
2037 for(std::list<u16>::iterator i = objects_to_remove.begin();
2038 i != objects_to_remove.end(); ++i)
2040 m_active_objects.erase(*i);
2047 #include "clientsimpleobject.h"
2053 ClientEnvironment::ClientEnvironment(ClientMap *map, scene::ISceneManager *smgr,
2054 ITextureSource *texturesource, IGameDef *gamedef,
2055 IrrlichtDevice *irr):
2058 m_texturesource(texturesource),
2064 ClientEnvironment::~ClientEnvironment()
2066 // delete active objects
2067 for(std::map<u16, ClientActiveObject*>::iterator
2068 i = m_active_objects.begin();
2069 i != m_active_objects.end(); ++i)
2074 for(std::list<ClientSimpleObject*>::iterator
2075 i = m_simple_objects.begin(); i != m_simple_objects.end(); ++i)
2084 Map & ClientEnvironment::getMap()
2089 ClientMap & ClientEnvironment::getClientMap()
2094 void ClientEnvironment::addPlayer(Player *player)
2096 DSTACK(__FUNCTION_NAME);
2098 It is a failure if player is local and there already is a local
2101 assert(!(player->isLocal() == true && getLocalPlayer() != NULL));
2103 Environment::addPlayer(player);
2106 LocalPlayer * ClientEnvironment::getLocalPlayer()
2108 for(std::list<Player*>::iterator i = m_players.begin();
2109 i != m_players.end(); ++i)
2111 Player *player = *i;
2112 if(player->isLocal())
2113 return (LocalPlayer*)player;
2118 void ClientEnvironment::step(float dtime)
2120 DSTACK(__FUNCTION_NAME);
2122 /* Step time of day */
2123 stepTimeOfDay(dtime);
2125 // Get some settings
2126 bool fly_allowed = m_gamedef->checkLocalPrivilege("fly");
2127 bool free_move = fly_allowed && g_settings->getBool("free_move");
2130 LocalPlayer *lplayer = getLocalPlayer();
2132 // collision info queue
2133 std::list<CollisionInfo> player_collisions;
2136 Get the speed the player is going
2138 bool is_climbing = lplayer->is_climbing;
2140 f32 player_speed = lplayer->getSpeed().getLength();
2143 Maximum position increment
2145 //f32 position_max_increment = 0.05*BS;
2146 f32 position_max_increment = 0.1*BS;
2148 // Maximum time increment (for collision detection etc)
2149 // time = distance / speed
2150 f32 dtime_max_increment = 1;
2151 if(player_speed > 0.001)
2152 dtime_max_increment = position_max_increment / player_speed;
2154 // Maximum time increment is 10ms or lower
2155 if(dtime_max_increment > 0.01)
2156 dtime_max_increment = 0.01;
2158 // Don't allow overly huge dtime
2162 f32 dtime_downcount = dtime;
2165 Stuff that has a maximum time increment
2174 if(dtime_downcount > dtime_max_increment)
2176 dtime_part = dtime_max_increment;
2177 dtime_downcount -= dtime_part;
2181 dtime_part = dtime_downcount;
2183 Setting this to 0 (no -=dtime_part) disables an infinite loop
2184 when dtime_part is so small that dtime_downcount -= dtime_part
2187 dtime_downcount = 0;
2196 if(free_move == false && is_climbing == false)
2199 v3f speed = lplayer->getSpeed();
2200 if(lplayer->in_liquid == false)
2201 speed.Y -= lplayer->movement_gravity * lplayer->physics_override_gravity * dtime_part * 2;
2203 // Liquid floating / sinking
2204 if(lplayer->in_liquid && !lplayer->swimming_vertical)
2205 speed.Y -= lplayer->movement_liquid_sink * dtime_part * 2;
2207 // Liquid resistance
2208 if(lplayer->in_liquid_stable || lplayer->in_liquid)
2210 // How much the node's viscosity blocks movement, ranges between 0 and 1
2211 // Should match the scale at which viscosity increase affects other liquid attributes
2212 const f32 viscosity_factor = 0.3;
2214 v3f d_wanted = -speed / lplayer->movement_liquid_fluidity;
2215 f32 dl = d_wanted.getLength();
2216 if(dl > lplayer->movement_liquid_fluidity_smooth)
2217 dl = lplayer->movement_liquid_fluidity_smooth;
2218 dl *= (lplayer->liquid_viscosity * viscosity_factor) + (1 - viscosity_factor);
2220 v3f d = d_wanted.normalize() * dl;
2224 if(speed.X > lplayer->movement_liquid_fluidity + lplayer->movement_liquid_fluidity_smooth) speed.X -= lplayer->movement_liquid_fluidity_smooth;
2225 if(speed.X < -lplayer->movement_liquid_fluidity - lplayer->movement_liquid_fluidity_smooth) speed.X += 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.Y < -lplayer->movement_liquid_fluidity - lplayer->movement_liquid_fluidity_smooth) speed.Y += 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;
2229 if(speed.Z < -lplayer->movement_liquid_fluidity - lplayer->movement_liquid_fluidity_smooth) speed.Z += lplayer->movement_liquid_fluidity_smooth;
2233 lplayer->setSpeed(speed);
2238 This also does collision detection.
2240 lplayer->move(dtime_part, this, position_max_increment,
2241 &player_collisions);
2244 while(dtime_downcount > 0.001);
2246 //std::cout<<"Looped "<<loopcount<<" times."<<std::endl;
2248 for(std::list<CollisionInfo>::iterator
2249 i = player_collisions.begin();
2250 i != player_collisions.end(); ++i)
2252 CollisionInfo &info = *i;
2253 v3f speed_diff = info.new_speed - info.old_speed;;
2254 // Handle only fall damage
2255 // (because otherwise walking against something in fast_move kills you)
2256 if(speed_diff.Y < 0 || info.old_speed.Y >= 0)
2258 // Get rid of other components
2261 f32 pre_factor = 1; // 1 hp per node/s
2262 f32 tolerance = BS*14; // 5 without damage
2263 f32 post_factor = 1; // 1 hp per node/s
2264 if(info.type == COLLISION_NODE)
2266 const ContentFeatures &f = m_gamedef->ndef()->
2267 get(m_map->getNodeNoEx(info.node_p));
2268 // Determine fall damage multiplier
2269 int addp = itemgroup_get(f.groups, "fall_damage_add_percent");
2270 pre_factor = 1.0 + (float)addp/100.0;
2272 float speed = pre_factor * speed_diff.getLength();
2273 if(speed > tolerance)
2275 f32 damage_f = (speed - tolerance)/BS * post_factor;
2276 u16 damage = (u16)(damage_f+0.5);
2278 damageLocalPlayer(damage, true);
2279 MtEvent *e = new SimpleTriggerEvent("PlayerFallingDamage");
2280 m_gamedef->event()->put(e);
2286 A quick draft of lava damage
2288 if(m_lava_hurt_interval.step(dtime, 1.0))
2290 v3f pf = lplayer->getPosition();
2292 // Feet, middle and head
2293 v3s16 p1 = floatToInt(pf + v3f(0, BS*0.1, 0), BS);
2294 MapNode n1 = m_map->getNodeNoEx(p1);
2295 v3s16 p2 = floatToInt(pf + v3f(0, BS*0.8, 0), BS);
2296 MapNode n2 = m_map->getNodeNoEx(p2);
2297 v3s16 p3 = floatToInt(pf + v3f(0, BS*1.6, 0), BS);
2298 MapNode n3 = m_map->getNodeNoEx(p3);
2300 u32 damage_per_second = 0;
2301 damage_per_second = MYMAX(damage_per_second,
2302 m_gamedef->ndef()->get(n1).damage_per_second);
2303 damage_per_second = MYMAX(damage_per_second,
2304 m_gamedef->ndef()->get(n2).damage_per_second);
2305 damage_per_second = MYMAX(damage_per_second,
2306 m_gamedef->ndef()->get(n3).damage_per_second);
2308 if(damage_per_second != 0)
2310 damageLocalPlayer(damage_per_second, true);
2317 if(m_drowning_interval.step(dtime, 2.0))
2319 v3f pf = lplayer->getPosition();
2322 v3s16 p = floatToInt(pf + v3f(0, BS*1.6, 0), BS);
2323 MapNode n = m_map->getNodeNoEx(p);
2324 ContentFeatures c = m_gamedef->ndef()->get(n);
2325 u8 drowning_damage = c.drowning;
2326 if(drowning_damage > 0 && lplayer->hp > 0){
2327 u16 breath = lplayer->getBreath();
2334 lplayer->setBreath(breath);
2335 updateLocalPlayerBreath(breath);
2338 if(lplayer->getBreath() == 0 && drowning_damage > 0){
2339 damageLocalPlayer(drowning_damage, true);
2342 if(m_breathing_interval.step(dtime, 0.5))
2344 v3f pf = lplayer->getPosition();
2347 v3s16 p = floatToInt(pf + v3f(0, BS*1.6, 0), BS);
2348 MapNode n = m_map->getNodeNoEx(p);
2349 ContentFeatures c = m_gamedef->ndef()->get(n);
2351 lplayer->setBreath(11);
2353 else if(c.drowning == 0){
2354 u16 breath = lplayer->getBreath();
2357 lplayer->setBreath(breath);
2358 updateLocalPlayerBreath(breath);
2364 Stuff that can be done in an arbitarily large dtime
2366 for(std::list<Player*>::iterator i = m_players.begin();
2367 i != m_players.end(); ++i)
2369 Player *player = *i;
2372 Handle non-local players
2374 if(player->isLocal() == false)
2377 player->move(dtime, *m_map, 100*BS);
2381 // Update lighting on all players on client
2385 v3s16 p = player->getLightPosition();
2386 MapNode n = m_map->getNode(p);
2387 light = n.getLightBlendF1((float)getDayNightRatio()/1000, m_gamedef->ndef());
2389 catch(InvalidPositionException &e){
2390 light = blend_light_f1((float)getDayNightRatio()/1000, LIGHT_SUN, 0);
2392 player->light = light;
2396 Step active objects and update lighting of them
2399 g_profiler->avg("CEnv: num of objects", m_active_objects.size());
2400 bool update_lighting = m_active_object_light_update_interval.step(dtime, 0.21);
2401 for(std::map<u16, ClientActiveObject*>::iterator
2402 i = m_active_objects.begin();
2403 i != m_active_objects.end(); ++i)
2405 ClientActiveObject* obj = i->second;
2407 obj->step(dtime, this);
2415 v3s16 p = obj->getLightPosition();
2416 MapNode n = m_map->getNode(p);
2417 light = n.getLightBlend(getDayNightRatio(), m_gamedef->ndef());
2419 catch(InvalidPositionException &e){
2420 light = blend_light(getDayNightRatio(), LIGHT_SUN, 0);
2422 obj->updateLight(light);
2427 Step and handle simple objects
2429 g_profiler->avg("CEnv: num of simple objects", m_simple_objects.size());
2430 for(std::list<ClientSimpleObject*>::iterator
2431 i = m_simple_objects.begin(); i != m_simple_objects.end();)
2433 ClientSimpleObject *simple = *i;
2434 std::list<ClientSimpleObject*>::iterator cur = i;
2436 simple->step(dtime);
2437 if(simple->m_to_be_removed){
2439 m_simple_objects.erase(cur);
2444 void ClientEnvironment::addSimpleObject(ClientSimpleObject *simple)
2446 m_simple_objects.push_back(simple);
2449 ClientActiveObject* ClientEnvironment::getActiveObject(u16 id)
2451 std::map<u16, ClientActiveObject*>::iterator n;
2452 n = m_active_objects.find(id);
2453 if(n == m_active_objects.end())
2458 bool isFreeClientActiveObjectId(u16 id,
2459 std::map<u16, ClientActiveObject*> &objects)
2464 return objects.find(id) == objects.end();
2467 u16 getFreeClientActiveObjectId(
2468 std::map<u16, ClientActiveObject*> &objects)
2470 //try to reuse id's as late as possible
2471 static u16 last_used_id = 0;
2472 u16 startid = last_used_id;
2476 if(isFreeClientActiveObjectId(last_used_id, objects))
2477 return last_used_id;
2479 if(last_used_id == startid)
2484 u16 ClientEnvironment::addActiveObject(ClientActiveObject *object)
2487 if(object->getId() == 0)
2489 u16 new_id = getFreeClientActiveObjectId(m_active_objects);
2492 infostream<<"ClientEnvironment::addActiveObject(): "
2493 <<"no free ids available"<<std::endl;
2497 object->setId(new_id);
2499 if(isFreeClientActiveObjectId(object->getId(), m_active_objects) == false)
2501 infostream<<"ClientEnvironment::addActiveObject(): "
2502 <<"id is not free ("<<object->getId()<<")"<<std::endl;
2506 infostream<<"ClientEnvironment::addActiveObject(): "
2507 <<"added (id="<<object->getId()<<")"<<std::endl;
2508 m_active_objects[object->getId()] = object;
2509 object->addToScene(m_smgr, m_texturesource, m_irr);
2510 { // Update lighting immediately
2514 v3s16 p = object->getLightPosition();
2515 MapNode n = m_map->getNode(p);
2516 light = n.getLightBlend(getDayNightRatio(), m_gamedef->ndef());
2518 catch(InvalidPositionException &e){
2519 light = blend_light(getDayNightRatio(), LIGHT_SUN, 0);
2521 object->updateLight(light);
2523 return object->getId();
2526 void ClientEnvironment::addActiveObject(u16 id, u8 type,
2527 const std::string &init_data)
2529 ClientActiveObject* obj =
2530 ClientActiveObject::create(type, m_gamedef, this);
2533 infostream<<"ClientEnvironment::addActiveObject(): "
2534 <<"id="<<id<<" type="<<type<<": Couldn't create object"
2543 obj->initialize(init_data);
2545 catch(SerializationError &e)
2547 errorstream<<"ClientEnvironment::addActiveObject():"
2548 <<" id="<<id<<" type="<<type
2549 <<": SerializationError in initialize(): "
2551 <<": init_data="<<serializeJsonString(init_data)
2555 addActiveObject(obj);
2558 void ClientEnvironment::removeActiveObject(u16 id)
2560 verbosestream<<"ClientEnvironment::removeActiveObject(): "
2561 <<"id="<<id<<std::endl;
2562 ClientActiveObject* obj = getActiveObject(id);
2565 infostream<<"ClientEnvironment::removeActiveObject(): "
2566 <<"id="<<id<<" not found"<<std::endl;
2569 obj->removeFromScene(true);
2571 m_active_objects.erase(id);
2574 void ClientEnvironment::processActiveObjectMessage(u16 id,
2575 const std::string &data)
2577 ClientActiveObject* obj = getActiveObject(id);
2580 infostream<<"ClientEnvironment::processActiveObjectMessage():"
2581 <<" got message for id="<<id<<", which doesn't exist."
2587 obj->processMessage(data);
2589 catch(SerializationError &e)
2591 errorstream<<"ClientEnvironment::processActiveObjectMessage():"
2592 <<" id="<<id<<" type="<<obj->getType()
2593 <<" SerializationError in processMessage(),"
2594 <<" message="<<serializeJsonString(data)
2600 Callbacks for activeobjects
2603 void ClientEnvironment::damageLocalPlayer(u8 damage, bool handle_hp)
2605 LocalPlayer *lplayer = getLocalPlayer();
2609 if(lplayer->hp > damage)
2610 lplayer->hp -= damage;
2615 ClientEnvEvent event;
2616 event.type = CEE_PLAYER_DAMAGE;
2617 event.player_damage.amount = damage;
2618 event.player_damage.send_to_server = handle_hp;
2619 m_client_event_queue.push_back(event);
2622 void ClientEnvironment::updateLocalPlayerBreath(u16 breath)
2624 ClientEnvEvent event;
2625 event.type = CEE_PLAYER_BREATH;
2626 event.player_breath.amount = breath;
2627 m_client_event_queue.push_back(event);
2631 Client likes to call these
2634 void ClientEnvironment::getActiveObjects(v3f origin, f32 max_d,
2635 std::vector<DistanceSortedActiveObject> &dest)
2637 for(std::map<u16, ClientActiveObject*>::iterator
2638 i = m_active_objects.begin();
2639 i != m_active_objects.end(); ++i)
2641 ClientActiveObject* obj = i->second;
2643 f32 d = (obj->getPosition() - origin).getLength();
2648 DistanceSortedActiveObject dso(obj, d);
2650 dest.push_back(dso);
2654 ClientEnvEvent ClientEnvironment::getClientEvent()
2656 ClientEnvEvent event;
2657 if(m_client_event_queue.empty())
2658 event.type = CEE_NONE;
2660 event = m_client_event_queue.front();
2661 m_client_event_queue.pop_front();
2666 #endif // #ifndef SERVER