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;
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)
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) {
381 void ServerEnvironment::serializePlayers(const std::string &savedir)
383 std::string players_path = savedir + "/players";
384 fs::CreateDir(players_path);
386 std::set<Player*> saved_players;
388 std::vector<fs::DirListNode> player_files = fs::GetDirListing(players_path);
389 for(u32 i=0; i<player_files.size(); i++)
391 if(player_files[i].dir || player_files[i].name[0] == '.')
394 // Full path to this file
395 std::string path = players_path + "/" + player_files[i].name;
397 //infostream<<"Checking player file "<<path<<std::endl;
399 // Load player to see what is its name
400 RemotePlayer testplayer(m_gamedef);
402 // Open file and deserialize
403 std::ifstream is(path.c_str(), std::ios_base::binary);
404 if(is.good() == false)
406 infostream<<"Failed to read "<<path<<std::endl;
409 testplayer.deSerialize(is, player_files[i].name);
412 //infostream<<"Loaded test player with name "<<testplayer.getName()<<std::endl;
414 // Search for the player
415 std::string playername = testplayer.getName();
416 Player *player = getPlayer(playername.c_str());
419 infostream<<"Didn't find matching player, ignoring file "<<path<<std::endl;
423 //infostream<<"Found matching player, overwriting."<<std::endl;
425 // OK, found. Save player there.
426 if(player->checkModified())
428 // Open file and serialize
429 std::ostringstream ss(std::ios_base::binary);
430 player->serialize(ss);
431 if(!fs::safeWriteToFile(path, ss.str()))
433 infostream<<"Failed to write "<<path<<std::endl;
436 saved_players.insert(player);
438 saved_players.insert(player);
442 for(std::list<Player*>::iterator i = m_players.begin();
443 i != m_players.end(); ++i)
446 if(saved_players.find(player) != saved_players.end())
448 /*infostream<<"Player "<<player->getName()
449 <<" was already saved."<<std::endl;*/
452 std::string playername = player->getName();
453 // Don't save unnamed player
456 //infostream<<"Not saving unnamed player."<<std::endl;
462 if(string_allowed(playername, PLAYERNAME_ALLOWED_CHARS) == false)
463 playername = "player";
464 std::string path = players_path + "/" + playername;
466 for(u32 i=0; i<1000; i++)
468 if(fs::PathExists(path) == false)
473 path = players_path + "/" + playername + itos(i);
477 infostream<<"Didn't find free file for player"<<std::endl;
482 /*infostream<<"Saving player "<<player->getName()<<" to "
484 // Open file and serialize
485 std::ostringstream ss(std::ios_base::binary);
486 player->serialize(ss);
487 if(!fs::safeWriteToFile(path, ss.str()))
489 infostream<<"Failed to write "<<path<<std::endl;
492 saved_players.insert(player);
496 //infostream<<"Saved "<<saved_players.size()<<" players."<<std::endl;
499 void ServerEnvironment::deSerializePlayers(const std::string &savedir)
501 std::string players_path = savedir + "/players";
503 std::vector<fs::DirListNode> player_files = fs::GetDirListing(players_path);
504 for(u32 i=0; i<player_files.size(); i++)
506 if(player_files[i].dir)
509 // Full path to this file
510 std::string path = players_path + "/" + player_files[i].name;
512 //infostream<<"Checking player file "<<path<<std::endl;
514 // Load player to see what is its name
515 RemotePlayer testplayer(m_gamedef);
517 // Open file and deserialize
518 std::ifstream is(path.c_str(), std::ios_base::binary);
519 if(is.good() == false)
521 infostream<<"Failed to read "<<path<<std::endl;
524 testplayer.deSerialize(is, player_files[i].name);
527 if(!string_allowed(testplayer.getName(), PLAYERNAME_ALLOWED_CHARS))
529 infostream<<"Not loading player with invalid name: "
530 <<testplayer.getName()<<std::endl;
533 /*infostream<<"Loaded test player with name "<<testplayer.getName()
536 // Search for the player
537 std::string playername = testplayer.getName();
538 Player *player = getPlayer(playername.c_str());
539 bool newplayer = false;
542 //infostream<<"Is a new player"<<std::endl;
543 player = new RemotePlayer(m_gamedef);
549 verbosestream<<"Reading player "<<testplayer.getName()<<" from "
551 // Open file and deserialize
552 std::ifstream is(path.c_str(), std::ios_base::binary);
553 if(is.good() == false)
555 infostream<<"Failed to read "<<path<<std::endl;
558 player->deSerialize(is, player_files[i].name);
568 void ServerEnvironment::saveMeta(const std::string &savedir)
570 std::string path = savedir + "/env_meta.txt";
572 // Open file and serialize
573 std::ostringstream ss(std::ios_base::binary);
576 args.setU64("game_time", m_game_time);
577 args.setU64("time_of_day", getTimeOfDay());
581 if(!fs::safeWriteToFile(path, ss.str()))
583 infostream<<"ServerEnvironment::saveMeta(): Failed to write "
585 throw SerializationError("Couldn't save env meta");
589 void ServerEnvironment::loadMeta(const std::string &savedir)
591 std::string path = savedir + "/env_meta.txt";
593 // Open file and deserialize
594 std::ifstream is(path.c_str(), std::ios_base::binary);
595 if(is.good() == false)
597 infostream<<"ServerEnvironment::loadMeta(): Failed to open "
599 throw SerializationError("Couldn't load env meta");
607 throw SerializationError
608 ("ServerEnvironment::loadMeta(): EnvArgsEnd not found");
610 std::getline(is, line);
611 std::string trimmedline = trim(line);
612 if(trimmedline == "EnvArgsEnd")
614 args.parseConfigLine(line);
618 m_game_time = args.getU64("game_time");
619 }catch(SettingNotFoundException &e){
620 // Getting this is crucial, otherwise timestamps are useless
621 throw SerializationError("Couldn't load env meta game_time");
625 m_time_of_day = args.getU64("time_of_day");
626 }catch(SettingNotFoundException &e){
627 // This is not as important
628 m_time_of_day = 9000;
634 ActiveBlockModifier *abm;
636 std::set<content_t> required_neighbors;
642 ServerEnvironment *m_env;
643 std::map<content_t, std::list<ActiveABM> > m_aabms;
645 ABMHandler(std::list<ABMWithState> &abms,
646 float dtime_s, ServerEnvironment *env,
652 INodeDefManager *ndef = env->getGameDef()->ndef();
653 for(std::list<ABMWithState>::iterator
654 i = abms.begin(); i != abms.end(); ++i){
655 ActiveBlockModifier *abm = i->abm;
656 float trigger_interval = abm->getTriggerInterval();
657 if(trigger_interval < 0.001)
658 trigger_interval = 0.001;
659 float actual_interval = dtime_s;
662 if(i->timer < trigger_interval)
664 i->timer -= trigger_interval;
665 actual_interval = trigger_interval;
667 float intervals = actual_interval / trigger_interval;
670 float chance = abm->getTriggerChance();
675 aabm.chance = chance / intervals;
679 std::set<std::string> required_neighbors_s
680 = abm->getRequiredNeighbors();
681 for(std::set<std::string>::iterator
682 i = required_neighbors_s.begin();
683 i != required_neighbors_s.end(); i++)
685 ndef->getIds(*i, aabm.required_neighbors);
688 std::set<std::string> contents_s = abm->getTriggerContents();
689 for(std::set<std::string>::iterator
690 i = contents_s.begin(); i != contents_s.end(); i++)
692 std::set<content_t> ids;
693 ndef->getIds(*i, ids);
694 for(std::set<content_t>::const_iterator k = ids.begin();
698 std::map<content_t, std::list<ActiveABM> >::iterator j;
700 if(j == m_aabms.end()){
701 std::list<ActiveABM> aabmlist;
702 m_aabms[c] = aabmlist;
705 j->second.push_back(aabm);
710 void apply(MapBlock *block)
715 ServerMap *map = &m_env->getServerMap();
718 for(p0.X=0; p0.X<MAP_BLOCKSIZE; p0.X++)
719 for(p0.Y=0; p0.Y<MAP_BLOCKSIZE; p0.Y++)
720 for(p0.Z=0; p0.Z<MAP_BLOCKSIZE; p0.Z++)
722 MapNode n = block->getNodeNoEx(p0);
723 content_t c = n.getContent();
724 v3s16 p = p0 + block->getPosRelative();
726 std::map<content_t, std::list<ActiveABM> >::iterator j;
728 if(j == m_aabms.end())
731 for(std::list<ActiveABM>::iterator
732 i = j->second.begin(); i != j->second.end(); i++)
734 if(myrand() % i->chance != 0)
738 if(!i->required_neighbors.empty())
741 for(p1.X = p.X-1; p1.X <= p.X+1; p1.X++)
742 for(p1.Y = p.Y-1; p1.Y <= p.Y+1; p1.Y++)
743 for(p1.Z = p.Z-1; p1.Z <= p.Z+1; p1.Z++)
747 MapNode n = map->getNodeNoEx(p1);
748 content_t c = n.getContent();
749 std::set<content_t>::const_iterator k;
750 k = i->required_neighbors.find(c);
751 if(k != i->required_neighbors.end()){
755 // No required neighbor found
760 // Find out how many objects the block contains
761 u32 active_object_count = block->m_static_objects.m_active.size();
762 // Find out how many objects this and all the neighbors contain
763 u32 active_object_count_wider = 0;
764 u32 wider_unknown_count = 0;
765 for(s16 x=-1; x<=1; x++)
766 for(s16 y=-1; y<=1; y++)
767 for(s16 z=-1; z<=1; z++)
769 MapBlock *block2 = map->getBlockNoCreateNoEx(
770 block->getPos() + v3s16(x,y,z));
772 wider_unknown_count = 0;
775 active_object_count_wider +=
776 block2->m_static_objects.m_active.size()
777 + block2->m_static_objects.m_stored.size();
780 u32 wider_known_count = 3*3*3 - wider_unknown_count;
781 active_object_count_wider += wider_unknown_count * active_object_count_wider / wider_known_count;
783 // Call all the trigger variations
784 i->abm->trigger(m_env, p, n);
785 i->abm->trigger(m_env, p, n,
786 active_object_count, active_object_count_wider);
792 void ServerEnvironment::activateBlock(MapBlock *block, u32 additional_dtime)
794 // Get time difference
796 u32 stamp = block->getTimestamp();
797 if(m_game_time > stamp && stamp != BLOCK_TIMESTAMP_UNDEFINED)
798 dtime_s = m_game_time - block->getTimestamp();
799 dtime_s += additional_dtime;
801 /*infostream<<"ServerEnvironment::activateBlock(): block timestamp: "
802 <<stamp<<", game time: "<<m_game_time<<std::endl;*/
804 // Set current time as timestamp
805 block->setTimestampNoChangedFlag(m_game_time);
807 /*infostream<<"ServerEnvironment::activateBlock(): block is "
808 <<dtime_s<<" seconds old."<<std::endl;*/
810 // Activate stored objects
811 activateObjects(block, dtime_s);
814 std::map<v3s16, NodeTimer> elapsed_timers =
815 block->m_node_timers.step((float)dtime_s);
816 if(!elapsed_timers.empty()){
818 for(std::map<v3s16, NodeTimer>::iterator
819 i = elapsed_timers.begin();
820 i != elapsed_timers.end(); i++){
821 n = block->getNodeNoEx(i->first);
822 v3s16 p = i->first + block->getPosRelative();
823 if(m_script->node_on_timer(p,n,i->second.elapsed))
824 block->setNodeTimer(i->first,NodeTimer(i->second.timeout,0));
828 /* Handle ActiveBlockModifiers */
829 ABMHandler abmhandler(m_abms, dtime_s, this, false);
830 abmhandler.apply(block);
833 void ServerEnvironment::addActiveBlockModifier(ActiveBlockModifier *abm)
835 m_abms.push_back(ABMWithState(abm));
838 bool ServerEnvironment::setNode(v3s16 p, const MapNode &n)
840 INodeDefManager *ndef = m_gamedef->ndef();
841 MapNode n_old = m_map->getNodeNoEx(p);
843 if(ndef->get(n_old).has_on_destruct)
844 m_script->node_on_destruct(p, n_old);
846 bool succeeded = m_map->addNodeWithEvent(p, n);
849 // Call post-destructor
850 if(ndef->get(n_old).has_after_destruct)
851 m_script->node_after_destruct(p, n_old);
853 if(ndef->get(n).has_on_construct)
854 m_script->node_on_construct(p, n);
858 bool ServerEnvironment::removeNode(v3s16 p)
860 INodeDefManager *ndef = m_gamedef->ndef();
861 MapNode n_old = m_map->getNodeNoEx(p);
863 if(ndef->get(n_old).has_on_destruct)
864 m_script->node_on_destruct(p, n_old);
866 // This is slightly optimized compared to addNodeWithEvent(air)
867 bool succeeded = m_map->removeNodeWithEvent(p);
870 // Call post-destructor
871 if(ndef->get(n_old).has_after_destruct)
872 m_script->node_after_destruct(p, n_old);
873 // Air doesn't require constructor
877 std::set<u16> ServerEnvironment::getObjectsInsideRadius(v3f pos, float radius)
879 std::set<u16> objects;
880 for(std::map<u16, ServerActiveObject*>::iterator
881 i = m_active_objects.begin();
882 i != m_active_objects.end(); ++i)
884 ServerActiveObject* obj = i->second;
886 v3f objectpos = obj->getBasePosition();
887 if(objectpos.getDistanceFrom(pos) > radius)
894 void ServerEnvironment::clearAllObjects()
896 infostream<<"ServerEnvironment::clearAllObjects(): "
897 <<"Removing all active objects"<<std::endl;
898 std::list<u16> objects_to_remove;
899 for(std::map<u16, ServerActiveObject*>::iterator
900 i = m_active_objects.begin();
901 i != m_active_objects.end(); ++i)
903 ServerActiveObject* obj = i->second;
904 if(obj->getType() == ACTIVEOBJECT_TYPE_PLAYER)
907 // Delete static object if block is loaded
908 if(obj->m_static_exists){
909 MapBlock *block = m_map->getBlockNoCreateNoEx(obj->m_static_block);
911 block->m_static_objects.remove(id);
912 block->raiseModified(MOD_STATE_WRITE_NEEDED,
914 obj->m_static_exists = false;
917 // If known by some client, don't delete immediately
918 if(obj->m_known_by_count > 0){
919 obj->m_pending_deactivation = true;
920 obj->m_removed = true;
924 // Tell the object about removal
925 obj->removingFromEnvironment();
926 // Deregister in scripting api
927 m_script->removeObjectReference(obj);
929 // Delete active object
930 if(obj->environmentDeletes())
932 // Id to be removed from m_active_objects
933 objects_to_remove.push_back(id);
935 // Remove references from m_active_objects
936 for(std::list<u16>::iterator i = objects_to_remove.begin();
937 i != objects_to_remove.end(); ++i)
939 m_active_objects.erase(*i);
942 // Get list of loaded blocks
943 std::list<v3s16> loaded_blocks;
944 infostream<<"ServerEnvironment::clearAllObjects(): "
945 <<"Listing all loaded blocks"<<std::endl;
946 m_map->listAllLoadedBlocks(loaded_blocks);
947 infostream<<"ServerEnvironment::clearAllObjects(): "
948 <<"Done listing all loaded blocks: "
949 <<loaded_blocks.size()<<std::endl;
951 // Get list of loadable blocks
952 std::list<v3s16> loadable_blocks;
953 infostream<<"ServerEnvironment::clearAllObjects(): "
954 <<"Listing all loadable blocks"<<std::endl;
955 m_map->listAllLoadableBlocks(loadable_blocks);
956 infostream<<"ServerEnvironment::clearAllObjects(): "
957 <<"Done listing all loadable blocks: "
958 <<loadable_blocks.size()
959 <<", now clearing"<<std::endl;
961 // Grab a reference on each loaded block to avoid unloading it
962 for(std::list<v3s16>::iterator i = loaded_blocks.begin();
963 i != loaded_blocks.end(); ++i)
966 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
971 // Remove objects in all loadable blocks
972 u32 unload_interval = g_settings->getS32("max_clearobjects_extra_loaded_blocks");
973 unload_interval = MYMAX(unload_interval, 1);
974 u32 report_interval = loadable_blocks.size() / 10;
975 u32 num_blocks_checked = 0;
976 u32 num_blocks_cleared = 0;
977 u32 num_objs_cleared = 0;
978 for(std::list<v3s16>::iterator i = loadable_blocks.begin();
979 i != loadable_blocks.end(); ++i)
982 MapBlock *block = m_map->emergeBlock(p, false);
984 errorstream<<"ServerEnvironment::clearAllObjects(): "
985 <<"Failed to emerge block "<<PP(p)<<std::endl;
988 u32 num_stored = block->m_static_objects.m_stored.size();
989 u32 num_active = block->m_static_objects.m_active.size();
990 if(num_stored != 0 || num_active != 0){
991 block->m_static_objects.m_stored.clear();
992 block->m_static_objects.m_active.clear();
993 block->raiseModified(MOD_STATE_WRITE_NEEDED,
995 num_objs_cleared += num_stored + num_active;
996 num_blocks_cleared++;
998 num_blocks_checked++;
1000 if(num_blocks_checked % report_interval == 0){
1001 float percent = 100.0 * (float)num_blocks_checked /
1002 loadable_blocks.size();
1003 infostream<<"ServerEnvironment::clearAllObjects(): "
1004 <<"Cleared "<<num_objs_cleared<<" objects"
1005 <<" in "<<num_blocks_cleared<<" blocks ("
1006 <<percent<<"%)"<<std::endl;
1008 if(num_blocks_checked % unload_interval == 0){
1009 m_map->unloadUnreferencedBlocks();
1012 m_map->unloadUnreferencedBlocks();
1014 // Drop references that were added above
1015 for(std::list<v3s16>::iterator i = loaded_blocks.begin();
1016 i != loaded_blocks.end(); ++i)
1019 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1024 infostream<<"ServerEnvironment::clearAllObjects(): "
1025 <<"Finished: Cleared "<<num_objs_cleared<<" objects"
1026 <<" in "<<num_blocks_cleared<<" blocks"<<std::endl;
1029 void ServerEnvironment::step(float dtime)
1031 DSTACK(__FUNCTION_NAME);
1033 //TimeTaker timer("ServerEnv step");
1035 /* Step time of day */
1036 stepTimeOfDay(dtime);
1039 // NOTE: This is kind of funny on a singleplayer game, but doesn't
1040 // really matter that much.
1041 m_recommended_send_interval = g_settings->getFloat("dedicated_server_step");
1047 m_game_time_fraction_counter += dtime;
1048 u32 inc_i = (u32)m_game_time_fraction_counter;
1049 m_game_time += inc_i;
1050 m_game_time_fraction_counter -= (float)inc_i;
1057 ScopeProfiler sp(g_profiler, "SEnv: handle players avg", SPT_AVG);
1058 for(std::list<Player*>::iterator i = m_players.begin();
1059 i != m_players.end(); ++i)
1061 Player *player = *i;
1063 // Ignore disconnected players
1064 if(player->peer_id == 0)
1068 player->move(dtime, *m_map, 100*BS);
1073 Manage active block list
1075 if(m_active_blocks_management_interval.step(dtime, 2.0))
1077 ScopeProfiler sp(g_profiler, "SEnv: manage act. block list avg /2s", SPT_AVG);
1079 Get player block positions
1081 std::list<v3s16> players_blockpos;
1082 for(std::list<Player*>::iterator
1083 i = m_players.begin();
1084 i != m_players.end(); ++i)
1086 Player *player = *i;
1087 // Ignore disconnected players
1088 if(player->peer_id == 0)
1090 v3s16 blockpos = getNodeBlockPos(
1091 floatToInt(player->getPosition(), BS));
1092 players_blockpos.push_back(blockpos);
1096 Update list of active blocks, collecting changes
1098 const s16 active_block_range = g_settings->getS16("active_block_range");
1099 std::set<v3s16> blocks_removed;
1100 std::set<v3s16> blocks_added;
1101 m_active_blocks.update(players_blockpos, active_block_range,
1102 blocks_removed, blocks_added);
1105 Handle removed blocks
1108 // Convert active objects that are no more in active blocks to static
1109 deactivateFarObjects(false);
1111 for(std::set<v3s16>::iterator
1112 i = blocks_removed.begin();
1113 i != blocks_removed.end(); ++i)
1117 /* infostream<<"Server: Block " << PP(p)
1118 << " became inactive"<<std::endl; */
1120 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1124 // Set current time as timestamp (and let it set ChangedFlag)
1125 block->setTimestamp(m_game_time);
1132 for(std::set<v3s16>::iterator
1133 i = blocks_added.begin();
1134 i != blocks_added.end(); ++i)
1138 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1140 // Block needs to be fetched first
1141 m_emerger->enqueueBlockEmerge(
1142 PEER_ID_INEXISTENT, p, false);
1143 m_active_blocks.m_list.erase(p);
1147 activateBlock(block);
1148 /* infostream<<"Server: Block " << PP(p)
1149 << " became active"<<std::endl; */
1154 Mess around in active blocks
1156 if(m_active_blocks_nodemetadata_interval.step(dtime, 1.0))
1158 ScopeProfiler sp(g_profiler, "SEnv: mess in act. blocks avg /1s", SPT_AVG);
1162 for(std::set<v3s16>::iterator
1163 i = m_active_blocks.m_list.begin();
1164 i != m_active_blocks.m_list.end(); ++i)
1168 /*infostream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
1169 <<") being handled"<<std::endl;*/
1171 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1175 // Reset block usage timer
1176 block->resetUsageTimer();
1178 // Set current time as timestamp
1179 block->setTimestampNoChangedFlag(m_game_time);
1180 // If time has changed much from the one on disk,
1181 // set block to be saved when it is unloaded
1182 if(block->getTimestamp() > block->getDiskTimestamp() + 60)
1183 block->raiseModified(MOD_STATE_WRITE_AT_UNLOAD,
1184 "Timestamp older than 60s (step)");
1187 std::map<v3s16, NodeTimer> elapsed_timers =
1188 block->m_node_timers.step((float)dtime);
1189 if(!elapsed_timers.empty()){
1191 for(std::map<v3s16, NodeTimer>::iterator
1192 i = elapsed_timers.begin();
1193 i != elapsed_timers.end(); i++){
1194 n = block->getNodeNoEx(i->first);
1195 p = i->first + block->getPosRelative();
1196 if(m_script->node_on_timer(p,n,i->second.elapsed))
1197 block->setNodeTimer(i->first,NodeTimer(i->second.timeout,0));
1203 const float abm_interval = 1.0;
1204 if(m_active_block_modifier_interval.step(dtime, abm_interval))
1206 if(m_active_block_interval_overload_skip > 0){
1207 ScopeProfiler sp(g_profiler, "SEnv: ABM overload skips");
1208 m_active_block_interval_overload_skip--;
1211 ScopeProfiler sp(g_profiler, "SEnv: modify in blocks avg /1s", SPT_AVG);
1212 TimeTaker timer("modify in active blocks");
1214 // Initialize handling of ActiveBlockModifiers
1215 ABMHandler abmhandler(m_abms, abm_interval, this, true);
1217 for(std::set<v3s16>::iterator
1218 i = m_active_blocks.m_list.begin();
1219 i != m_active_blocks.m_list.end(); ++i)
1223 /*infostream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
1224 <<") being handled"<<std::endl;*/
1226 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1230 // Set current time as timestamp
1231 block->setTimestampNoChangedFlag(m_game_time);
1233 /* Handle ActiveBlockModifiers */
1234 abmhandler.apply(block);
1237 u32 time_ms = timer.stop(true);
1238 u32 max_time_ms = 200;
1239 if(time_ms > max_time_ms){
1240 infostream<<"WARNING: active block modifiers took "
1241 <<time_ms<<"ms (longer than "
1242 <<max_time_ms<<"ms)"<<std::endl;
1243 m_active_block_interval_overload_skip = (time_ms / max_time_ms) + 1;
1248 Step script environment (run global on_step())
1250 m_script->environment_Step(dtime);
1256 ScopeProfiler sp(g_profiler, "SEnv: step act. objs avg", SPT_AVG);
1257 //TimeTaker timer("Step active objects");
1259 g_profiler->avg("SEnv: num of objects", m_active_objects.size());
1261 // This helps the objects to send data at the same time
1262 bool send_recommended = false;
1263 m_send_recommended_timer += dtime;
1264 if(m_send_recommended_timer > getSendRecommendedInterval())
1266 m_send_recommended_timer -= getSendRecommendedInterval();
1267 send_recommended = true;
1270 for(std::map<u16, ServerActiveObject*>::iterator
1271 i = m_active_objects.begin();
1272 i != m_active_objects.end(); ++i)
1274 ServerActiveObject* obj = i->second;
1275 // Remove non-peaceful mobs on peaceful mode
1276 if(g_settings->getBool("only_peaceful_mobs")){
1277 if(!obj->isPeaceful())
1278 obj->m_removed = true;
1280 // Don't step if is to be removed or stored statically
1281 if(obj->m_removed || obj->m_pending_deactivation)
1284 obj->step(dtime, send_recommended);
1285 // Read messages from object
1286 while(!obj->m_messages_out.empty())
1288 m_active_object_messages.push_back(
1289 obj->m_messages_out.pop_front());
1295 Manage active objects
1297 if(m_object_management_interval.step(dtime, 0.5))
1299 ScopeProfiler sp(g_profiler, "SEnv: remove removed objs avg /.5s", SPT_AVG);
1301 Remove objects that satisfy (m_removed && m_known_by_count==0)
1303 removeRemovedObjects();
1307 ServerActiveObject* ServerEnvironment::getActiveObject(u16 id)
1309 std::map<u16, ServerActiveObject*>::iterator n;
1310 n = m_active_objects.find(id);
1311 if(n == m_active_objects.end())
1316 bool isFreeServerActiveObjectId(u16 id,
1317 std::map<u16, ServerActiveObject*> &objects)
1322 return objects.find(id) == objects.end();
1325 u16 getFreeServerActiveObjectId(
1326 std::map<u16, ServerActiveObject*> &objects)
1328 //try to reuse id's as late as possible
1329 static u16 last_used_id = 0;
1330 u16 startid = last_used_id;
1334 if(isFreeServerActiveObjectId(last_used_id, objects))
1335 return last_used_id;
1337 if(last_used_id == startid)
1342 u16 ServerEnvironment::addActiveObject(ServerActiveObject *object)
1345 u16 id = addActiveObjectRaw(object, true, 0);
1350 bool ServerEnvironment::addActiveObjectAsStatic(ServerActiveObject *obj)
1354 v3f objectpos = obj->getBasePosition();
1356 // The block in which the object resides in
1357 v3s16 blockpos_o = getNodeBlockPos(floatToInt(objectpos, BS));
1360 Update the static data
1363 // Create new static object
1364 std::string staticdata = obj->getStaticData();
1365 StaticObject s_obj(obj->getType(), objectpos, staticdata);
1366 // Add to the block where the object is located in
1367 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1368 // Get or generate the block
1369 MapBlock *block = m_map->emergeBlock(blockpos);
1371 bool succeeded = false;
1375 block->m_static_objects.insert(0, s_obj);
1376 block->raiseModified(MOD_STATE_WRITE_AT_UNLOAD,
1377 "addActiveObjectAsStatic");
1381 infostream<<"ServerEnvironment::addActiveObjectAsStatic: "
1382 <<"Could not find or generate "
1383 <<"a block for storing static object"<<std::endl;
1387 if(obj->environmentDeletes())
1395 Finds out what new objects have been added to
1396 inside a radius around a position
1398 void ServerEnvironment::getAddedActiveObjects(v3s16 pos, s16 radius,
1399 std::set<u16> ¤t_objects,
1400 std::set<u16> &added_objects)
1402 v3f pos_f = intToFloat(pos, BS);
1403 f32 radius_f = radius * BS;
1405 Go through the object list,
1406 - discard m_removed objects,
1407 - discard objects that are too far away,
1408 - discard objects that are found in current_objects.
1409 - add remaining objects to added_objects
1411 for(std::map<u16, ServerActiveObject*>::iterator
1412 i = m_active_objects.begin();
1413 i != m_active_objects.end(); ++i)
1417 ServerActiveObject *object = i->second;
1420 // Discard if removed or deactivating
1421 if(object->m_removed || object->m_pending_deactivation)
1423 if(object->unlimitedTransferDistance() == false){
1424 // Discard if too far
1425 f32 distance_f = object->getBasePosition().getDistanceFrom(pos_f);
1426 if(distance_f > radius_f)
1429 // Discard if already on current_objects
1430 std::set<u16>::iterator n;
1431 n = current_objects.find(id);
1432 if(n != current_objects.end())
1434 // Add to added_objects
1435 added_objects.insert(id);
1440 Finds out what objects have been removed from
1441 inside a radius around a position
1443 void ServerEnvironment::getRemovedActiveObjects(v3s16 pos, s16 radius,
1444 std::set<u16> ¤t_objects,
1445 std::set<u16> &removed_objects)
1447 v3f pos_f = intToFloat(pos, BS);
1448 f32 radius_f = radius * BS;
1450 Go through current_objects; object is removed if:
1451 - object is not found in m_active_objects (this is actually an
1452 error condition; objects should be set m_removed=true and removed
1453 only after all clients have been informed about removal), or
1454 - object has m_removed=true, or
1455 - object is too far away
1457 for(std::set<u16>::iterator
1458 i = current_objects.begin();
1459 i != current_objects.end(); ++i)
1462 ServerActiveObject *object = getActiveObject(id);
1465 infostream<<"ServerEnvironment::getRemovedActiveObjects():"
1466 <<" object in current_objects is NULL"<<std::endl;
1467 removed_objects.insert(id);
1471 if(object->m_removed || object->m_pending_deactivation)
1473 removed_objects.insert(id);
1477 // If transfer distance is unlimited, don't remove
1478 if(object->unlimitedTransferDistance())
1481 f32 distance_f = object->getBasePosition().getDistanceFrom(pos_f);
1483 if(distance_f >= radius_f)
1485 removed_objects.insert(id);
1493 ActiveObjectMessage ServerEnvironment::getActiveObjectMessage()
1495 if(m_active_object_messages.empty())
1496 return ActiveObjectMessage(0);
1498 ActiveObjectMessage message = m_active_object_messages.front();
1499 m_active_object_messages.pop_front();
1504 ************ Private methods *************
1507 u16 ServerEnvironment::addActiveObjectRaw(ServerActiveObject *object,
1508 bool set_changed, u32 dtime_s)
1511 if(object->getId() == 0){
1512 u16 new_id = getFreeServerActiveObjectId(m_active_objects);
1515 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1516 <<"no free ids available"<<std::endl;
1517 if(object->environmentDeletes())
1521 object->setId(new_id);
1524 verbosestream<<"ServerEnvironment::addActiveObjectRaw(): "
1525 <<"supplied with id "<<object->getId()<<std::endl;
1527 if(isFreeServerActiveObjectId(object->getId(), m_active_objects) == false)
1529 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1530 <<"id is not free ("<<object->getId()<<")"<<std::endl;
1531 if(object->environmentDeletes())
1535 /*infostream<<"ServerEnvironment::addActiveObjectRaw(): "
1536 <<"added (id="<<object->getId()<<")"<<std::endl;*/
1538 m_active_objects[object->getId()] = object;
1540 verbosestream<<"ServerEnvironment::addActiveObjectRaw(): "
1541 <<"Added id="<<object->getId()<<"; there are now "
1542 <<m_active_objects.size()<<" active objects."
1545 // Register reference in scripting api (must be done before post-init)
1546 m_script->addObjectReference(object);
1547 // Post-initialize object
1548 object->addedToEnvironment(dtime_s);
1550 // Add static data to block
1551 if(object->isStaticAllowed())
1553 // Add static object to active static list of the block
1554 v3f objectpos = object->getBasePosition();
1555 std::string staticdata = object->getStaticData();
1556 StaticObject s_obj(object->getType(), objectpos, staticdata);
1557 // Add to the block where the object is located in
1558 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1559 MapBlock *block = m_map->emergeBlock(blockpos);
1561 block->m_static_objects.m_active[object->getId()] = s_obj;
1562 object->m_static_exists = true;
1563 object->m_static_block = blockpos;
1566 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1567 "addActiveObjectRaw");
1569 v3s16 p = floatToInt(objectpos, BS);
1570 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1571 <<"could not emerge block for storing id="<<object->getId()
1572 <<" statically (pos="<<PP(p)<<")"<<std::endl;
1576 return object->getId();
1580 Remove objects that satisfy (m_removed && m_known_by_count==0)
1582 void ServerEnvironment::removeRemovedObjects()
1584 std::list<u16> objects_to_remove;
1585 for(std::map<u16, ServerActiveObject*>::iterator
1586 i = m_active_objects.begin();
1587 i != m_active_objects.end(); ++i)
1590 ServerActiveObject* obj = i->second;
1591 // This shouldn't happen but check it
1594 infostream<<"NULL object found in ServerEnvironment"
1595 <<" while finding removed objects. id="<<id<<std::endl;
1596 // Id to be removed from m_active_objects
1597 objects_to_remove.push_back(id);
1602 We will delete objects that are marked as removed or thatare
1603 waiting for deletion after deactivation
1605 if(obj->m_removed == false && obj->m_pending_deactivation == false)
1609 Delete static data from block if is marked as removed
1611 if(obj->m_static_exists && obj->m_removed)
1613 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
1615 block->m_static_objects.remove(id);
1616 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1617 "removeRemovedObjects/remove");
1618 obj->m_static_exists = false;
1620 infostream<<"Failed to emerge block from which an object to "
1621 <<"be removed was loaded from. id="<<id<<std::endl;
1625 // If m_known_by_count > 0, don't actually remove. On some future
1626 // invocation this will be 0, which is when removal will continue.
1627 if(obj->m_known_by_count > 0)
1631 Move static data from active to stored if not marked as removed
1633 if(obj->m_static_exists && !obj->m_removed){
1634 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
1636 std::map<u16, StaticObject>::iterator i =
1637 block->m_static_objects.m_active.find(id);
1638 if(i != block->m_static_objects.m_active.end()){
1639 block->m_static_objects.m_stored.push_back(i->second);
1640 block->m_static_objects.m_active.erase(id);
1641 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1642 "removeRemovedObjects/deactivate");
1645 infostream<<"Failed to emerge block from which an object to "
1646 <<"be deactivated was loaded from. id="<<id<<std::endl;
1650 // Tell the object about removal
1651 obj->removingFromEnvironment();
1652 // Deregister in scripting api
1653 m_script->removeObjectReference(obj);
1656 if(obj->environmentDeletes())
1658 // Id to be removed from m_active_objects
1659 objects_to_remove.push_back(id);
1661 // Remove references from m_active_objects
1662 for(std::list<u16>::iterator i = objects_to_remove.begin();
1663 i != objects_to_remove.end(); ++i)
1665 m_active_objects.erase(*i);
1669 static void print_hexdump(std::ostream &o, const std::string &data)
1671 const int linelength = 16;
1672 for(int l=0; ; l++){
1673 int i0 = linelength * l;
1674 bool at_end = false;
1675 int thislinelength = linelength;
1676 if(i0 + thislinelength > (int)data.size()){
1677 thislinelength = data.size() - i0;
1680 for(int di=0; di<linelength; di++){
1683 if(di<thislinelength)
1684 snprintf(buf, 4, "%.2x ", data[i]);
1686 snprintf(buf, 4, " ");
1690 for(int di=0; di<thislinelength; di++){
1704 Convert stored objects from blocks near the players to active.
1706 void ServerEnvironment::activateObjects(MapBlock *block, u32 dtime_s)
1710 // Ignore if no stored objects (to not set changed flag)
1711 if(block->m_static_objects.m_stored.size() == 0)
1713 verbosestream<<"ServerEnvironment::activateObjects(): "
1714 <<"activating objects of block "<<PP(block->getPos())
1715 <<" ("<<block->m_static_objects.m_stored.size()
1716 <<" objects)"<<std::endl;
1717 bool large_amount = (block->m_static_objects.m_stored.size() > g_settings->getU16("max_objects_per_block"));
1719 errorstream<<"suspiciously large amount of objects detected: "
1720 <<block->m_static_objects.m_stored.size()<<" in "
1721 <<PP(block->getPos())
1722 <<"; removing all of them."<<std::endl;
1723 // Clear stored list
1724 block->m_static_objects.m_stored.clear();
1725 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1726 "stored list cleared in activateObjects due to "
1727 "large amount of objects");
1731 // Activate stored objects
1732 std::list<StaticObject> new_stored;
1733 for(std::list<StaticObject>::iterator
1734 i = block->m_static_objects.m_stored.begin();
1735 i != block->m_static_objects.m_stored.end(); ++i)
1737 /*infostream<<"Server: Creating an active object from "
1738 <<"static data"<<std::endl;*/
1739 StaticObject &s_obj = *i;
1740 // Create an active object from the data
1741 ServerActiveObject *obj = ServerActiveObject::create
1742 (s_obj.type, this, 0, s_obj.pos, s_obj.data);
1743 // If couldn't create object, store static data back.
1746 errorstream<<"ServerEnvironment::activateObjects(): "
1747 <<"failed to create active object from static object "
1748 <<"in block "<<PP(s_obj.pos/BS)
1749 <<" type="<<(int)s_obj.type<<" data:"<<std::endl;
1750 print_hexdump(verbosestream, s_obj.data);
1752 new_stored.push_back(s_obj);
1755 verbosestream<<"ServerEnvironment::activateObjects(): "
1756 <<"activated static object pos="<<PP(s_obj.pos/BS)
1757 <<" type="<<(int)s_obj.type<<std::endl;
1758 // This will also add the object to the active static list
1759 addActiveObjectRaw(obj, false, dtime_s);
1761 // Clear stored list
1762 block->m_static_objects.m_stored.clear();
1763 // Add leftover failed stuff to stored list
1764 for(std::list<StaticObject>::iterator
1765 i = new_stored.begin();
1766 i != new_stored.end(); ++i)
1768 StaticObject &s_obj = *i;
1769 block->m_static_objects.m_stored.push_back(s_obj);
1772 // Turn the active counterparts of activated objects not pending for
1774 for(std::map<u16, StaticObject>::iterator
1775 i = block->m_static_objects.m_active.begin();
1776 i != block->m_static_objects.m_active.end(); ++i)
1779 ServerActiveObject *object = getActiveObject(id);
1781 object->m_pending_deactivation = false;
1785 Note: Block hasn't really been modified here.
1786 The objects have just been activated and moved from the stored
1787 static list to the active static list.
1788 As such, the block is essentially the same.
1789 Thus, do not call block->raiseModified(MOD_STATE_WRITE_NEEDED).
1790 Otherwise there would be a huge amount of unnecessary I/O.
1795 Convert objects that are not standing inside active blocks to static.
1797 If m_known_by_count != 0, active object is not deleted, but static
1798 data is still updated.
1800 If force_delete is set, active object is deleted nevertheless. It
1801 shall only be set so in the destructor of the environment.
1803 If block wasn't generated (not in memory or on disk),
1805 void ServerEnvironment::deactivateFarObjects(bool force_delete)
1807 std::list<u16> objects_to_remove;
1808 for(std::map<u16, ServerActiveObject*>::iterator
1809 i = m_active_objects.begin();
1810 i != m_active_objects.end(); ++i)
1812 ServerActiveObject* obj = i->second;
1815 // Do not deactivate if static data creation not allowed
1816 if(!force_delete && !obj->isStaticAllowed())
1819 // If pending deactivation, let removeRemovedObjects() do it
1820 if(!force_delete && obj->m_pending_deactivation)
1824 v3f objectpos = obj->getBasePosition();
1826 // The block in which the object resides in
1827 v3s16 blockpos_o = getNodeBlockPos(floatToInt(objectpos, BS));
1829 // If object's static data is stored in a deactivated block and object
1830 // is actually located in an active block, re-save to the block in
1831 // which the object is actually located in.
1833 obj->m_static_exists &&
1834 !m_active_blocks.contains(obj->m_static_block) &&
1835 m_active_blocks.contains(blockpos_o))
1837 v3s16 old_static_block = obj->m_static_block;
1839 // Save to block where object is located
1840 MapBlock *block = m_map->emergeBlock(blockpos_o, false);
1842 errorstream<<"ServerEnvironment::deactivateFarObjects(): "
1843 <<"Could not save object id="<<id
1844 <<" to it's current block "<<PP(blockpos_o)
1848 std::string staticdata_new = obj->getStaticData();
1849 StaticObject s_obj(obj->getType(), objectpos, staticdata_new);
1850 block->m_static_objects.insert(id, s_obj);
1851 obj->m_static_block = blockpos_o;
1852 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1853 "deactivateFarObjects: Static data moved in");
1855 // Delete from block where object was located
1856 block = m_map->emergeBlock(old_static_block, false);
1858 errorstream<<"ServerEnvironment::deactivateFarObjects(): "
1859 <<"Could not delete object id="<<id
1860 <<" from it's previous block "<<PP(old_static_block)
1864 block->m_static_objects.remove(id);
1865 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1866 "deactivateFarObjects: Static data moved out");
1870 // If block is active, don't remove
1871 if(!force_delete && m_active_blocks.contains(blockpos_o))
1874 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
1875 <<"deactivating object id="<<id<<" on inactive block "
1876 <<PP(blockpos_o)<<std::endl;
1878 // If known by some client, don't immediately delete.
1879 bool pending_delete = (obj->m_known_by_count > 0 && !force_delete);
1882 Update the static data
1885 if(obj->isStaticAllowed())
1887 // Create new static object
1888 std::string staticdata_new = obj->getStaticData();
1889 StaticObject s_obj(obj->getType(), objectpos, staticdata_new);
1891 bool stays_in_same_block = false;
1892 bool data_changed = true;
1894 if(obj->m_static_exists){
1895 if(obj->m_static_block == blockpos_o)
1896 stays_in_same_block = true;
1898 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
1900 std::map<u16, StaticObject>::iterator n =
1901 block->m_static_objects.m_active.find(id);
1902 if(n != block->m_static_objects.m_active.end()){
1903 StaticObject static_old = n->second;
1905 float save_movem = obj->getMinimumSavedMovement();
1907 if(static_old.data == staticdata_new &&
1908 (static_old.pos - objectpos).getLength() < save_movem)
1909 data_changed = false;
1911 errorstream<<"ServerEnvironment::deactivateFarObjects(): "
1912 <<"id="<<id<<" m_static_exists=true but "
1913 <<"static data doesn't actually exist in "
1914 <<PP(obj->m_static_block)<<std::endl;
1918 bool shall_be_written = (!stays_in_same_block || data_changed);
1920 // Delete old static object
1921 if(obj->m_static_exists)
1923 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
1926 block->m_static_objects.remove(id);
1927 obj->m_static_exists = false;
1928 // Only mark block as modified if data changed considerably
1929 if(shall_be_written)
1930 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1931 "deactivateFarObjects: Static data "
1932 "changed considerably");
1936 // Add to the block where the object is located in
1937 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1938 // Get or generate the block
1939 MapBlock *block = NULL;
1941 block = m_map->emergeBlock(blockpos);
1942 } catch(InvalidPositionException &e){
1943 // Handled via NULL pointer
1944 // NOTE: emergeBlock's failure is usually determined by it
1945 // actually returning NULL
1950 if(block->m_static_objects.m_stored.size() >= g_settings->getU16("max_objects_per_block")){
1951 errorstream<<"ServerEnv: Trying to store id="<<obj->getId()
1952 <<" statically but block "<<PP(blockpos)
1953 <<" already contains "
1954 <<block->m_static_objects.m_stored.size()
1956 <<" Forcing delete."<<std::endl;
1957 force_delete = true;
1959 // If static counterpart already exists in target block,
1961 // This shouldn't happen because the object is removed from
1962 // the previous block before this according to
1963 // obj->m_static_block, but happens rarely for some unknown
1964 // reason. Unsuccessful attempts have been made to find
1966 if(id && block->m_static_objects.m_active.find(id) != block->m_static_objects.m_active.end()){
1967 infostream<<"ServerEnv: WARNING: Performing hack #83274"
1969 block->m_static_objects.remove(id);
1971 // Store static data
1972 u16 store_id = pending_delete ? id : 0;
1973 block->m_static_objects.insert(store_id, s_obj);
1975 // Only mark block as modified if data changed considerably
1976 if(shall_be_written)
1977 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1978 "deactivateFarObjects: Static data "
1979 "changed considerably");
1981 obj->m_static_exists = true;
1982 obj->m_static_block = block->getPos();
1987 v3s16 p = floatToInt(objectpos, BS);
1988 errorstream<<"ServerEnv: Could not find or generate "
1989 <<"a block for storing id="<<obj->getId()
1990 <<" statically (pos="<<PP(p)<<")"<<std::endl;
1997 If known by some client, set pending deactivation.
1998 Otherwise delete it immediately.
2001 if(pending_delete && !force_delete)
2003 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
2004 <<"object id="<<id<<" is known by clients"
2005 <<"; not deleting yet"<<std::endl;
2007 obj->m_pending_deactivation = true;
2011 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
2012 <<"object id="<<id<<" is not known by clients"
2013 <<"; deleting"<<std::endl;
2015 // Tell the object about removal
2016 obj->removingFromEnvironment();
2017 // Deregister in scripting api
2018 m_script->removeObjectReference(obj);
2020 // Delete active object
2021 if(obj->environmentDeletes())
2023 // Id to be removed from m_active_objects
2024 objects_to_remove.push_back(id);
2027 // Remove references from m_active_objects
2028 for(std::list<u16>::iterator i = objects_to_remove.begin();
2029 i != objects_to_remove.end(); ++i)
2031 m_active_objects.erase(*i);
2038 #include "clientsimpleobject.h"
2044 ClientEnvironment::ClientEnvironment(ClientMap *map, scene::ISceneManager *smgr,
2045 ITextureSource *texturesource, IGameDef *gamedef,
2046 IrrlichtDevice *irr):
2049 m_texturesource(texturesource),
2055 ClientEnvironment::~ClientEnvironment()
2057 // delete active objects
2058 for(std::map<u16, ClientActiveObject*>::iterator
2059 i = m_active_objects.begin();
2060 i != m_active_objects.end(); ++i)
2065 for(std::list<ClientSimpleObject*>::iterator
2066 i = m_simple_objects.begin(); i != m_simple_objects.end(); ++i)
2075 Map & ClientEnvironment::getMap()
2080 ClientMap & ClientEnvironment::getClientMap()
2085 void ClientEnvironment::addPlayer(Player *player)
2087 DSTACK(__FUNCTION_NAME);
2089 It is a failure if player is local and there already is a local
2092 assert(!(player->isLocal() == true && getLocalPlayer() != NULL));
2094 Environment::addPlayer(player);
2097 LocalPlayer * ClientEnvironment::getLocalPlayer()
2099 for(std::list<Player*>::iterator i = m_players.begin();
2100 i != m_players.end(); ++i)
2102 Player *player = *i;
2103 if(player->isLocal())
2104 return (LocalPlayer*)player;
2109 void ClientEnvironment::step(float dtime)
2111 DSTACK(__FUNCTION_NAME);
2113 /* Step time of day */
2114 stepTimeOfDay(dtime);
2116 // Get some settings
2117 bool fly_allowed = m_gamedef->checkLocalPrivilege("fly");
2118 bool free_move = fly_allowed && g_settings->getBool("free_move");
2121 LocalPlayer *lplayer = getLocalPlayer();
2123 // collision info queue
2124 std::list<CollisionInfo> player_collisions;
2127 Get the speed the player is going
2129 bool is_climbing = lplayer->is_climbing;
2131 f32 player_speed = lplayer->getSpeed().getLength();
2134 Maximum position increment
2136 //f32 position_max_increment = 0.05*BS;
2137 f32 position_max_increment = 0.1*BS;
2139 // Maximum time increment (for collision detection etc)
2140 // time = distance / speed
2141 f32 dtime_max_increment = 1;
2142 if(player_speed > 0.001)
2143 dtime_max_increment = position_max_increment / player_speed;
2145 // Maximum time increment is 10ms or lower
2146 if(dtime_max_increment > 0.01)
2147 dtime_max_increment = 0.01;
2149 // Don't allow overly huge dtime
2153 f32 dtime_downcount = dtime;
2156 Stuff that has a maximum time increment
2165 if(dtime_downcount > dtime_max_increment)
2167 dtime_part = dtime_max_increment;
2168 dtime_downcount -= dtime_part;
2172 dtime_part = dtime_downcount;
2174 Setting this to 0 (no -=dtime_part) disables an infinite loop
2175 when dtime_part is so small that dtime_downcount -= dtime_part
2178 dtime_downcount = 0;
2187 if(free_move == false && is_climbing == false)
2190 v3f speed = lplayer->getSpeed();
2191 if(lplayer->in_liquid == false)
2192 speed.Y -= lplayer->movement_gravity * lplayer->physics_override_gravity * dtime_part * 2;
2194 // Liquid floating / sinking
2195 if(lplayer->in_liquid && !lplayer->swimming_vertical)
2196 speed.Y -= lplayer->movement_liquid_sink * dtime_part * 2;
2198 // Liquid resistance
2199 if(lplayer->in_liquid_stable || lplayer->in_liquid)
2201 // How much the node's viscosity blocks movement, ranges between 0 and 1
2202 // Should match the scale at which viscosity increase affects other liquid attributes
2203 const f32 viscosity_factor = 0.3;
2205 v3f d_wanted = -speed / lplayer->movement_liquid_fluidity;
2206 f32 dl = d_wanted.getLength();
2207 if(dl > lplayer->movement_liquid_fluidity_smooth)
2208 dl = lplayer->movement_liquid_fluidity_smooth;
2209 dl *= (lplayer->liquid_viscosity * viscosity_factor) + (1 - viscosity_factor);
2211 v3f d = d_wanted.normalize() * dl;
2215 if(speed.X > lplayer->movement_liquid_fluidity + lplayer->movement_liquid_fluidity_smooth) speed.X -= lplayer->movement_liquid_fluidity_smooth;
2216 if(speed.X < -lplayer->movement_liquid_fluidity - lplayer->movement_liquid_fluidity_smooth) speed.X += lplayer->movement_liquid_fluidity_smooth;
2217 if(speed.Y > lplayer->movement_liquid_fluidity + lplayer->movement_liquid_fluidity_smooth) speed.Y -= lplayer->movement_liquid_fluidity_smooth;
2218 if(speed.Y < -lplayer->movement_liquid_fluidity - lplayer->movement_liquid_fluidity_smooth) speed.Y += lplayer->movement_liquid_fluidity_smooth;
2219 if(speed.Z > lplayer->movement_liquid_fluidity + lplayer->movement_liquid_fluidity_smooth) speed.Z -= lplayer->movement_liquid_fluidity_smooth;
2220 if(speed.Z < -lplayer->movement_liquid_fluidity - lplayer->movement_liquid_fluidity_smooth) speed.Z += lplayer->movement_liquid_fluidity_smooth;
2224 lplayer->setSpeed(speed);
2229 This also does collision detection.
2231 lplayer->move(dtime_part, this, position_max_increment,
2232 &player_collisions);
2235 while(dtime_downcount > 0.001);
2237 //std::cout<<"Looped "<<loopcount<<" times."<<std::endl;
2239 for(std::list<CollisionInfo>::iterator
2240 i = player_collisions.begin();
2241 i != player_collisions.end(); ++i)
2243 CollisionInfo &info = *i;
2244 v3f speed_diff = info.new_speed - info.old_speed;;
2245 // Handle only fall damage
2246 // (because otherwise walking against something in fast_move kills you)
2247 if(speed_diff.Y < 0 || info.old_speed.Y >= 0)
2249 // Get rid of other components
2252 f32 pre_factor = 1; // 1 hp per node/s
2253 f32 tolerance = BS*14; // 5 without damage
2254 f32 post_factor = 1; // 1 hp per node/s
2255 if(info.type == COLLISION_NODE)
2257 const ContentFeatures &f = m_gamedef->ndef()->
2258 get(m_map->getNodeNoEx(info.node_p));
2259 // Determine fall damage multiplier
2260 int addp = itemgroup_get(f.groups, "fall_damage_add_percent");
2261 pre_factor = 1.0 + (float)addp/100.0;
2263 float speed = pre_factor * speed_diff.getLength();
2264 if(speed > tolerance)
2266 f32 damage_f = (speed - tolerance)/BS * post_factor;
2267 u16 damage = (u16)(damage_f+0.5);
2269 damageLocalPlayer(damage, true);
2270 MtEvent *e = new SimpleTriggerEvent("PlayerFallingDamage");
2271 m_gamedef->event()->put(e);
2277 A quick draft of lava damage
2279 if(m_lava_hurt_interval.step(dtime, 1.0))
2281 v3f pf = lplayer->getPosition();
2283 // Feet, middle and head
2284 v3s16 p1 = floatToInt(pf + v3f(0, BS*0.1, 0), BS);
2285 MapNode n1 = m_map->getNodeNoEx(p1);
2286 v3s16 p2 = floatToInt(pf + v3f(0, BS*0.8, 0), BS);
2287 MapNode n2 = m_map->getNodeNoEx(p2);
2288 v3s16 p3 = floatToInt(pf + v3f(0, BS*1.6, 0), BS);
2289 MapNode n3 = m_map->getNodeNoEx(p3);
2291 u32 damage_per_second = 0;
2292 damage_per_second = MYMAX(damage_per_second,
2293 m_gamedef->ndef()->get(n1).damage_per_second);
2294 damage_per_second = MYMAX(damage_per_second,
2295 m_gamedef->ndef()->get(n2).damage_per_second);
2296 damage_per_second = MYMAX(damage_per_second,
2297 m_gamedef->ndef()->get(n3).damage_per_second);
2299 if(damage_per_second != 0)
2301 damageLocalPlayer(damage_per_second, true);
2308 if(m_drowning_interval.step(dtime, 2.0))
2310 v3f pf = lplayer->getPosition();
2313 v3s16 p = floatToInt(pf + v3f(0, BS*1.6, 0), BS);
2314 MapNode n = m_map->getNodeNoEx(p);
2315 ContentFeatures c = m_gamedef->ndef()->get(n);
2316 u8 drowning_damage = c.drowning;
2317 if(drowning_damage > 0 && lplayer->hp > 0){
2318 u16 breath = lplayer->getBreath();
2325 lplayer->setBreath(breath);
2326 updateLocalPlayerBreath(breath);
2329 if(lplayer->getBreath() == 0 && drowning_damage > 0){
2330 damageLocalPlayer(drowning_damage, true);
2333 if(m_breathing_interval.step(dtime, 0.5))
2335 v3f pf = lplayer->getPosition();
2338 v3s16 p = floatToInt(pf + v3f(0, BS*1.6, 0), BS);
2339 MapNode n = m_map->getNodeNoEx(p);
2340 ContentFeatures c = m_gamedef->ndef()->get(n);
2342 lplayer->setBreath(11);
2344 else if(c.drowning == 0){
2345 u16 breath = lplayer->getBreath();
2348 lplayer->setBreath(breath);
2349 updateLocalPlayerBreath(breath);
2355 Stuff that can be done in an arbitarily large dtime
2357 for(std::list<Player*>::iterator i = m_players.begin();
2358 i != m_players.end(); ++i)
2360 Player *player = *i;
2363 Handle non-local players
2365 if(player->isLocal() == false)
2368 player->move(dtime, *m_map, 100*BS);
2372 // Update lighting on all players on client
2376 v3s16 p = player->getLightPosition();
2377 MapNode n = m_map->getNode(p);
2378 light = n.getLightBlendF1((float)getDayNightRatio()/1000, m_gamedef->ndef());
2380 catch(InvalidPositionException &e){
2381 light = blend_light_f1((float)getDayNightRatio()/1000, LIGHT_SUN, 0);
2383 player->light = light;
2387 Step active objects and update lighting of them
2390 g_profiler->avg("CEnv: num of objects", m_active_objects.size());
2391 bool update_lighting = m_active_object_light_update_interval.step(dtime, 0.21);
2392 for(std::map<u16, ClientActiveObject*>::iterator
2393 i = m_active_objects.begin();
2394 i != m_active_objects.end(); ++i)
2396 ClientActiveObject* obj = i->second;
2398 obj->step(dtime, this);
2406 v3s16 p = obj->getLightPosition();
2407 MapNode n = m_map->getNode(p);
2408 light = n.getLightBlend(getDayNightRatio(), m_gamedef->ndef());
2410 catch(InvalidPositionException &e){
2411 light = blend_light(getDayNightRatio(), LIGHT_SUN, 0);
2413 obj->updateLight(light);
2418 Step and handle simple objects
2420 g_profiler->avg("CEnv: num of simple objects", m_simple_objects.size());
2421 for(std::list<ClientSimpleObject*>::iterator
2422 i = m_simple_objects.begin(); i != m_simple_objects.end();)
2424 ClientSimpleObject *simple = *i;
2425 std::list<ClientSimpleObject*>::iterator cur = i;
2427 simple->step(dtime);
2428 if(simple->m_to_be_removed){
2430 m_simple_objects.erase(cur);
2435 void ClientEnvironment::addSimpleObject(ClientSimpleObject *simple)
2437 m_simple_objects.push_back(simple);
2440 ClientActiveObject* ClientEnvironment::getActiveObject(u16 id)
2442 std::map<u16, ClientActiveObject*>::iterator n;
2443 n = m_active_objects.find(id);
2444 if(n == m_active_objects.end())
2449 bool isFreeClientActiveObjectId(u16 id,
2450 std::map<u16, ClientActiveObject*> &objects)
2455 return objects.find(id) == objects.end();
2458 u16 getFreeClientActiveObjectId(
2459 std::map<u16, ClientActiveObject*> &objects)
2461 //try to reuse id's as late as possible
2462 static u16 last_used_id = 0;
2463 u16 startid = last_used_id;
2467 if(isFreeClientActiveObjectId(last_used_id, objects))
2468 return last_used_id;
2470 if(last_used_id == startid)
2475 u16 ClientEnvironment::addActiveObject(ClientActiveObject *object)
2478 if(object->getId() == 0)
2480 u16 new_id = getFreeClientActiveObjectId(m_active_objects);
2483 infostream<<"ClientEnvironment::addActiveObject(): "
2484 <<"no free ids available"<<std::endl;
2488 object->setId(new_id);
2490 if(isFreeClientActiveObjectId(object->getId(), m_active_objects) == false)
2492 infostream<<"ClientEnvironment::addActiveObject(): "
2493 <<"id is not free ("<<object->getId()<<")"<<std::endl;
2497 infostream<<"ClientEnvironment::addActiveObject(): "
2498 <<"added (id="<<object->getId()<<")"<<std::endl;
2499 m_active_objects[object->getId()] = object;
2500 object->addToScene(m_smgr, m_texturesource, m_irr);
2501 { // Update lighting immediately
2505 v3s16 p = object->getLightPosition();
2506 MapNode n = m_map->getNode(p);
2507 light = n.getLightBlend(getDayNightRatio(), m_gamedef->ndef());
2509 catch(InvalidPositionException &e){
2510 light = blend_light(getDayNightRatio(), LIGHT_SUN, 0);
2512 object->updateLight(light);
2514 return object->getId();
2517 void ClientEnvironment::addActiveObject(u16 id, u8 type,
2518 const std::string &init_data)
2520 ClientActiveObject* obj =
2521 ClientActiveObject::create(type, m_gamedef, this);
2524 infostream<<"ClientEnvironment::addActiveObject(): "
2525 <<"id="<<id<<" type="<<type<<": Couldn't create object"
2534 obj->initialize(init_data);
2536 catch(SerializationError &e)
2538 errorstream<<"ClientEnvironment::addActiveObject():"
2539 <<" id="<<id<<" type="<<type
2540 <<": SerializationError in initialize(): "
2542 <<": init_data="<<serializeJsonString(init_data)
2546 addActiveObject(obj);
2549 void ClientEnvironment::removeActiveObject(u16 id)
2551 verbosestream<<"ClientEnvironment::removeActiveObject(): "
2552 <<"id="<<id<<std::endl;
2553 ClientActiveObject* obj = getActiveObject(id);
2556 infostream<<"ClientEnvironment::removeActiveObject(): "
2557 <<"id="<<id<<" not found"<<std::endl;
2560 obj->removeFromScene(true);
2562 m_active_objects.erase(id);
2565 void ClientEnvironment::processActiveObjectMessage(u16 id,
2566 const std::string &data)
2568 ClientActiveObject* obj = getActiveObject(id);
2571 infostream<<"ClientEnvironment::processActiveObjectMessage():"
2572 <<" got message for id="<<id<<", which doesn't exist."
2578 obj->processMessage(data);
2580 catch(SerializationError &e)
2582 errorstream<<"ClientEnvironment::processActiveObjectMessage():"
2583 <<" id="<<id<<" type="<<obj->getType()
2584 <<" SerializationError in processMessage(),"
2585 <<" message="<<serializeJsonString(data)
2591 Callbacks for activeobjects
2594 void ClientEnvironment::damageLocalPlayer(u8 damage, bool handle_hp)
2596 LocalPlayer *lplayer = getLocalPlayer();
2600 if(lplayer->hp > damage)
2601 lplayer->hp -= damage;
2606 ClientEnvEvent event;
2607 event.type = CEE_PLAYER_DAMAGE;
2608 event.player_damage.amount = damage;
2609 event.player_damage.send_to_server = handle_hp;
2610 m_client_event_queue.push_back(event);
2613 void ClientEnvironment::updateLocalPlayerBreath(u16 breath)
2615 ClientEnvEvent event;
2616 event.type = CEE_PLAYER_BREATH;
2617 event.player_breath.amount = breath;
2618 m_client_event_queue.push_back(event);
2622 Client likes to call these
2625 void ClientEnvironment::getActiveObjects(v3f origin, f32 max_d,
2626 std::vector<DistanceSortedActiveObject> &dest)
2628 for(std::map<u16, ClientActiveObject*>::iterator
2629 i = m_active_objects.begin();
2630 i != m_active_objects.end(); ++i)
2632 ClientActiveObject* obj = i->second;
2634 f32 d = (obj->getPosition() - origin).getLength();
2639 DistanceSortedActiveObject dso(obj, d);
2641 dest.push_back(dso);
2645 ClientEnvEvent ClientEnvironment::getClientEvent()
2647 ClientEnvEvent event;
2648 if(m_client_event_queue.empty())
2649 event.type = CEE_NONE;
2651 event = m_client_event_queue.front();
2652 m_client_event_queue.pop_front();
2657 #endif // #ifndef SERVER