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.
23 #include "environment.h"
26 #include "collision.h"
27 #include "content_mapnode.h"
29 #include "serverobject.h"
30 #include "content_sao.h"
35 #include "cpp_api/scriptapi.h"
37 #include "nodemetadata.h"
38 #include "main.h" // For g_settings, g_profiler
41 #include "clientmap.h"
42 #include "localplayer.h"
45 #include "daynightratio.h"
47 #include "util/serialize.h"
49 #define PP(x) "("<<(x).X<<","<<(x).Y<<","<<(x).Z<<")"
51 Environment::Environment():
53 m_time_of_day_f(9000./24000),
54 m_time_of_day_speed(0),
59 Environment::~Environment()
62 for(std::list<Player*>::iterator i = m_players.begin();
63 i != m_players.end(); ++i)
69 void Environment::addPlayer(Player *player)
71 DSTACK(__FUNCTION_NAME);
73 Check that peer_ids are unique.
74 Also check that names are unique.
75 Exception: there can be multiple players with peer_id=0
77 // If peer id is non-zero, it has to be unique.
78 if(player->peer_id != 0)
79 assert(getPlayer(player->peer_id) == NULL);
80 // Name has to be unique.
81 assert(getPlayer(player->getName()) == NULL);
83 m_players.push_back(player);
86 void Environment::removePlayer(u16 peer_id)
88 DSTACK(__FUNCTION_NAME);
90 for(std::list<Player*>::iterator i = m_players.begin();
91 i != m_players.end(); ++i)
94 if(player->peer_id != peer_id)
99 // See if there is an another one
100 // (shouldn't be, but just to be sure)
105 Player * Environment::getPlayer(u16 peer_id)
107 for(std::list<Player*>::iterator i = m_players.begin();
108 i != m_players.end(); ++i)
111 if(player->peer_id == peer_id)
117 Player * Environment::getPlayer(const char *name)
119 for(std::list<Player*>::iterator i = m_players.begin();
120 i != m_players.end(); ++i)
123 if(strcmp(player->getName(), name) == 0)
129 Player * Environment::getRandomConnectedPlayer()
131 std::list<Player*> connected_players = getPlayers(true);
132 u32 chosen_one = myrand() % connected_players.size();
134 for(std::list<Player*>::iterator
135 i = connected_players.begin();
136 i != connected_players.end(); ++i)
148 Player * Environment::getNearestConnectedPlayer(v3f pos)
150 std::list<Player*> connected_players = getPlayers(true);
152 Player *nearest_player = NULL;
153 for(std::list<Player*>::iterator
154 i = connected_players.begin();
155 i != connected_players.end(); ++i)
158 f32 d = player->getPosition().getDistanceFrom(pos);
159 if(d < nearest_d || nearest_player == NULL)
162 nearest_player = player;
165 return nearest_player;
168 std::list<Player*> Environment::getPlayers()
173 std::list<Player*> Environment::getPlayers(bool ignore_disconnected)
175 std::list<Player*> newlist;
176 for(std::list<Player*>::iterator
177 i = m_players.begin();
178 i != m_players.end(); ++i)
182 if(ignore_disconnected)
184 // Ignore disconnected players
185 if(player->peer_id == 0)
189 newlist.push_back(player);
194 void Environment::printPlayers(std::ostream &o)
196 o<<"Players in environment:"<<std::endl;
197 for(std::list<Player*>::iterator i = m_players.begin();
198 i != m_players.end(); i++)
201 o<<"Player peer_id="<<player->peer_id<<std::endl;
205 u32 Environment::getDayNightRatio()
207 bool smooth = g_settings->getBool("enable_shaders");
208 return time_to_daynight_ratio(m_time_of_day_f*24000, smooth);
211 void Environment::stepTimeOfDay(float dtime)
213 m_time_counter += dtime;
214 f32 speed = m_time_of_day_speed * 24000./(24.*3600);
215 u32 units = (u32)(m_time_counter*speed);
216 m_time_counter -= (f32)units / speed;
220 if(m_time_of_day + units >= 24000)
222 m_time_of_day = (m_time_of_day + units) % 24000;
224 m_time_of_day_f = (float)m_time_of_day / 24000.0;
227 m_time_of_day_f += m_time_of_day_speed/24/3600*dtime;
228 if(m_time_of_day_f > 1.0)
229 m_time_of_day_f -= 1.0;
230 if(m_time_of_day_f < 0.0)
231 m_time_of_day_f += 1.0;
239 ABMWithState::ABMWithState(ActiveBlockModifier *abm_):
243 // Initialize timer to random value to spread processing
244 float itv = abm->getTriggerInterval();
245 itv = MYMAX(0.001, itv); // No less than 1ms
246 int minval = MYMAX(-0.51*itv, -60); // Clamp to
247 int maxval = MYMIN(0.51*itv, 60); // +-60 seconds
248 timer = myrand_range(minval, maxval);
255 void fillRadiusBlock(v3s16 p0, s16 r, std::set<v3s16> &list)
258 for(p.X=p0.X-r; p.X<=p0.X+r; p.X++)
259 for(p.Y=p0.Y-r; p.Y<=p0.Y+r; p.Y++)
260 for(p.Z=p0.Z-r; p.Z<=p0.Z+r; p.Z++)
267 void ActiveBlockList::update(std::list<v3s16> &active_positions,
269 std::set<v3s16> &blocks_removed,
270 std::set<v3s16> &blocks_added)
275 std::set<v3s16> newlist;
276 for(std::list<v3s16>::iterator i = active_positions.begin();
277 i != active_positions.end(); ++i)
279 fillRadiusBlock(*i, radius, newlist);
283 Find out which blocks on the old list are not on the new list
285 // Go through old list
286 for(std::set<v3s16>::iterator i = m_list.begin();
287 i != m_list.end(); ++i)
290 // If not on new list, it's been removed
291 if(newlist.find(p) == newlist.end())
292 blocks_removed.insert(p);
296 Find out which blocks on the new list are not on the old list
298 // Go through new list
299 for(std::set<v3s16>::iterator i = newlist.begin();
300 i != newlist.end(); ++i)
303 // If not on old list, it's been added
304 if(m_list.find(p) == m_list.end())
305 blocks_added.insert(p);
312 for(std::set<v3s16>::iterator i = newlist.begin();
313 i != newlist.end(); ++i)
324 ServerEnvironment::ServerEnvironment(ServerMap *map, ScriptApi *scriptIface,
325 IGameDef *gamedef, IBackgroundBlockEmerger *emerger):
327 m_script(scriptIface),
330 m_random_spawn_timer(3),
331 m_send_recommended_timer(0),
332 m_active_block_interval_overload_skip(0),
334 m_game_time_fraction_counter(0),
335 m_recommended_send_interval(0.1),
336 m_max_lag_estimate(0.1)
340 ServerEnvironment::~ServerEnvironment()
342 // Clear active block list.
343 // This makes the next one delete all active objects.
344 m_active_blocks.clear();
346 // Convert all objects to static and delete the active objects
347 deactivateFarObjects(true);
352 // Delete ActiveBlockModifiers
353 for(std::list<ABMWithState>::iterator
354 i = m_abms.begin(); i != m_abms.end(); ++i){
359 Map & ServerEnvironment::getMap()
364 ServerMap & ServerEnvironment::getServerMap()
369 bool ServerEnvironment::line_of_sight(v3f pos1, v3f pos2, float stepsize)
371 float distance = pos1.getDistanceFrom(pos2);
373 //calculate normalized direction vector
374 v3f normalized_vector = v3f((pos2.X - pos1.X)/distance,
375 (pos2.Y - pos1.Y)/distance,
376 (pos2.Z - pos1.Z)/distance);
378 //find out if there's a node on path between pos1 and pos2
379 for (float i = 1; i < distance; i += stepsize) {
380 v3s16 pos = floatToInt(v3f(normalized_vector.X * i,
381 normalized_vector.Y * i,
382 normalized_vector.Z * i) +pos1,BS);
384 MapNode n = getMap().getNodeNoEx(pos);
386 if(n.param0 != CONTENT_AIR) {
393 void ServerEnvironment::serializePlayers(const std::string &savedir)
395 std::string players_path = savedir + "/players";
396 fs::CreateDir(players_path);
398 std::set<Player*> saved_players;
400 std::vector<fs::DirListNode> player_files = fs::GetDirListing(players_path);
401 for(u32 i=0; i<player_files.size(); i++)
403 if(player_files[i].dir || player_files[i].name[0] == '.')
406 // Full path to this file
407 std::string path = players_path + "/" + player_files[i].name;
409 //infostream<<"Checking player file "<<path<<std::endl;
411 // Load player to see what is its name
412 RemotePlayer testplayer(m_gamedef);
414 // Open file and deserialize
415 std::ifstream is(path.c_str(), std::ios_base::binary);
416 if(is.good() == false)
418 infostream<<"Failed to read "<<path<<std::endl;
421 testplayer.deSerialize(is, player_files[i].name);
424 //infostream<<"Loaded test player with name "<<testplayer.getName()<<std::endl;
426 // Search for the player
427 std::string playername = testplayer.getName();
428 Player *player = getPlayer(playername.c_str());
431 infostream<<"Didn't find matching player, ignoring file "<<path<<std::endl;
435 //infostream<<"Found matching player, overwriting."<<std::endl;
437 // OK, found. Save player there.
438 if(player->checkModified())
440 // Open file and serialize
441 std::ofstream os(path.c_str(), std::ios_base::binary);
442 if(os.good() == false)
444 infostream<<"Failed to overwrite "<<path<<std::endl;
447 player->serialize(os);
448 saved_players.insert(player);
450 saved_players.insert(player);
454 for(std::list<Player*>::iterator i = m_players.begin();
455 i != m_players.end(); ++i)
458 if(saved_players.find(player) != saved_players.end())
460 /*infostream<<"Player "<<player->getName()
461 <<" was already saved."<<std::endl;*/
464 std::string playername = player->getName();
465 // Don't save unnamed player
468 //infostream<<"Not saving unnamed player."<<std::endl;
474 if(string_allowed(playername, PLAYERNAME_ALLOWED_CHARS) == false)
475 playername = "player";
476 std::string path = players_path + "/" + playername;
478 for(u32 i=0; i<1000; i++)
480 if(fs::PathExists(path) == false)
485 path = players_path + "/" + playername + itos(i);
489 infostream<<"Didn't find free file for player"<<std::endl;
494 /*infostream<<"Saving player "<<player->getName()<<" to "
496 // Open file and serialize
497 std::ofstream os(path.c_str(), std::ios_base::binary);
498 if(os.good() == false)
500 infostream<<"Failed to overwrite "<<path<<std::endl;
503 player->serialize(os);
504 saved_players.insert(player);
508 //infostream<<"Saved "<<saved_players.size()<<" players."<<std::endl;
511 void ServerEnvironment::deSerializePlayers(const std::string &savedir)
513 std::string players_path = savedir + "/players";
515 std::vector<fs::DirListNode> player_files = fs::GetDirListing(players_path);
516 for(u32 i=0; i<player_files.size(); i++)
518 if(player_files[i].dir)
521 // Full path to this file
522 std::string path = players_path + "/" + player_files[i].name;
524 //infostream<<"Checking player file "<<path<<std::endl;
526 // Load player to see what is its name
527 RemotePlayer testplayer(m_gamedef);
529 // Open file and deserialize
530 std::ifstream is(path.c_str(), std::ios_base::binary);
531 if(is.good() == false)
533 infostream<<"Failed to read "<<path<<std::endl;
536 testplayer.deSerialize(is, player_files[i].name);
539 if(!string_allowed(testplayer.getName(), PLAYERNAME_ALLOWED_CHARS))
541 infostream<<"Not loading player with invalid name: "
542 <<testplayer.getName()<<std::endl;
545 /*infostream<<"Loaded test player with name "<<testplayer.getName()
548 // Search for the player
549 std::string playername = testplayer.getName();
550 Player *player = getPlayer(playername.c_str());
551 bool newplayer = false;
554 //infostream<<"Is a new player"<<std::endl;
555 player = new RemotePlayer(m_gamedef);
561 verbosestream<<"Reading player "<<testplayer.getName()<<" from "
563 // Open file and deserialize
564 std::ifstream is(path.c_str(), std::ios_base::binary);
565 if(is.good() == false)
567 infostream<<"Failed to read "<<path<<std::endl;
570 player->deSerialize(is, player_files[i].name);
580 void ServerEnvironment::saveMeta(const std::string &savedir)
582 std::string path = savedir + "/env_meta.txt";
584 // Open file and serialize
585 std::ofstream os(path.c_str(), std::ios_base::binary);
586 if(os.good() == false)
588 infostream<<"ServerEnvironment::saveMeta(): Failed to open "
590 throw SerializationError("Couldn't save env meta");
594 args.setU64("game_time", m_game_time);
595 args.setU64("time_of_day", getTimeOfDay());
600 void ServerEnvironment::loadMeta(const std::string &savedir)
602 std::string path = savedir + "/env_meta.txt";
604 // Open file and deserialize
605 std::ifstream is(path.c_str(), std::ios_base::binary);
606 if(is.good() == false)
608 infostream<<"ServerEnvironment::loadMeta(): Failed to open "
610 throw SerializationError("Couldn't load env meta");
618 throw SerializationError
619 ("ServerEnvironment::loadMeta(): EnvArgsEnd not found");
621 std::getline(is, line);
622 std::string trimmedline = trim(line);
623 if(trimmedline == "EnvArgsEnd")
625 args.parseConfigLine(line);
629 m_game_time = args.getU64("game_time");
630 }catch(SettingNotFoundException &e){
631 // Getting this is crucial, otherwise timestamps are useless
632 throw SerializationError("Couldn't load env meta game_time");
636 m_time_of_day = args.getU64("time_of_day");
637 }catch(SettingNotFoundException &e){
638 // This is not as important
639 m_time_of_day = 9000;
645 ActiveBlockModifier *abm;
647 std::set<content_t> required_neighbors;
653 ServerEnvironment *m_env;
654 std::map<content_t, std::list<ActiveABM> > m_aabms;
656 ABMHandler(std::list<ABMWithState> &abms,
657 float dtime_s, ServerEnvironment *env,
663 INodeDefManager *ndef = env->getGameDef()->ndef();
664 for(std::list<ABMWithState>::iterator
665 i = abms.begin(); i != abms.end(); ++i){
666 ActiveBlockModifier *abm = i->abm;
667 float trigger_interval = abm->getTriggerInterval();
668 if(trigger_interval < 0.001)
669 trigger_interval = 0.001;
670 float actual_interval = dtime_s;
673 if(i->timer < trigger_interval)
675 i->timer -= trigger_interval;
676 actual_interval = trigger_interval;
678 float intervals = actual_interval / trigger_interval;
681 float chance = abm->getTriggerChance();
686 aabm.chance = chance / intervals;
690 std::set<std::string> required_neighbors_s
691 = abm->getRequiredNeighbors();
692 for(std::set<std::string>::iterator
693 i = required_neighbors_s.begin();
694 i != required_neighbors_s.end(); i++)
696 ndef->getIds(*i, aabm.required_neighbors);
699 std::set<std::string> contents_s = abm->getTriggerContents();
700 for(std::set<std::string>::iterator
701 i = contents_s.begin(); i != contents_s.end(); i++)
703 std::set<content_t> ids;
704 ndef->getIds(*i, ids);
705 for(std::set<content_t>::const_iterator k = ids.begin();
709 std::map<content_t, std::list<ActiveABM> >::iterator j;
711 if(j == m_aabms.end()){
712 std::list<ActiveABM> aabmlist;
713 m_aabms[c] = aabmlist;
716 j->second.push_back(aabm);
721 void apply(MapBlock *block)
726 ServerMap *map = &m_env->getServerMap();
729 for(p0.X=0; p0.X<MAP_BLOCKSIZE; p0.X++)
730 for(p0.Y=0; p0.Y<MAP_BLOCKSIZE; p0.Y++)
731 for(p0.Z=0; p0.Z<MAP_BLOCKSIZE; p0.Z++)
733 MapNode n = block->getNodeNoEx(p0);
734 content_t c = n.getContent();
735 v3s16 p = p0 + block->getPosRelative();
737 std::map<content_t, std::list<ActiveABM> >::iterator j;
739 if(j == m_aabms.end())
742 for(std::list<ActiveABM>::iterator
743 i = j->second.begin(); i != j->second.end(); i++)
745 if(myrand() % i->chance != 0)
749 if(!i->required_neighbors.empty())
752 for(p1.X = p.X-1; p1.X <= p.X+1; p1.X++)
753 for(p1.Y = p.Y-1; p1.Y <= p.Y+1; p1.Y++)
754 for(p1.Z = p.Z-1; p1.Z <= p.Z+1; p1.Z++)
758 MapNode n = map->getNodeNoEx(p1);
759 content_t c = n.getContent();
760 std::set<content_t>::const_iterator k;
761 k = i->required_neighbors.find(c);
762 if(k != i->required_neighbors.end()){
766 // No required neighbor found
771 // Find out how many objects the block contains
772 u32 active_object_count = block->m_static_objects.m_active.size();
773 // Find out how many objects this and all the neighbors contain
774 u32 active_object_count_wider = 0;
775 u32 wider_unknown_count = 0;
776 for(s16 x=-1; x<=1; x++)
777 for(s16 y=-1; y<=1; y++)
778 for(s16 z=-1; z<=1; z++)
780 MapBlock *block2 = map->getBlockNoCreateNoEx(
781 block->getPos() + v3s16(x,y,z));
783 wider_unknown_count = 0;
786 active_object_count_wider +=
787 block2->m_static_objects.m_active.size()
788 + block2->m_static_objects.m_stored.size();
791 u32 wider_known_count = 3*3*3 - wider_unknown_count;
792 active_object_count_wider += wider_unknown_count * active_object_count_wider / wider_known_count;
794 // Call all the trigger variations
795 i->abm->trigger(m_env, p, n);
796 i->abm->trigger(m_env, p, n,
797 active_object_count, active_object_count_wider);
803 void ServerEnvironment::activateBlock(MapBlock *block, u32 additional_dtime)
805 // Get time difference
807 u32 stamp = block->getTimestamp();
808 if(m_game_time > stamp && stamp != BLOCK_TIMESTAMP_UNDEFINED)
809 dtime_s = m_game_time - block->getTimestamp();
810 dtime_s += additional_dtime;
812 /*infostream<<"ServerEnvironment::activateBlock(): block timestamp: "
813 <<stamp<<", game time: "<<m_game_time<<std::endl;*/
815 // Set current time as timestamp
816 block->setTimestampNoChangedFlag(m_game_time);
818 /*infostream<<"ServerEnvironment::activateBlock(): block is "
819 <<dtime_s<<" seconds old."<<std::endl;*/
821 // Activate stored objects
822 activateObjects(block, dtime_s);
825 std::map<v3s16, NodeTimer> elapsed_timers =
826 block->m_node_timers.step((float)dtime_s);
827 if(!elapsed_timers.empty()){
829 for(std::map<v3s16, NodeTimer>::iterator
830 i = elapsed_timers.begin();
831 i != elapsed_timers.end(); i++){
832 n = block->getNodeNoEx(i->first);
833 v3s16 p = i->first + block->getPosRelative();
834 if(m_script->node_on_timer(p,n,i->second.elapsed))
835 block->setNodeTimer(i->first,NodeTimer(i->second.timeout,0));
839 /* Handle ActiveBlockModifiers */
840 ABMHandler abmhandler(m_abms, dtime_s, this, false);
841 abmhandler.apply(block);
844 void ServerEnvironment::addActiveBlockModifier(ActiveBlockModifier *abm)
846 m_abms.push_back(ABMWithState(abm));
849 bool ServerEnvironment::setNode(v3s16 p, const MapNode &n)
851 INodeDefManager *ndef = m_gamedef->ndef();
852 MapNode n_old = m_map->getNodeNoEx(p);
854 if(ndef->get(n_old).has_on_destruct)
855 m_script->node_on_destruct(p, n_old);
857 bool succeeded = m_map->addNodeWithEvent(p, n);
860 // Call post-destructor
861 if(ndef->get(n_old).has_after_destruct)
862 m_script->node_after_destruct(p, n_old);
864 if(ndef->get(n).has_on_construct)
865 m_script->node_on_construct(p, n);
869 bool ServerEnvironment::removeNode(v3s16 p)
871 INodeDefManager *ndef = m_gamedef->ndef();
872 MapNode n_old = m_map->getNodeNoEx(p);
874 if(ndef->get(n_old).has_on_destruct)
875 m_script->node_on_destruct(p, n_old);
877 // This is slightly optimized compared to addNodeWithEvent(air)
878 bool succeeded = m_map->removeNodeWithEvent(p);
881 // Call post-destructor
882 if(ndef->get(n_old).has_after_destruct)
883 m_script->node_after_destruct(p, n_old);
884 // Air doesn't require constructor
888 std::set<u16> ServerEnvironment::getObjectsInsideRadius(v3f pos, float radius)
890 std::set<u16> objects;
891 for(std::map<u16, ServerActiveObject*>::iterator
892 i = m_active_objects.begin();
893 i != m_active_objects.end(); ++i)
895 ServerActiveObject* obj = i->second;
897 v3f objectpos = obj->getBasePosition();
898 if(objectpos.getDistanceFrom(pos) > radius)
905 void ServerEnvironment::clearAllObjects()
907 infostream<<"ServerEnvironment::clearAllObjects(): "
908 <<"Removing all active objects"<<std::endl;
909 std::list<u16> objects_to_remove;
910 for(std::map<u16, ServerActiveObject*>::iterator
911 i = m_active_objects.begin();
912 i != m_active_objects.end(); ++i)
914 ServerActiveObject* obj = i->second;
915 if(obj->getType() == ACTIVEOBJECT_TYPE_PLAYER)
918 // Delete static object if block is loaded
919 if(obj->m_static_exists){
920 MapBlock *block = m_map->getBlockNoCreateNoEx(obj->m_static_block);
922 block->m_static_objects.remove(id);
923 block->raiseModified(MOD_STATE_WRITE_NEEDED,
925 obj->m_static_exists = false;
928 // If known by some client, don't delete immediately
929 if(obj->m_known_by_count > 0){
930 obj->m_pending_deactivation = true;
931 obj->m_removed = true;
935 // Tell the object about removal
936 obj->removingFromEnvironment();
937 // Deregister in scripting api
938 m_script->removeObjectReference(obj);
940 // Delete active object
941 if(obj->environmentDeletes())
943 // Id to be removed from m_active_objects
944 objects_to_remove.push_back(id);
946 // Remove references from m_active_objects
947 for(std::list<u16>::iterator i = objects_to_remove.begin();
948 i != objects_to_remove.end(); ++i)
950 m_active_objects.erase(*i);
953 // Get list of loaded blocks
954 std::list<v3s16> loaded_blocks;
955 infostream<<"ServerEnvironment::clearAllObjects(): "
956 <<"Listing all loaded blocks"<<std::endl;
957 m_map->listAllLoadedBlocks(loaded_blocks);
958 infostream<<"ServerEnvironment::clearAllObjects(): "
959 <<"Done listing all loaded blocks: "
960 <<loaded_blocks.size()<<std::endl;
962 // Get list of loadable blocks
963 std::list<v3s16> loadable_blocks;
964 infostream<<"ServerEnvironment::clearAllObjects(): "
965 <<"Listing all loadable blocks"<<std::endl;
966 m_map->listAllLoadableBlocks(loadable_blocks);
967 infostream<<"ServerEnvironment::clearAllObjects(): "
968 <<"Done listing all loadable blocks: "
969 <<loadable_blocks.size()
970 <<", now clearing"<<std::endl;
972 // Grab a reference on each loaded block to avoid unloading it
973 for(std::list<v3s16>::iterator i = loaded_blocks.begin();
974 i != loaded_blocks.end(); ++i)
977 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
982 // Remove objects in all loadable blocks
983 u32 unload_interval = g_settings->getS32("max_clearobjects_extra_loaded_blocks");
984 unload_interval = MYMAX(unload_interval, 1);
985 u32 report_interval = loadable_blocks.size() / 10;
986 u32 num_blocks_checked = 0;
987 u32 num_blocks_cleared = 0;
988 u32 num_objs_cleared = 0;
989 for(std::list<v3s16>::iterator i = loadable_blocks.begin();
990 i != loadable_blocks.end(); ++i)
993 MapBlock *block = m_map->emergeBlock(p, false);
995 errorstream<<"ServerEnvironment::clearAllObjects(): "
996 <<"Failed to emerge block "<<PP(p)<<std::endl;
999 u32 num_stored = block->m_static_objects.m_stored.size();
1000 u32 num_active = block->m_static_objects.m_active.size();
1001 if(num_stored != 0 || num_active != 0){
1002 block->m_static_objects.m_stored.clear();
1003 block->m_static_objects.m_active.clear();
1004 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1006 num_objs_cleared += num_stored + num_active;
1007 num_blocks_cleared++;
1009 num_blocks_checked++;
1011 if(num_blocks_checked % report_interval == 0){
1012 float percent = 100.0 * (float)num_blocks_checked /
1013 loadable_blocks.size();
1014 infostream<<"ServerEnvironment::clearAllObjects(): "
1015 <<"Cleared "<<num_objs_cleared<<" objects"
1016 <<" in "<<num_blocks_cleared<<" blocks ("
1017 <<percent<<"%)"<<std::endl;
1019 if(num_blocks_checked % unload_interval == 0){
1020 m_map->unloadUnreferencedBlocks();
1023 m_map->unloadUnreferencedBlocks();
1025 // Drop references that were added above
1026 for(std::list<v3s16>::iterator i = loaded_blocks.begin();
1027 i != loaded_blocks.end(); ++i)
1030 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1035 infostream<<"ServerEnvironment::clearAllObjects(): "
1036 <<"Finished: Cleared "<<num_objs_cleared<<" objects"
1037 <<" in "<<num_blocks_cleared<<" blocks"<<std::endl;
1040 void ServerEnvironment::step(float dtime)
1042 DSTACK(__FUNCTION_NAME);
1044 //TimeTaker timer("ServerEnv step");
1046 /* Step time of day */
1047 stepTimeOfDay(dtime);
1050 // NOTE: This is kind of funny on a singleplayer game, but doesn't
1051 // really matter that much.
1052 m_recommended_send_interval = g_settings->getFloat("dedicated_server_step");
1058 m_game_time_fraction_counter += dtime;
1059 u32 inc_i = (u32)m_game_time_fraction_counter;
1060 m_game_time += inc_i;
1061 m_game_time_fraction_counter -= (float)inc_i;
1068 ScopeProfiler sp(g_profiler, "SEnv: handle players avg", SPT_AVG);
1069 for(std::list<Player*>::iterator i = m_players.begin();
1070 i != m_players.end(); ++i)
1072 Player *player = *i;
1074 // Ignore disconnected players
1075 if(player->peer_id == 0)
1079 player->move(dtime, *m_map, 100*BS);
1084 Manage active block list
1086 if(m_active_blocks_management_interval.step(dtime, 2.0))
1088 ScopeProfiler sp(g_profiler, "SEnv: manage act. block list avg /2s", SPT_AVG);
1090 Get player block positions
1092 std::list<v3s16> players_blockpos;
1093 for(std::list<Player*>::iterator
1094 i = m_players.begin();
1095 i != m_players.end(); ++i)
1097 Player *player = *i;
1098 // Ignore disconnected players
1099 if(player->peer_id == 0)
1101 v3s16 blockpos = getNodeBlockPos(
1102 floatToInt(player->getPosition(), BS));
1103 players_blockpos.push_back(blockpos);
1107 Update list of active blocks, collecting changes
1109 const s16 active_block_range = g_settings->getS16("active_block_range");
1110 std::set<v3s16> blocks_removed;
1111 std::set<v3s16> blocks_added;
1112 m_active_blocks.update(players_blockpos, active_block_range,
1113 blocks_removed, blocks_added);
1116 Handle removed blocks
1119 // Convert active objects that are no more in active blocks to static
1120 deactivateFarObjects(false);
1122 for(std::set<v3s16>::iterator
1123 i = blocks_removed.begin();
1124 i != blocks_removed.end(); ++i)
1128 /* infostream<<"Server: Block " << PP(p)
1129 << " became inactive"<<std::endl; */
1131 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1135 // Set current time as timestamp (and let it set ChangedFlag)
1136 block->setTimestamp(m_game_time);
1143 for(std::set<v3s16>::iterator
1144 i = blocks_added.begin();
1145 i != blocks_added.end(); ++i)
1149 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1151 // Block needs to be fetched first
1152 m_emerger->queueBlockEmerge(p, false);
1153 m_active_blocks.m_list.erase(p);
1157 activateBlock(block);
1158 /* infostream<<"Server: Block " << PP(p)
1159 << " became active"<<std::endl; */
1164 Mess around in active blocks
1166 if(m_active_blocks_nodemetadata_interval.step(dtime, 1.0))
1168 ScopeProfiler sp(g_profiler, "SEnv: mess in act. blocks avg /1s", SPT_AVG);
1172 for(std::set<v3s16>::iterator
1173 i = m_active_blocks.m_list.begin();
1174 i != m_active_blocks.m_list.end(); ++i)
1178 /*infostream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
1179 <<") being handled"<<std::endl;*/
1181 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1185 // Reset block usage timer
1186 block->resetUsageTimer();
1188 // Set current time as timestamp
1189 block->setTimestampNoChangedFlag(m_game_time);
1190 // If time has changed much from the one on disk,
1191 // set block to be saved when it is unloaded
1192 if(block->getTimestamp() > block->getDiskTimestamp() + 60)
1193 block->raiseModified(MOD_STATE_WRITE_AT_UNLOAD,
1194 "Timestamp older than 60s (step)");
1197 std::map<v3s16, NodeTimer> elapsed_timers =
1198 block->m_node_timers.step((float)dtime);
1199 if(!elapsed_timers.empty()){
1201 for(std::map<v3s16, NodeTimer>::iterator
1202 i = elapsed_timers.begin();
1203 i != elapsed_timers.end(); i++){
1204 n = block->getNodeNoEx(i->first);
1205 p = i->first + block->getPosRelative();
1206 if(m_script->node_on_timer(p,n,i->second.elapsed))
1207 block->setNodeTimer(i->first,NodeTimer(i->second.timeout,0));
1213 const float abm_interval = 1.0;
1214 if(m_active_block_modifier_interval.step(dtime, abm_interval))
1216 if(m_active_block_interval_overload_skip > 0){
1217 ScopeProfiler sp(g_profiler, "SEnv: ABM overload skips");
1218 m_active_block_interval_overload_skip--;
1221 ScopeProfiler sp(g_profiler, "SEnv: modify in blocks avg /1s", SPT_AVG);
1222 TimeTaker timer("modify in active blocks");
1224 // Initialize handling of ActiveBlockModifiers
1225 ABMHandler abmhandler(m_abms, abm_interval, this, true);
1227 for(std::set<v3s16>::iterator
1228 i = m_active_blocks.m_list.begin();
1229 i != m_active_blocks.m_list.end(); ++i)
1233 /*infostream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
1234 <<") being handled"<<std::endl;*/
1236 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1240 // Set current time as timestamp
1241 block->setTimestampNoChangedFlag(m_game_time);
1243 /* Handle ActiveBlockModifiers */
1244 abmhandler.apply(block);
1247 u32 time_ms = timer.stop(true);
1248 u32 max_time_ms = 200;
1249 if(time_ms > max_time_ms){
1250 infostream<<"WARNING: active block modifiers took "
1251 <<time_ms<<"ms (longer than "
1252 <<max_time_ms<<"ms)"<<std::endl;
1253 m_active_block_interval_overload_skip = (time_ms / max_time_ms) + 1;
1258 Step script environment (run global on_step())
1260 m_script->environment_Step(dtime);
1266 ScopeProfiler sp(g_profiler, "SEnv: step act. objs avg", SPT_AVG);
1267 //TimeTaker timer("Step active objects");
1269 g_profiler->avg("SEnv: num of objects", m_active_objects.size());
1271 // This helps the objects to send data at the same time
1272 bool send_recommended = false;
1273 m_send_recommended_timer += dtime;
1274 if(m_send_recommended_timer > getSendRecommendedInterval())
1276 m_send_recommended_timer -= getSendRecommendedInterval();
1277 send_recommended = true;
1280 for(std::map<u16, ServerActiveObject*>::iterator
1281 i = m_active_objects.begin();
1282 i != m_active_objects.end(); ++i)
1284 ServerActiveObject* obj = i->second;
1285 // Remove non-peaceful mobs on peaceful mode
1286 if(g_settings->getBool("only_peaceful_mobs")){
1287 if(!obj->isPeaceful())
1288 obj->m_removed = true;
1290 // Don't step if is to be removed or stored statically
1291 if(obj->m_removed || obj->m_pending_deactivation)
1294 obj->step(dtime, send_recommended);
1295 // Read messages from object
1296 while(!obj->m_messages_out.empty())
1298 m_active_object_messages.push_back(
1299 obj->m_messages_out.pop_front());
1305 Manage active objects
1307 if(m_object_management_interval.step(dtime, 0.5))
1309 ScopeProfiler sp(g_profiler, "SEnv: remove removed objs avg /.5s", SPT_AVG);
1311 Remove objects that satisfy (m_removed && m_known_by_count==0)
1313 removeRemovedObjects();
1317 ServerActiveObject* ServerEnvironment::getActiveObject(u16 id)
1319 std::map<u16, ServerActiveObject*>::iterator n;
1320 n = m_active_objects.find(id);
1321 if(n == m_active_objects.end())
1326 bool isFreeServerActiveObjectId(u16 id,
1327 std::map<u16, ServerActiveObject*> &objects)
1332 return objects.find(id) == objects.end();
1335 u16 getFreeServerActiveObjectId(
1336 std::map<u16, ServerActiveObject*> &objects)
1338 //try to reuse id's as late as possible
1339 static u16 last_used_id = 0;
1340 u16 startid = last_used_id;
1344 if(isFreeServerActiveObjectId(last_used_id, objects))
1345 return last_used_id;
1347 if(last_used_id == startid)
1352 u16 ServerEnvironment::addActiveObject(ServerActiveObject *object)
1355 u16 id = addActiveObjectRaw(object, true, 0);
1360 bool ServerEnvironment::addActiveObjectAsStatic(ServerActiveObject *obj)
1364 v3f objectpos = obj->getBasePosition();
1366 // The block in which the object resides in
1367 v3s16 blockpos_o = getNodeBlockPos(floatToInt(objectpos, BS));
1370 Update the static data
1373 // Create new static object
1374 std::string staticdata = obj->getStaticData();
1375 StaticObject s_obj(obj->getType(), objectpos, staticdata);
1376 // Add to the block where the object is located in
1377 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1378 // Get or generate the block
1379 MapBlock *block = m_map->emergeBlock(blockpos);
1381 bool succeeded = false;
1385 block->m_static_objects.insert(0, s_obj);
1386 block->raiseModified(MOD_STATE_WRITE_AT_UNLOAD,
1387 "addActiveObjectAsStatic");
1391 infostream<<"ServerEnvironment::addActiveObjectAsStatic: "
1392 <<"Could not find or generate "
1393 <<"a block for storing static object"<<std::endl;
1397 if(obj->environmentDeletes())
1405 Finds out what new objects have been added to
1406 inside a radius around a position
1408 void ServerEnvironment::getAddedActiveObjects(v3s16 pos, s16 radius,
1409 std::set<u16> ¤t_objects,
1410 std::set<u16> &added_objects)
1412 v3f pos_f = intToFloat(pos, BS);
1413 f32 radius_f = radius * BS;
1415 Go through the object list,
1416 - discard m_removed objects,
1417 - discard objects that are too far away,
1418 - discard objects that are found in current_objects.
1419 - add remaining objects to added_objects
1421 for(std::map<u16, ServerActiveObject*>::iterator
1422 i = m_active_objects.begin();
1423 i != m_active_objects.end(); ++i)
1427 ServerActiveObject *object = i->second;
1430 // Discard if removed
1431 if(object->m_removed)
1433 if(object->unlimitedTransferDistance() == false){
1434 // Discard if too far
1435 f32 distance_f = object->getBasePosition().getDistanceFrom(pos_f);
1436 if(distance_f > radius_f)
1439 // Discard if already on current_objects
1440 std::set<u16>::iterator n;
1441 n = current_objects.find(id);
1442 if(n != current_objects.end())
1444 // Add to added_objects
1445 added_objects.insert(id);
1450 Finds out what objects have been removed from
1451 inside a radius around a position
1453 void ServerEnvironment::getRemovedActiveObjects(v3s16 pos, s16 radius,
1454 std::set<u16> ¤t_objects,
1455 std::set<u16> &removed_objects)
1457 v3f pos_f = intToFloat(pos, BS);
1458 f32 radius_f = radius * BS;
1460 Go through current_objects; object is removed if:
1461 - object is not found in m_active_objects (this is actually an
1462 error condition; objects should be set m_removed=true and removed
1463 only after all clients have been informed about removal), or
1464 - object has m_removed=true, or
1465 - object is too far away
1467 for(std::set<u16>::iterator
1468 i = current_objects.begin();
1469 i != current_objects.end(); ++i)
1472 ServerActiveObject *object = getActiveObject(id);
1475 infostream<<"ServerEnvironment::getRemovedActiveObjects():"
1476 <<" object in current_objects is NULL"<<std::endl;
1477 removed_objects.insert(id);
1481 if(object->m_removed)
1483 removed_objects.insert(id);
1487 // If transfer distance is unlimited, don't remove
1488 if(object->unlimitedTransferDistance())
1491 f32 distance_f = object->getBasePosition().getDistanceFrom(pos_f);
1493 if(distance_f >= radius_f)
1495 removed_objects.insert(id);
1503 ActiveObjectMessage ServerEnvironment::getActiveObjectMessage()
1505 if(m_active_object_messages.empty())
1506 return ActiveObjectMessage(0);
1508 return m_active_object_messages.pop_front();
1512 ************ Private methods *************
1515 u16 ServerEnvironment::addActiveObjectRaw(ServerActiveObject *object,
1516 bool set_changed, u32 dtime_s)
1519 if(object->getId() == 0){
1520 u16 new_id = getFreeServerActiveObjectId(m_active_objects);
1523 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1524 <<"no free ids available"<<std::endl;
1525 if(object->environmentDeletes())
1529 object->setId(new_id);
1532 verbosestream<<"ServerEnvironment::addActiveObjectRaw(): "
1533 <<"supplied with id "<<object->getId()<<std::endl;
1535 if(isFreeServerActiveObjectId(object->getId(), m_active_objects) == false)
1537 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1538 <<"id is not free ("<<object->getId()<<")"<<std::endl;
1539 if(object->environmentDeletes())
1543 /*infostream<<"ServerEnvironment::addActiveObjectRaw(): "
1544 <<"added (id="<<object->getId()<<")"<<std::endl;*/
1546 m_active_objects[object->getId()] = object;
1548 verbosestream<<"ServerEnvironment::addActiveObjectRaw(): "
1549 <<"Added id="<<object->getId()<<"; there are now "
1550 <<m_active_objects.size()<<" active objects."
1553 // Register reference in scripting api (must be done before post-init)
1554 m_script->addObjectReference(object);
1555 // Post-initialize object
1556 object->addedToEnvironment(dtime_s);
1558 // Add static data to block
1559 if(object->isStaticAllowed())
1561 // Add static object to active static list of the block
1562 v3f objectpos = object->getBasePosition();
1563 std::string staticdata = object->getStaticData();
1564 StaticObject s_obj(object->getType(), objectpos, staticdata);
1565 // Add to the block where the object is located in
1566 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1567 MapBlock *block = m_map->getBlockNoCreateNoEx(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");
1579 v3s16 p = floatToInt(objectpos, BS);
1580 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1581 <<"could not find block for storing id="<<object->getId()
1582 <<" statically (pos="<<PP(p)<<")"<<std::endl;
1586 return object->getId();
1590 Remove objects that satisfy (m_removed && m_known_by_count==0)
1592 void ServerEnvironment::removeRemovedObjects()
1594 std::list<u16> objects_to_remove;
1595 for(std::map<u16, ServerActiveObject*>::iterator
1596 i = m_active_objects.begin();
1597 i != m_active_objects.end(); ++i)
1600 ServerActiveObject* obj = i->second;
1601 // This shouldn't happen but check it
1604 infostream<<"NULL object found in ServerEnvironment"
1605 <<" while finding removed objects. id="<<id<<std::endl;
1606 // Id to be removed from m_active_objects
1607 objects_to_remove.push_back(id);
1612 We will delete objects that are marked as removed or thatare
1613 waiting for deletion after deactivation
1615 if(obj->m_removed == false && obj->m_pending_deactivation == false)
1619 Delete static data from block if is marked as removed
1621 if(obj->m_static_exists && obj->m_removed)
1623 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
1625 block->m_static_objects.remove(id);
1626 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1627 "removeRemovedObjects");
1628 obj->m_static_exists = false;
1630 infostream << "failed to emerge block from which "
1631 "an object to be removed was loaded from. id="<<id<<std::endl;
1635 // If m_known_by_count > 0, don't actually remove.
1636 if(obj->m_known_by_count > 0)
1639 // Tell the object about removal
1640 obj->removingFromEnvironment();
1641 // Deregister in scripting api
1642 m_script->removeObjectReference(obj);
1645 if(obj->environmentDeletes())
1647 // Id to be removed from m_active_objects
1648 objects_to_remove.push_back(id);
1650 // Remove references from m_active_objects
1651 for(std::list<u16>::iterator i = objects_to_remove.begin();
1652 i != objects_to_remove.end(); ++i)
1654 m_active_objects.erase(*i);
1658 static void print_hexdump(std::ostream &o, const std::string &data)
1660 const int linelength = 16;
1661 for(int l=0; ; l++){
1662 int i0 = linelength * l;
1663 bool at_end = false;
1664 int thislinelength = linelength;
1665 if(i0 + thislinelength > (int)data.size()){
1666 thislinelength = data.size() - i0;
1669 for(int di=0; di<linelength; di++){
1672 if(di<thislinelength)
1673 snprintf(buf, 4, "%.2x ", data[i]);
1675 snprintf(buf, 4, " ");
1679 for(int di=0; di<thislinelength; di++){
1693 Convert stored objects from blocks near the players to active.
1695 void ServerEnvironment::activateObjects(MapBlock *block, u32 dtime_s)
1699 // Ignore if no stored objects (to not set changed flag)
1700 if(block->m_static_objects.m_stored.size() == 0)
1702 verbosestream<<"ServerEnvironment::activateObjects(): "
1703 <<"activating objects of block "<<PP(block->getPos())
1704 <<" ("<<block->m_static_objects.m_stored.size()
1705 <<" objects)"<<std::endl;
1706 bool large_amount = (block->m_static_objects.m_stored.size() > g_settings->getU16("max_objects_per_block"));
1708 errorstream<<"suspiciously large amount of objects detected: "
1709 <<block->m_static_objects.m_stored.size()<<" in "
1710 <<PP(block->getPos())
1711 <<"; removing all of them."<<std::endl;
1712 // Clear stored list
1713 block->m_static_objects.m_stored.clear();
1714 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1715 "stored list cleared in activateObjects due to "
1716 "large amount of objects");
1719 // A list for objects that couldn't be converted to active for some
1720 // reason. They will be stored back.
1721 std::list<StaticObject> new_stored;
1722 // Loop through stored static objects
1723 for(std::list<StaticObject>::iterator
1724 i = block->m_static_objects.m_stored.begin();
1725 i != block->m_static_objects.m_stored.end(); ++i)
1727 /*infostream<<"Server: Creating an active object from "
1728 <<"static data"<<std::endl;*/
1729 StaticObject &s_obj = *i;
1730 // Create an active object from the data
1731 ServerActiveObject *obj = ServerActiveObject::create
1732 (s_obj.type, this, 0, s_obj.pos, s_obj.data);
1733 // If couldn't create object, store static data back.
1736 errorstream<<"ServerEnvironment::activateObjects(): "
1737 <<"failed to create active object from static object "
1738 <<"in block "<<PP(s_obj.pos/BS)
1739 <<" type="<<(int)s_obj.type<<" data:"<<std::endl;
1740 print_hexdump(verbosestream, s_obj.data);
1742 new_stored.push_back(s_obj);
1745 verbosestream<<"ServerEnvironment::activateObjects(): "
1746 <<"activated static object pos="<<PP(s_obj.pos/BS)
1747 <<" type="<<(int)s_obj.type<<std::endl;
1748 // This will also add the object to the active static list
1749 addActiveObjectRaw(obj, false, dtime_s);
1751 // Clear stored list
1752 block->m_static_objects.m_stored.clear();
1753 // Add leftover failed stuff to stored list
1754 for(std::list<StaticObject>::iterator
1755 i = new_stored.begin();
1756 i != new_stored.end(); ++i)
1758 StaticObject &s_obj = *i;
1759 block->m_static_objects.m_stored.push_back(s_obj);
1762 Note: Block hasn't really been modified here.
1763 The objects have just been activated and moved from the stored
1764 static list to the active static list.
1765 As such, the block is essentially the same.
1766 Thus, do not call block->raiseModified(MOD_STATE_WRITE_NEEDED).
1767 Otherwise there would be a huge amount of unnecessary I/O.
1772 Convert objects that are not standing inside active blocks to static.
1774 If m_known_by_count != 0, active object is not deleted, but static
1775 data is still updated.
1777 If force_delete is set, active object is deleted nevertheless. It
1778 shall only be set so in the destructor of the environment.
1780 If block wasn't generated (not in memory or on disk),
1782 void ServerEnvironment::deactivateFarObjects(bool force_delete)
1784 std::list<u16> objects_to_remove;
1785 for(std::map<u16, ServerActiveObject*>::iterator
1786 i = m_active_objects.begin();
1787 i != m_active_objects.end(); ++i)
1789 ServerActiveObject* obj = i->second;
1792 // Do not deactivate if static data creation not allowed
1793 if(!force_delete && !obj->isStaticAllowed())
1796 // If pending deactivation, let removeRemovedObjects() do it
1797 if(!force_delete && obj->m_pending_deactivation)
1801 v3f objectpos = obj->getBasePosition();
1803 // The block in which the object resides in
1804 v3s16 blockpos_o = getNodeBlockPos(floatToInt(objectpos, BS));
1806 // If block is active, don't remove
1807 if(!force_delete && m_active_blocks.contains(blockpos_o))
1810 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
1811 <<"deactivating object id="<<id<<" on inactive block "
1812 <<PP(blockpos_o)<<std::endl;
1814 // If known by some client, don't immediately delete.
1815 bool pending_delete = (obj->m_known_by_count > 0 && !force_delete);
1818 Update the static data
1821 if(obj->isStaticAllowed())
1823 // Create new static object
1824 std::string staticdata_new = obj->getStaticData();
1825 StaticObject s_obj(obj->getType(), objectpos, staticdata_new);
1827 bool stays_in_same_block = false;
1828 bool data_changed = true;
1830 if(obj->m_static_exists){
1831 if(obj->m_static_block == blockpos_o)
1832 stays_in_same_block = true;
1834 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
1836 std::map<u16, StaticObject>::iterator n =
1837 block->m_static_objects.m_active.find(id);
1838 if(n != block->m_static_objects.m_active.end()){
1839 StaticObject static_old = n->second;
1841 float save_movem = obj->getMinimumSavedMovement();
1843 if(static_old.data == staticdata_new &&
1844 (static_old.pos - objectpos).getLength() < save_movem)
1845 data_changed = false;
1847 errorstream<<"ServerEnvironment::deactivateFarObjects(): "
1848 <<"id="<<id<<" m_static_exists=true but "
1849 <<"static data doesn't actually exist in "
1850 <<PP(obj->m_static_block)<<std::endl;
1854 bool shall_be_written = (!stays_in_same_block || data_changed);
1856 // Delete old static object
1857 if(obj->m_static_exists)
1859 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
1862 block->m_static_objects.remove(id);
1863 obj->m_static_exists = false;
1864 // Only mark block as modified if data changed considerably
1865 if(shall_be_written)
1866 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1867 "deactivateFarObjects: Static data "
1868 "changed considerably");
1872 // Add to the block where the object is located in
1873 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1874 // Get or generate the block
1875 MapBlock *block = NULL;
1877 block = m_map->emergeBlock(blockpos);
1878 } catch(InvalidPositionException &e){
1879 // Handled via NULL pointer
1884 if(block->m_static_objects.m_stored.size() >= g_settings->getU16("max_objects_per_block")){
1885 errorstream<<"ServerEnv: Trying to store id="<<obj->getId()
1886 <<" statically but block "<<PP(blockpos)
1887 <<" already contains "
1888 <<block->m_static_objects.m_stored.size()
1890 <<" Forcing delete."<<std::endl;
1891 force_delete = true;
1893 // If static counterpart already exists, remove it first.
1894 // This shouldn't happen, but happens rarely for some
1895 // unknown reason. Unsuccessful attempts have been made to
1896 // find said reason.
1897 if(id && block->m_static_objects.m_active.find(id) != block->m_static_objects.m_active.end()){
1898 infostream<<"ServerEnv: WARNING: Performing hack #83274"
1900 block->m_static_objects.remove(id);
1903 block->m_static_objects.insert(0, s_obj);
1905 // Only mark block as modified if data changed considerably
1906 if(shall_be_written)
1907 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1908 "deactivateFarObjects: Static data "
1909 "changed considerably");
1911 obj->m_static_exists = true;
1912 obj->m_static_block = block->getPos();
1917 v3s16 p = floatToInt(objectpos, BS);
1918 errorstream<<"ServerEnv: Could not find or generate "
1919 <<"a block for storing id="<<obj->getId()
1920 <<" statically (pos="<<PP(p)<<")"<<std::endl;
1927 If known by some client, set pending deactivation.
1928 Otherwise delete it immediately.
1931 if(pending_delete && !force_delete)
1933 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
1934 <<"object id="<<id<<" is known by clients"
1935 <<"; not deleting yet"<<std::endl;
1937 obj->m_pending_deactivation = true;
1941 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
1942 <<"object id="<<id<<" is not known by clients"
1943 <<"; deleting"<<std::endl;
1945 // Tell the object about removal
1946 obj->removingFromEnvironment();
1947 // Deregister in scripting api
1948 m_script->removeObjectReference(obj);
1950 // Delete active object
1951 if(obj->environmentDeletes())
1953 // Id to be removed from m_active_objects
1954 objects_to_remove.push_back(id);
1957 // Remove references from m_active_objects
1958 for(std::list<u16>::iterator i = objects_to_remove.begin();
1959 i != objects_to_remove.end(); ++i)
1961 m_active_objects.erase(*i);
1968 #include "clientsimpleobject.h"
1974 ClientEnvironment::ClientEnvironment(ClientMap *map, scene::ISceneManager *smgr,
1975 ITextureSource *texturesource, IGameDef *gamedef,
1976 IrrlichtDevice *irr):
1979 m_texturesource(texturesource),
1985 ClientEnvironment::~ClientEnvironment()
1987 // delete active objects
1988 for(std::map<u16, ClientActiveObject*>::iterator
1989 i = m_active_objects.begin();
1990 i != m_active_objects.end(); ++i)
1995 for(std::list<ClientSimpleObject*>::iterator
1996 i = m_simple_objects.begin(); i != m_simple_objects.end(); ++i)
2005 Map & ClientEnvironment::getMap()
2010 ClientMap & ClientEnvironment::getClientMap()
2015 void ClientEnvironment::addPlayer(Player *player)
2017 DSTACK(__FUNCTION_NAME);
2019 It is a failure if player is local and there already is a local
2022 assert(!(player->isLocal() == true && getLocalPlayer() != NULL));
2024 Environment::addPlayer(player);
2027 LocalPlayer * ClientEnvironment::getLocalPlayer()
2029 for(std::list<Player*>::iterator i = m_players.begin();
2030 i != m_players.end(); ++i)
2032 Player *player = *i;
2033 if(player->isLocal())
2034 return (LocalPlayer*)player;
2039 void ClientEnvironment::step(float dtime)
2041 DSTACK(__FUNCTION_NAME);
2043 /* Step time of day */
2044 stepTimeOfDay(dtime);
2046 // Get some settings
2047 bool fly_allowed = m_gamedef->checkLocalPrivilege("fly");
2048 bool free_move = fly_allowed && g_settings->getBool("free_move");
2051 LocalPlayer *lplayer = getLocalPlayer();
2053 // collision info queue
2054 std::list<CollisionInfo> player_collisions;
2057 Get the speed the player is going
2059 bool is_climbing = lplayer->is_climbing;
2061 f32 player_speed = lplayer->getSpeed().getLength();
2064 Maximum position increment
2066 //f32 position_max_increment = 0.05*BS;
2067 f32 position_max_increment = 0.1*BS;
2069 // Maximum time increment (for collision detection etc)
2070 // time = distance / speed
2071 f32 dtime_max_increment = 1;
2072 if(player_speed > 0.001)
2073 dtime_max_increment = position_max_increment / player_speed;
2075 // Maximum time increment is 10ms or lower
2076 if(dtime_max_increment > 0.01)
2077 dtime_max_increment = 0.01;
2079 // Don't allow overly huge dtime
2083 f32 dtime_downcount = dtime;
2086 Stuff that has a maximum time increment
2095 if(dtime_downcount > dtime_max_increment)
2097 dtime_part = dtime_max_increment;
2098 dtime_downcount -= dtime_part;
2102 dtime_part = dtime_downcount;
2104 Setting this to 0 (no -=dtime_part) disables an infinite loop
2105 when dtime_part is so small that dtime_downcount -= dtime_part
2108 dtime_downcount = 0;
2117 if(free_move == false && is_climbing == false)
2120 v3f speed = lplayer->getSpeed();
2121 if(lplayer->in_liquid == false)
2122 speed.Y -= lplayer->movement_gravity * lplayer->physics_override_gravity * dtime_part * 2;
2124 // Liquid floating / sinking
2125 if(lplayer->in_liquid && !lplayer->swimming_vertical)
2126 speed.Y -= lplayer->movement_liquid_sink * dtime_part * 2;
2128 // Liquid resistance
2129 if(lplayer->in_liquid_stable || lplayer->in_liquid)
2131 // How much the node's viscosity blocks movement, ranges between 0 and 1
2132 // Should match the scale at which viscosity increase affects other liquid attributes
2133 const f32 viscosity_factor = 0.3;
2135 v3f d_wanted = -speed / lplayer->movement_liquid_fluidity;
2136 f32 dl = d_wanted.getLength();
2137 if(dl > lplayer->movement_liquid_fluidity_smooth)
2138 dl = lplayer->movement_liquid_fluidity_smooth;
2139 dl *= (lplayer->liquid_viscosity * viscosity_factor) + (1 - viscosity_factor);
2141 v3f d = d_wanted.normalize() * dl;
2145 if(speed.X > lplayer->movement_liquid_fluidity + lplayer->movement_liquid_fluidity_smooth) speed.X -= lplayer->movement_liquid_fluidity_smooth;
2146 if(speed.X < -lplayer->movement_liquid_fluidity - lplayer->movement_liquid_fluidity_smooth) speed.X += lplayer->movement_liquid_fluidity_smooth;
2147 if(speed.Y > lplayer->movement_liquid_fluidity + lplayer->movement_liquid_fluidity_smooth) speed.Y -= lplayer->movement_liquid_fluidity_smooth;
2148 if(speed.Y < -lplayer->movement_liquid_fluidity - lplayer->movement_liquid_fluidity_smooth) speed.Y += lplayer->movement_liquid_fluidity_smooth;
2149 if(speed.Z > lplayer->movement_liquid_fluidity + lplayer->movement_liquid_fluidity_smooth) speed.Z -= lplayer->movement_liquid_fluidity_smooth;
2150 if(speed.Z < -lplayer->movement_liquid_fluidity - lplayer->movement_liquid_fluidity_smooth) speed.Z += lplayer->movement_liquid_fluidity_smooth;
2154 lplayer->setSpeed(speed);
2159 This also does collision detection.
2161 lplayer->move(dtime_part, this, position_max_increment,
2162 &player_collisions);
2165 while(dtime_downcount > 0.001);
2167 //std::cout<<"Looped "<<loopcount<<" times."<<std::endl;
2169 for(std::list<CollisionInfo>::iterator
2170 i = player_collisions.begin();
2171 i != player_collisions.end(); ++i)
2173 CollisionInfo &info = *i;
2174 v3f speed_diff = info.new_speed - info.old_speed;;
2175 // Handle only fall damage
2176 // (because otherwise walking against something in fast_move kills you)
2177 if(speed_diff.Y < 0 || info.old_speed.Y >= 0)
2179 // Get rid of other components
2182 f32 pre_factor = 1; // 1 hp per node/s
2183 f32 tolerance = BS*14; // 5 without damage
2184 f32 post_factor = 1; // 1 hp per node/s
2185 if(info.type == COLLISION_NODE)
2187 const ContentFeatures &f = m_gamedef->ndef()->
2188 get(m_map->getNodeNoEx(info.node_p));
2189 // Determine fall damage multiplier
2190 int addp = itemgroup_get(f.groups, "fall_damage_add_percent");
2191 pre_factor = 1.0 + (float)addp/100.0;
2193 float speed = pre_factor * speed_diff.getLength();
2194 if(speed > tolerance)
2196 f32 damage_f = (speed - tolerance)/BS * post_factor;
2197 u16 damage = (u16)(damage_f+0.5);
2199 damageLocalPlayer(damage, true);
2200 MtEvent *e = new SimpleTriggerEvent("PlayerFallingDamage");
2201 m_gamedef->event()->put(e);
2207 A quick draft of lava damage
2209 if(m_lava_hurt_interval.step(dtime, 1.0))
2211 v3f pf = lplayer->getPosition();
2213 // Feet, middle and head
2214 v3s16 p1 = floatToInt(pf + v3f(0, BS*0.1, 0), BS);
2215 MapNode n1 = m_map->getNodeNoEx(p1);
2216 v3s16 p2 = floatToInt(pf + v3f(0, BS*0.8, 0), BS);
2217 MapNode n2 = m_map->getNodeNoEx(p2);
2218 v3s16 p3 = floatToInt(pf + v3f(0, BS*1.6, 0), BS);
2219 MapNode n3 = m_map->getNodeNoEx(p3);
2221 u32 damage_per_second = 0;
2222 damage_per_second = MYMAX(damage_per_second,
2223 m_gamedef->ndef()->get(n1).damage_per_second);
2224 damage_per_second = MYMAX(damage_per_second,
2225 m_gamedef->ndef()->get(n2).damage_per_second);
2226 damage_per_second = MYMAX(damage_per_second,
2227 m_gamedef->ndef()->get(n3).damage_per_second);
2229 if(damage_per_second != 0)
2231 damageLocalPlayer(damage_per_second, true);
2238 if(m_drowning_interval.step(dtime, 2.0))
2240 v3f pf = lplayer->getPosition();
2243 v3s16 p = floatToInt(pf + v3f(0, BS*1.6, 0), BS);
2244 MapNode n = m_map->getNodeNoEx(p);
2245 ContentFeatures c = m_gamedef->ndef()->get(n);
2246 u8 drowning_damage = c.drowning;
2247 if(drowning_damage > 0 && lplayer->hp > 0){
2248 u16 breath = lplayer->getBreath();
2255 lplayer->setBreath(breath);
2256 updateLocalPlayerBreath(breath);
2259 if(lplayer->getBreath() == 0 && drowning_damage > 0){
2260 damageLocalPlayer(drowning_damage, true);
2263 if(m_breathing_interval.step(dtime, 0.5))
2265 v3f pf = lplayer->getPosition();
2268 v3s16 p = floatToInt(pf + v3f(0, BS*1.6, 0), BS);
2269 MapNode n = m_map->getNodeNoEx(p);
2270 ContentFeatures c = m_gamedef->ndef()->get(n);
2272 lplayer->setBreath(11);
2274 else if(c.drowning == 0){
2275 u16 breath = lplayer->getBreath();
2278 lplayer->setBreath(breath);
2279 updateLocalPlayerBreath(breath);
2285 Stuff that can be done in an arbitarily large dtime
2287 for(std::list<Player*>::iterator i = m_players.begin();
2288 i != m_players.end(); ++i)
2290 Player *player = *i;
2293 Handle non-local players
2295 if(player->isLocal() == false)
2298 player->move(dtime, *m_map, 100*BS);
2302 // Update lighting on all players on client
2306 v3s16 p = player->getLightPosition();
2307 MapNode n = m_map->getNode(p);
2308 light = n.getLightBlendF1((float)getDayNightRatio()/1000, m_gamedef->ndef());
2310 catch(InvalidPositionException &e){
2311 light = blend_light_f1((float)getDayNightRatio()/1000, LIGHT_SUN, 0);
2313 player->light = light;
2317 Step active objects and update lighting of them
2320 g_profiler->avg("CEnv: num of objects", m_active_objects.size());
2321 bool update_lighting = m_active_object_light_update_interval.step(dtime, 0.21);
2322 for(std::map<u16, ClientActiveObject*>::iterator
2323 i = m_active_objects.begin();
2324 i != m_active_objects.end(); ++i)
2326 ClientActiveObject* obj = i->second;
2328 obj->step(dtime, this);
2336 v3s16 p = obj->getLightPosition();
2337 MapNode n = m_map->getNode(p);
2338 light = n.getLightBlend(getDayNightRatio(), m_gamedef->ndef());
2340 catch(InvalidPositionException &e){
2341 light = blend_light(getDayNightRatio(), LIGHT_SUN, 0);
2343 obj->updateLight(light);
2348 Step and handle simple objects
2350 g_profiler->avg("CEnv: num of simple objects", m_simple_objects.size());
2351 for(std::list<ClientSimpleObject*>::iterator
2352 i = m_simple_objects.begin(); i != m_simple_objects.end();)
2354 ClientSimpleObject *simple = *i;
2355 std::list<ClientSimpleObject*>::iterator cur = i;
2357 simple->step(dtime);
2358 if(simple->m_to_be_removed){
2360 m_simple_objects.erase(cur);
2365 void ClientEnvironment::addSimpleObject(ClientSimpleObject *simple)
2367 m_simple_objects.push_back(simple);
2370 ClientActiveObject* ClientEnvironment::getActiveObject(u16 id)
2372 std::map<u16, ClientActiveObject*>::iterator n;
2373 n = m_active_objects.find(id);
2374 if(n == m_active_objects.end())
2379 bool isFreeClientActiveObjectId(u16 id,
2380 std::map<u16, ClientActiveObject*> &objects)
2385 return objects.find(id) == objects.end();
2388 u16 getFreeClientActiveObjectId(
2389 std::map<u16, ClientActiveObject*> &objects)
2391 //try to reuse id's as late as possible
2392 static u16 last_used_id = 0;
2393 u16 startid = last_used_id;
2397 if(isFreeClientActiveObjectId(last_used_id, objects))
2398 return last_used_id;
2400 if(last_used_id == startid)
2405 u16 ClientEnvironment::addActiveObject(ClientActiveObject *object)
2408 if(object->getId() == 0)
2410 u16 new_id = getFreeClientActiveObjectId(m_active_objects);
2413 infostream<<"ClientEnvironment::addActiveObject(): "
2414 <<"no free ids available"<<std::endl;
2418 object->setId(new_id);
2420 if(isFreeClientActiveObjectId(object->getId(), m_active_objects) == false)
2422 infostream<<"ClientEnvironment::addActiveObject(): "
2423 <<"id is not free ("<<object->getId()<<")"<<std::endl;
2427 infostream<<"ClientEnvironment::addActiveObject(): "
2428 <<"added (id="<<object->getId()<<")"<<std::endl;
2429 m_active_objects[object->getId()] = object;
2430 object->addToScene(m_smgr, m_texturesource, m_irr);
2431 { // Update lighting immediately
2435 v3s16 p = object->getLightPosition();
2436 MapNode n = m_map->getNode(p);
2437 light = n.getLightBlend(getDayNightRatio(), m_gamedef->ndef());
2439 catch(InvalidPositionException &e){
2440 light = blend_light(getDayNightRatio(), LIGHT_SUN, 0);
2442 object->updateLight(light);
2444 return object->getId();
2447 void ClientEnvironment::addActiveObject(u16 id, u8 type,
2448 const std::string &init_data)
2450 ClientActiveObject* obj =
2451 ClientActiveObject::create(type, m_gamedef, this);
2454 infostream<<"ClientEnvironment::addActiveObject(): "
2455 <<"id="<<id<<" type="<<type<<": Couldn't create object"
2464 obj->initialize(init_data);
2466 catch(SerializationError &e)
2468 errorstream<<"ClientEnvironment::addActiveObject():"
2469 <<" id="<<id<<" type="<<type
2470 <<": SerializationError in initialize(): "
2472 <<": init_data="<<serializeJsonString(init_data)
2476 addActiveObject(obj);
2479 void ClientEnvironment::removeActiveObject(u16 id)
2481 verbosestream<<"ClientEnvironment::removeActiveObject(): "
2482 <<"id="<<id<<std::endl;
2483 ClientActiveObject* obj = getActiveObject(id);
2486 infostream<<"ClientEnvironment::removeActiveObject(): "
2487 <<"id="<<id<<" not found"<<std::endl;
2490 obj->removeFromScene(true);
2492 m_active_objects.erase(id);
2495 void ClientEnvironment::processActiveObjectMessage(u16 id,
2496 const std::string &data)
2498 ClientActiveObject* obj = getActiveObject(id);
2501 infostream<<"ClientEnvironment::processActiveObjectMessage():"
2502 <<" got message for id="<<id<<", which doesn't exist."
2508 obj->processMessage(data);
2510 catch(SerializationError &e)
2512 errorstream<<"ClientEnvironment::processActiveObjectMessage():"
2513 <<" id="<<id<<" type="<<obj->getType()
2514 <<" SerializationError in processMessage(),"
2515 <<" message="<<serializeJsonString(data)
2521 Callbacks for activeobjects
2524 void ClientEnvironment::damageLocalPlayer(u8 damage, bool handle_hp)
2526 LocalPlayer *lplayer = getLocalPlayer();
2530 if(lplayer->hp > damage)
2531 lplayer->hp -= damage;
2536 ClientEnvEvent event;
2537 event.type = CEE_PLAYER_DAMAGE;
2538 event.player_damage.amount = damage;
2539 event.player_damage.send_to_server = handle_hp;
2540 m_client_event_queue.push_back(event);
2543 void ClientEnvironment::updateLocalPlayerBreath(u16 breath)
2545 ClientEnvEvent event;
2546 event.type = CEE_PLAYER_BREATH;
2547 event.player_breath.amount = breath;
2548 m_client_event_queue.push_back(event);
2552 Client likes to call these
2555 void ClientEnvironment::getActiveObjects(v3f origin, f32 max_d,
2556 std::vector<DistanceSortedActiveObject> &dest)
2558 for(std::map<u16, ClientActiveObject*>::iterator
2559 i = m_active_objects.begin();
2560 i != m_active_objects.end(); ++i)
2562 ClientActiveObject* obj = i->second;
2564 f32 d = (obj->getPosition() - origin).getLength();
2569 DistanceSortedActiveObject dso(obj, d);
2571 dest.push_back(dso);
2575 ClientEnvEvent ClientEnvironment::getClientEvent()
2577 if(m_client_event_queue.empty())
2579 ClientEnvEvent event;
2580 event.type = CEE_NONE;
2583 return m_client_event_queue.pop_front();
2586 #endif // #ifndef SERVER