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"
34 #include "cpp_api/scriptapi.h"
36 #include "nodemetadata.h"
37 #include "main.h" // For g_settings, g_profiler
40 #include "clientmap.h"
41 #include "localplayer.h"
44 #include "daynightratio.h"
46 #include "util/serialize.h"
48 #define PP(x) "("<<(x).X<<","<<(x).Y<<","<<(x).Z<<")"
50 Environment::Environment():
52 m_time_of_day_f(9000./24000),
53 m_time_of_day_speed(0),
58 Environment::~Environment()
61 for(std::list<Player*>::iterator i = m_players.begin();
62 i != m_players.end(); ++i)
68 void Environment::addPlayer(Player *player)
70 DSTACK(__FUNCTION_NAME);
72 Check that peer_ids are unique.
73 Also check that names are unique.
74 Exception: there can be multiple players with peer_id=0
76 // If peer id is non-zero, it has to be unique.
77 if(player->peer_id != 0)
78 assert(getPlayer(player->peer_id) == NULL);
79 // Name has to be unique.
80 assert(getPlayer(player->getName()) == NULL);
82 m_players.push_back(player);
85 void Environment::removePlayer(u16 peer_id)
87 DSTACK(__FUNCTION_NAME);
89 for(std::list<Player*>::iterator i = m_players.begin();
90 i != m_players.end(); ++i)
93 if(player->peer_id != peer_id)
98 // See if there is an another one
99 // (shouldn't be, but just to be sure)
104 Player * Environment::getPlayer(u16 peer_id)
106 for(std::list<Player*>::iterator i = m_players.begin();
107 i != m_players.end(); ++i)
110 if(player->peer_id == peer_id)
116 Player * Environment::getPlayer(const char *name)
118 for(std::list<Player*>::iterator i = m_players.begin();
119 i != m_players.end(); ++i)
122 if(strcmp(player->getName(), name) == 0)
128 Player * Environment::getRandomConnectedPlayer()
130 std::list<Player*> connected_players = getPlayers(true);
131 u32 chosen_one = myrand() % connected_players.size();
133 for(std::list<Player*>::iterator
134 i = connected_players.begin();
135 i != connected_players.end(); ++i)
147 Player * Environment::getNearestConnectedPlayer(v3f pos)
149 std::list<Player*> connected_players = getPlayers(true);
151 Player *nearest_player = NULL;
152 for(std::list<Player*>::iterator
153 i = connected_players.begin();
154 i != connected_players.end(); ++i)
157 f32 d = player->getPosition().getDistanceFrom(pos);
158 if(d < nearest_d || nearest_player == NULL)
161 nearest_player = player;
164 return nearest_player;
167 std::list<Player*> Environment::getPlayers()
172 std::list<Player*> Environment::getPlayers(bool ignore_disconnected)
174 std::list<Player*> newlist;
175 for(std::list<Player*>::iterator
176 i = m_players.begin();
177 i != m_players.end(); ++i)
181 if(ignore_disconnected)
183 // Ignore disconnected players
184 if(player->peer_id == 0)
188 newlist.push_back(player);
193 void Environment::printPlayers(std::ostream &o)
195 o<<"Players in environment:"<<std::endl;
196 for(std::list<Player*>::iterator i = m_players.begin();
197 i != m_players.end(); i++)
200 o<<"Player peer_id="<<player->peer_id<<std::endl;
204 u32 Environment::getDayNightRatio()
206 bool smooth = g_settings->getBool("enable_shaders");
207 return time_to_daynight_ratio(m_time_of_day_f*24000, smooth);
210 void Environment::stepTimeOfDay(float dtime)
212 m_time_counter += dtime;
213 f32 speed = m_time_of_day_speed * 24000./(24.*3600);
214 u32 units = (u32)(m_time_counter*speed);
215 m_time_counter -= (f32)units / speed;
219 if(m_time_of_day + units >= 24000)
221 m_time_of_day = (m_time_of_day + units) % 24000;
223 m_time_of_day_f = (float)m_time_of_day / 24000.0;
226 m_time_of_day_f += m_time_of_day_speed/24/3600*dtime;
227 if(m_time_of_day_f > 1.0)
228 m_time_of_day_f -= 1.0;
229 if(m_time_of_day_f < 0.0)
230 m_time_of_day_f += 1.0;
238 ABMWithState::ABMWithState(ActiveBlockModifier *abm_):
242 // Initialize timer to random value to spread processing
243 float itv = abm->getTriggerInterval();
244 itv = MYMAX(0.001, itv); // No less than 1ms
245 int minval = MYMAX(-0.51*itv, -60); // Clamp to
246 int maxval = MYMIN(0.51*itv, 60); // +-60 seconds
247 timer = myrand_range(minval, maxval);
254 void fillRadiusBlock(v3s16 p0, s16 r, std::set<v3s16> &list)
257 for(p.X=p0.X-r; p.X<=p0.X+r; p.X++)
258 for(p.Y=p0.Y-r; p.Y<=p0.Y+r; p.Y++)
259 for(p.Z=p0.Z-r; p.Z<=p0.Z+r; p.Z++)
266 void ActiveBlockList::update(std::list<v3s16> &active_positions,
268 std::set<v3s16> &blocks_removed,
269 std::set<v3s16> &blocks_added)
274 std::set<v3s16> newlist;
275 for(std::list<v3s16>::iterator i = active_positions.begin();
276 i != active_positions.end(); ++i)
278 fillRadiusBlock(*i, radius, newlist);
282 Find out which blocks on the old list are not on the new list
284 // Go through old list
285 for(std::set<v3s16>::iterator i = m_list.begin();
286 i != m_list.end(); ++i)
289 // If not on new list, it's been removed
290 if(newlist.find(p) == newlist.end())
291 blocks_removed.insert(p);
295 Find out which blocks on the new list are not on the old list
297 // Go through new list
298 for(std::set<v3s16>::iterator i = newlist.begin();
299 i != newlist.end(); ++i)
302 // If not on old list, it's been added
303 if(m_list.find(p) == m_list.end())
304 blocks_added.insert(p);
311 for(std::set<v3s16>::iterator i = newlist.begin();
312 i != newlist.end(); ++i)
323 ServerEnvironment::ServerEnvironment(ServerMap *map, ScriptApi *scriptIface,
324 IGameDef *gamedef, IBackgroundBlockEmerger *emerger):
326 m_script(scriptIface),
329 m_random_spawn_timer(3),
330 m_send_recommended_timer(0),
331 m_active_block_interval_overload_skip(0),
333 m_game_time_fraction_counter(0),
334 m_recommended_send_interval(0.1),
335 m_max_lag_estimate(0.1)
339 ServerEnvironment::~ServerEnvironment()
341 // Clear active block list.
342 // This makes the next one delete all active objects.
343 m_active_blocks.clear();
345 // Convert all objects to static and delete the active objects
346 deactivateFarObjects(true);
351 // Delete ActiveBlockModifiers
352 for(std::list<ABMWithState>::iterator
353 i = m_abms.begin(); i != m_abms.end(); ++i){
358 Map & ServerEnvironment::getMap()
363 ServerMap & ServerEnvironment::getServerMap()
368 bool ServerEnvironment::line_of_sight(v3f pos1, v3f pos2, float stepsize)
370 float distance = pos1.getDistanceFrom(pos2);
372 //calculate normalized direction vector
373 v3f normalized_vector = v3f((pos2.X - pos1.X)/distance,
374 (pos2.Y - pos1.Y)/distance,
375 (pos2.Z - pos1.Z)/distance);
377 //find out if there's a node on path between pos1 and pos2
378 for (float i = 1; i < distance; i += stepsize) {
379 v3s16 pos = floatToInt(v3f(normalized_vector.X * i,
380 normalized_vector.Y * i,
381 normalized_vector.Z * i) +pos1,BS);
383 MapNode n = getMap().getNodeNoEx(pos);
385 if(n.param0 != CONTENT_AIR) {
392 void ServerEnvironment::serializePlayers(const std::string &savedir)
394 std::string players_path = savedir + "/players";
395 fs::CreateDir(players_path);
397 std::set<Player*> saved_players;
399 std::vector<fs::DirListNode> player_files = fs::GetDirListing(players_path);
400 for(u32 i=0; i<player_files.size(); i++)
402 if(player_files[i].dir || player_files[i].name[0] == '.')
405 // Full path to this file
406 std::string path = players_path + "/" + player_files[i].name;
408 //infostream<<"Checking player file "<<path<<std::endl;
410 // Load player to see what is its name
411 RemotePlayer testplayer(m_gamedef);
413 // Open file and deserialize
414 std::ifstream is(path.c_str(), std::ios_base::binary);
415 if(is.good() == false)
417 infostream<<"Failed to read "<<path<<std::endl;
420 testplayer.deSerialize(is, player_files[i].name);
423 //infostream<<"Loaded test player with name "<<testplayer.getName()<<std::endl;
425 // Search for the player
426 std::string playername = testplayer.getName();
427 Player *player = getPlayer(playername.c_str());
430 infostream<<"Didn't find matching player, ignoring file "<<path<<std::endl;
434 //infostream<<"Found matching player, overwriting."<<std::endl;
436 // OK, found. Save player there.
437 if(player->checkModified())
439 // Open file and serialize
440 std::ofstream os(path.c_str(), std::ios_base::binary);
441 if(os.good() == false)
443 infostream<<"Failed to overwrite "<<path<<std::endl;
446 player->serialize(os);
447 saved_players.insert(player);
449 saved_players.insert(player);
453 for(std::list<Player*>::iterator i = m_players.begin();
454 i != m_players.end(); ++i)
457 if(saved_players.find(player) != saved_players.end())
459 /*infostream<<"Player "<<player->getName()
460 <<" was already saved."<<std::endl;*/
463 std::string playername = player->getName();
464 // Don't save unnamed player
467 //infostream<<"Not saving unnamed player."<<std::endl;
473 if(string_allowed(playername, PLAYERNAME_ALLOWED_CHARS) == false)
474 playername = "player";
475 std::string path = players_path + "/" + playername;
477 for(u32 i=0; i<1000; i++)
479 if(fs::PathExists(path) == false)
484 path = players_path + "/" + playername + itos(i);
488 infostream<<"Didn't find free file for player"<<std::endl;
493 /*infostream<<"Saving player "<<player->getName()<<" to "
495 // Open file and serialize
496 std::ofstream os(path.c_str(), std::ios_base::binary);
497 if(os.good() == false)
499 infostream<<"Failed to overwrite "<<path<<std::endl;
502 player->serialize(os);
503 saved_players.insert(player);
507 //infostream<<"Saved "<<saved_players.size()<<" players."<<std::endl;
510 void ServerEnvironment::deSerializePlayers(const std::string &savedir)
512 std::string players_path = savedir + "/players";
514 std::vector<fs::DirListNode> player_files = fs::GetDirListing(players_path);
515 for(u32 i=0; i<player_files.size(); i++)
517 if(player_files[i].dir)
520 // Full path to this file
521 std::string path = players_path + "/" + player_files[i].name;
523 //infostream<<"Checking player file "<<path<<std::endl;
525 // Load player to see what is its name
526 RemotePlayer testplayer(m_gamedef);
528 // Open file and deserialize
529 std::ifstream is(path.c_str(), std::ios_base::binary);
530 if(is.good() == false)
532 infostream<<"Failed to read "<<path<<std::endl;
535 testplayer.deSerialize(is, player_files[i].name);
538 if(!string_allowed(testplayer.getName(), PLAYERNAME_ALLOWED_CHARS))
540 infostream<<"Not loading player with invalid name: "
541 <<testplayer.getName()<<std::endl;
544 /*infostream<<"Loaded test player with name "<<testplayer.getName()
547 // Search for the player
548 std::string playername = testplayer.getName();
549 Player *player = getPlayer(playername.c_str());
550 bool newplayer = false;
553 //infostream<<"Is a new player"<<std::endl;
554 player = new RemotePlayer(m_gamedef);
560 verbosestream<<"Reading player "<<testplayer.getName()<<" from "
562 // Open file and deserialize
563 std::ifstream is(path.c_str(), std::ios_base::binary);
564 if(is.good() == false)
566 infostream<<"Failed to read "<<path<<std::endl;
569 player->deSerialize(is, player_files[i].name);
579 void ServerEnvironment::saveMeta(const std::string &savedir)
581 std::string path = savedir + "/env_meta.txt";
583 // Open file and serialize
584 std::ofstream os(path.c_str(), std::ios_base::binary);
585 if(os.good() == false)
587 infostream<<"ServerEnvironment::saveMeta(): Failed to open "
589 throw SerializationError("Couldn't save env meta");
593 args.setU64("game_time", m_game_time);
594 args.setU64("time_of_day", getTimeOfDay());
599 void ServerEnvironment::loadMeta(const std::string &savedir)
601 std::string path = savedir + "/env_meta.txt";
603 // Open file and deserialize
604 std::ifstream is(path.c_str(), std::ios_base::binary);
605 if(is.good() == false)
607 infostream<<"ServerEnvironment::loadMeta(): Failed to open "
609 throw SerializationError("Couldn't load env meta");
617 throw SerializationError
618 ("ServerEnvironment::loadMeta(): EnvArgsEnd not found");
620 std::getline(is, line);
621 std::string trimmedline = trim(line);
622 if(trimmedline == "EnvArgsEnd")
624 args.parseConfigLine(line);
628 m_game_time = args.getU64("game_time");
629 }catch(SettingNotFoundException &e){
630 // Getting this is crucial, otherwise timestamps are useless
631 throw SerializationError("Couldn't load env meta game_time");
635 m_time_of_day = args.getU64("time_of_day");
636 }catch(SettingNotFoundException &e){
637 // This is not as important
638 m_time_of_day = 9000;
644 ActiveBlockModifier *abm;
646 std::set<content_t> required_neighbors;
652 ServerEnvironment *m_env;
653 std::map<content_t, std::list<ActiveABM> > m_aabms;
655 ABMHandler(std::list<ABMWithState> &abms,
656 float dtime_s, ServerEnvironment *env,
662 INodeDefManager *ndef = env->getGameDef()->ndef();
663 for(std::list<ABMWithState>::iterator
664 i = abms.begin(); i != abms.end(); ++i){
665 ActiveBlockModifier *abm = i->abm;
666 float trigger_interval = abm->getTriggerInterval();
667 if(trigger_interval < 0.001)
668 trigger_interval = 0.001;
669 float actual_interval = dtime_s;
672 if(i->timer < trigger_interval)
674 i->timer -= trigger_interval;
675 actual_interval = trigger_interval;
677 float intervals = actual_interval / trigger_interval;
680 float chance = abm->getTriggerChance();
685 aabm.chance = chance / intervals;
689 std::set<std::string> required_neighbors_s
690 = abm->getRequiredNeighbors();
691 for(std::set<std::string>::iterator
692 i = required_neighbors_s.begin();
693 i != required_neighbors_s.end(); i++)
695 ndef->getIds(*i, aabm.required_neighbors);
698 std::set<std::string> contents_s = abm->getTriggerContents();
699 for(std::set<std::string>::iterator
700 i = contents_s.begin(); i != contents_s.end(); i++)
702 std::set<content_t> ids;
703 ndef->getIds(*i, ids);
704 for(std::set<content_t>::const_iterator k = ids.begin();
708 std::map<content_t, std::list<ActiveABM> >::iterator j;
710 if(j == m_aabms.end()){
711 std::list<ActiveABM> aabmlist;
712 m_aabms[c] = aabmlist;
715 j->second.push_back(aabm);
720 void apply(MapBlock *block)
725 ServerMap *map = &m_env->getServerMap();
728 for(p0.X=0; p0.X<MAP_BLOCKSIZE; p0.X++)
729 for(p0.Y=0; p0.Y<MAP_BLOCKSIZE; p0.Y++)
730 for(p0.Z=0; p0.Z<MAP_BLOCKSIZE; p0.Z++)
732 MapNode n = block->getNodeNoEx(p0);
733 content_t c = n.getContent();
734 v3s16 p = p0 + block->getPosRelative();
736 std::map<content_t, std::list<ActiveABM> >::iterator j;
738 if(j == m_aabms.end())
741 for(std::list<ActiveABM>::iterator
742 i = j->second.begin(); i != j->second.end(); i++)
744 if(myrand() % i->chance != 0)
748 if(!i->required_neighbors.empty())
751 for(p1.X = p.X-1; p1.X <= p.X+1; p1.X++)
752 for(p1.Y = p.Y-1; p1.Y <= p.Y+1; p1.Y++)
753 for(p1.Z = p.Z-1; p1.Z <= p.Z+1; p1.Z++)
757 MapNode n = map->getNodeNoEx(p1);
758 content_t c = n.getContent();
759 std::set<content_t>::const_iterator k;
760 k = i->required_neighbors.find(c);
761 if(k != i->required_neighbors.end()){
765 // No required neighbor found
770 // Find out how many objects the block contains
771 u32 active_object_count = block->m_static_objects.m_active.size();
772 // Find out how many objects this and all the neighbors contain
773 u32 active_object_count_wider = 0;
774 u32 wider_unknown_count = 0;
775 for(s16 x=-1; x<=1; x++)
776 for(s16 y=-1; y<=1; y++)
777 for(s16 z=-1; z<=1; z++)
779 MapBlock *block2 = map->getBlockNoCreateNoEx(
780 block->getPos() + v3s16(x,y,z));
782 wider_unknown_count = 0;
785 active_object_count_wider +=
786 block2->m_static_objects.m_active.size()
787 + block2->m_static_objects.m_stored.size();
790 u32 wider_known_count = 3*3*3 - wider_unknown_count;
791 active_object_count_wider += wider_unknown_count * active_object_count_wider / wider_known_count;
793 // Call all the trigger variations
794 i->abm->trigger(m_env, p, n);
795 i->abm->trigger(m_env, p, n,
796 active_object_count, active_object_count_wider);
802 void ServerEnvironment::activateBlock(MapBlock *block, u32 additional_dtime)
804 // Get time difference
806 u32 stamp = block->getTimestamp();
807 if(m_game_time > stamp && stamp != BLOCK_TIMESTAMP_UNDEFINED)
808 dtime_s = m_game_time - block->getTimestamp();
809 dtime_s += additional_dtime;
811 /*infostream<<"ServerEnvironment::activateBlock(): block timestamp: "
812 <<stamp<<", game time: "<<m_game_time<<std::endl;*/
814 // Set current time as timestamp
815 block->setTimestampNoChangedFlag(m_game_time);
817 /*infostream<<"ServerEnvironment::activateBlock(): block is "
818 <<dtime_s<<" seconds old."<<std::endl;*/
820 // Activate stored objects
821 activateObjects(block, dtime_s);
824 std::map<v3s16, NodeTimer> elapsed_timers =
825 block->m_node_timers.step((float)dtime_s);
826 if(!elapsed_timers.empty()){
828 for(std::map<v3s16, NodeTimer>::iterator
829 i = elapsed_timers.begin();
830 i != elapsed_timers.end(); i++){
831 n = block->getNodeNoEx(i->first);
832 v3s16 p = i->first + block->getPosRelative();
833 if(m_script->node_on_timer(p,n,i->second.elapsed))
834 block->setNodeTimer(i->first,NodeTimer(i->second.timeout,0));
838 /* Handle ActiveBlockModifiers */
839 ABMHandler abmhandler(m_abms, dtime_s, this, false);
840 abmhandler.apply(block);
843 void ServerEnvironment::addActiveBlockModifier(ActiveBlockModifier *abm)
845 m_abms.push_back(ABMWithState(abm));
848 bool ServerEnvironment::setNode(v3s16 p, const MapNode &n)
850 INodeDefManager *ndef = m_gamedef->ndef();
851 MapNode n_old = m_map->getNodeNoEx(p);
853 if(ndef->get(n_old).has_on_destruct)
854 m_script->node_on_destruct(p, n_old);
856 bool succeeded = m_map->addNodeWithEvent(p, n);
859 // Call post-destructor
860 if(ndef->get(n_old).has_after_destruct)
861 m_script->node_after_destruct(p, n_old);
863 if(ndef->get(n).has_on_construct)
864 m_script->node_on_construct(p, n);
868 bool ServerEnvironment::removeNode(v3s16 p)
870 INodeDefManager *ndef = m_gamedef->ndef();
871 MapNode n_old = m_map->getNodeNoEx(p);
873 if(ndef->get(n_old).has_on_destruct)
874 m_script->node_on_destruct(p, n_old);
876 // This is slightly optimized compared to addNodeWithEvent(air)
877 bool succeeded = m_map->removeNodeWithEvent(p);
880 // Call post-destructor
881 if(ndef->get(n_old).has_after_destruct)
882 m_script->node_after_destruct(p, n_old);
883 // Air doesn't require constructor
887 std::set<u16> ServerEnvironment::getObjectsInsideRadius(v3f pos, float radius)
889 std::set<u16> objects;
890 for(std::map<u16, ServerActiveObject*>::iterator
891 i = m_active_objects.begin();
892 i != m_active_objects.end(); ++i)
894 ServerActiveObject* obj = i->second;
896 v3f objectpos = obj->getBasePosition();
897 if(objectpos.getDistanceFrom(pos) > radius)
904 void ServerEnvironment::clearAllObjects()
906 infostream<<"ServerEnvironment::clearAllObjects(): "
907 <<"Removing all active objects"<<std::endl;
908 std::list<u16> objects_to_remove;
909 for(std::map<u16, ServerActiveObject*>::iterator
910 i = m_active_objects.begin();
911 i != m_active_objects.end(); ++i)
913 ServerActiveObject* obj = i->second;
914 if(obj->getType() == ACTIVEOBJECT_TYPE_PLAYER)
917 // Delete static object if block is loaded
918 if(obj->m_static_exists){
919 MapBlock *block = m_map->getBlockNoCreateNoEx(obj->m_static_block);
921 block->m_static_objects.remove(id);
922 block->raiseModified(MOD_STATE_WRITE_NEEDED,
924 obj->m_static_exists = false;
927 // If known by some client, don't delete immediately
928 if(obj->m_known_by_count > 0){
929 obj->m_pending_deactivation = true;
930 obj->m_removed = true;
934 // Tell the object about removal
935 obj->removingFromEnvironment();
936 // Deregister in scripting api
937 m_script->removeObjectReference(obj);
939 // Delete active object
940 if(obj->environmentDeletes())
942 // Id to be removed from m_active_objects
943 objects_to_remove.push_back(id);
945 // Remove references from m_active_objects
946 for(std::list<u16>::iterator i = objects_to_remove.begin();
947 i != objects_to_remove.end(); ++i)
949 m_active_objects.erase(*i);
952 // Get list of loaded blocks
953 std::list<v3s16> loaded_blocks;
954 infostream<<"ServerEnvironment::clearAllObjects(): "
955 <<"Listing all loaded blocks"<<std::endl;
956 m_map->listAllLoadedBlocks(loaded_blocks);
957 infostream<<"ServerEnvironment::clearAllObjects(): "
958 <<"Done listing all loaded blocks: "
959 <<loaded_blocks.size()<<std::endl;
961 // Get list of loadable blocks
962 std::list<v3s16> loadable_blocks;
963 infostream<<"ServerEnvironment::clearAllObjects(): "
964 <<"Listing all loadable blocks"<<std::endl;
965 m_map->listAllLoadableBlocks(loadable_blocks);
966 infostream<<"ServerEnvironment::clearAllObjects(): "
967 <<"Done listing all loadable blocks: "
968 <<loadable_blocks.size()
969 <<", now clearing"<<std::endl;
971 // Grab a reference on each loaded block to avoid unloading it
972 for(std::list<v3s16>::iterator i = loaded_blocks.begin();
973 i != loaded_blocks.end(); ++i)
976 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
981 // Remove objects in all loadable blocks
982 u32 unload_interval = g_settings->getS32("max_clearobjects_extra_loaded_blocks");
983 unload_interval = MYMAX(unload_interval, 1);
984 u32 report_interval = loadable_blocks.size() / 10;
985 u32 num_blocks_checked = 0;
986 u32 num_blocks_cleared = 0;
987 u32 num_objs_cleared = 0;
988 for(std::list<v3s16>::iterator i = loadable_blocks.begin();
989 i != loadable_blocks.end(); ++i)
992 MapBlock *block = m_map->emergeBlock(p, false);
994 errorstream<<"ServerEnvironment::clearAllObjects(): "
995 <<"Failed to emerge block "<<PP(p)<<std::endl;
998 u32 num_stored = block->m_static_objects.m_stored.size();
999 u32 num_active = block->m_static_objects.m_active.size();
1000 if(num_stored != 0 || num_active != 0){
1001 block->m_static_objects.m_stored.clear();
1002 block->m_static_objects.m_active.clear();
1003 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1005 num_objs_cleared += num_stored + num_active;
1006 num_blocks_cleared++;
1008 num_blocks_checked++;
1010 if(num_blocks_checked % report_interval == 0){
1011 float percent = 100.0 * (float)num_blocks_checked /
1012 loadable_blocks.size();
1013 infostream<<"ServerEnvironment::clearAllObjects(): "
1014 <<"Cleared "<<num_objs_cleared<<" objects"
1015 <<" in "<<num_blocks_cleared<<" blocks ("
1016 <<percent<<"%)"<<std::endl;
1018 if(num_blocks_checked % unload_interval == 0){
1019 m_map->unloadUnreferencedBlocks();
1022 m_map->unloadUnreferencedBlocks();
1024 // Drop references that were added above
1025 for(std::list<v3s16>::iterator i = loaded_blocks.begin();
1026 i != loaded_blocks.end(); ++i)
1029 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1034 infostream<<"ServerEnvironment::clearAllObjects(): "
1035 <<"Finished: Cleared "<<num_objs_cleared<<" objects"
1036 <<" in "<<num_blocks_cleared<<" blocks"<<std::endl;
1039 void ServerEnvironment::step(float dtime)
1041 DSTACK(__FUNCTION_NAME);
1043 //TimeTaker timer("ServerEnv step");
1045 /* Step time of day */
1046 stepTimeOfDay(dtime);
1049 // NOTE: This is kind of funny on a singleplayer game, but doesn't
1050 // really matter that much.
1051 m_recommended_send_interval = g_settings->getFloat("dedicated_server_step");
1057 m_game_time_fraction_counter += dtime;
1058 u32 inc_i = (u32)m_game_time_fraction_counter;
1059 m_game_time += inc_i;
1060 m_game_time_fraction_counter -= (float)inc_i;
1067 ScopeProfiler sp(g_profiler, "SEnv: handle players avg", SPT_AVG);
1068 for(std::list<Player*>::iterator i = m_players.begin();
1069 i != m_players.end(); ++i)
1071 Player *player = *i;
1073 // Ignore disconnected players
1074 if(player->peer_id == 0)
1078 player->move(dtime, *m_map, 100*BS);
1083 Manage active block list
1085 if(m_active_blocks_management_interval.step(dtime, 2.0))
1087 ScopeProfiler sp(g_profiler, "SEnv: manage act. block list avg /2s", SPT_AVG);
1089 Get player block positions
1091 std::list<v3s16> players_blockpos;
1092 for(std::list<Player*>::iterator
1093 i = m_players.begin();
1094 i != m_players.end(); ++i)
1096 Player *player = *i;
1097 // Ignore disconnected players
1098 if(player->peer_id == 0)
1100 v3s16 blockpos = getNodeBlockPos(
1101 floatToInt(player->getPosition(), BS));
1102 players_blockpos.push_back(blockpos);
1106 Update list of active blocks, collecting changes
1108 const s16 active_block_range = g_settings->getS16("active_block_range");
1109 std::set<v3s16> blocks_removed;
1110 std::set<v3s16> blocks_added;
1111 m_active_blocks.update(players_blockpos, active_block_range,
1112 blocks_removed, blocks_added);
1115 Handle removed blocks
1118 // Convert active objects that are no more in active blocks to static
1119 deactivateFarObjects(false);
1121 for(std::set<v3s16>::iterator
1122 i = blocks_removed.begin();
1123 i != blocks_removed.end(); ++i)
1127 /* infostream<<"Server: Block " << PP(p)
1128 << " became inactive"<<std::endl; */
1130 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1134 // Set current time as timestamp (and let it set ChangedFlag)
1135 block->setTimestamp(m_game_time);
1142 for(std::set<v3s16>::iterator
1143 i = blocks_added.begin();
1144 i != blocks_added.end(); ++i)
1148 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1150 // Block needs to be fetched first
1151 m_emerger->queueBlockEmerge(p, false);
1152 m_active_blocks.m_list.erase(p);
1156 activateBlock(block);
1157 /* infostream<<"Server: Block " << PP(p)
1158 << " became active"<<std::endl; */
1163 Mess around in active blocks
1165 if(m_active_blocks_nodemetadata_interval.step(dtime, 1.0))
1167 ScopeProfiler sp(g_profiler, "SEnv: mess in act. blocks avg /1s", SPT_AVG);
1171 for(std::set<v3s16>::iterator
1172 i = m_active_blocks.m_list.begin();
1173 i != m_active_blocks.m_list.end(); ++i)
1177 /*infostream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
1178 <<") being handled"<<std::endl;*/
1180 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1184 // Reset block usage timer
1185 block->resetUsageTimer();
1187 // Set current time as timestamp
1188 block->setTimestampNoChangedFlag(m_game_time);
1189 // If time has changed much from the one on disk,
1190 // set block to be saved when it is unloaded
1191 if(block->getTimestamp() > block->getDiskTimestamp() + 60)
1192 block->raiseModified(MOD_STATE_WRITE_AT_UNLOAD,
1193 "Timestamp older than 60s (step)");
1196 std::map<v3s16, NodeTimer> elapsed_timers =
1197 block->m_node_timers.step((float)dtime);
1198 if(!elapsed_timers.empty()){
1200 for(std::map<v3s16, NodeTimer>::iterator
1201 i = elapsed_timers.begin();
1202 i != elapsed_timers.end(); i++){
1203 n = block->getNodeNoEx(i->first);
1204 p = i->first + block->getPosRelative();
1205 if(m_script->node_on_timer(p,n,i->second.elapsed))
1206 block->setNodeTimer(i->first,NodeTimer(i->second.timeout,0));
1212 const float abm_interval = 1.0;
1213 if(m_active_block_modifier_interval.step(dtime, abm_interval))
1215 if(m_active_block_interval_overload_skip > 0){
1216 ScopeProfiler sp(g_profiler, "SEnv: ABM overload skips");
1217 m_active_block_interval_overload_skip--;
1220 ScopeProfiler sp(g_profiler, "SEnv: modify in blocks avg /1s", SPT_AVG);
1221 TimeTaker timer("modify in active blocks");
1223 // Initialize handling of ActiveBlockModifiers
1224 ABMHandler abmhandler(m_abms, abm_interval, this, true);
1226 for(std::set<v3s16>::iterator
1227 i = m_active_blocks.m_list.begin();
1228 i != m_active_blocks.m_list.end(); ++i)
1232 /*infostream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
1233 <<") being handled"<<std::endl;*/
1235 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1239 // Set current time as timestamp
1240 block->setTimestampNoChangedFlag(m_game_time);
1242 /* Handle ActiveBlockModifiers */
1243 abmhandler.apply(block);
1246 u32 time_ms = timer.stop(true);
1247 u32 max_time_ms = 200;
1248 if(time_ms > max_time_ms){
1249 infostream<<"WARNING: active block modifiers took "
1250 <<time_ms<<"ms (longer than "
1251 <<max_time_ms<<"ms)"<<std::endl;
1252 m_active_block_interval_overload_skip = (time_ms / max_time_ms) + 1;
1257 Step script environment (run global on_step())
1259 m_script->environment_Step(dtime);
1265 ScopeProfiler sp(g_profiler, "SEnv: step act. objs avg", SPT_AVG);
1266 //TimeTaker timer("Step active objects");
1268 g_profiler->avg("SEnv: num of objects", m_active_objects.size());
1270 // This helps the objects to send data at the same time
1271 bool send_recommended = false;
1272 m_send_recommended_timer += dtime;
1273 if(m_send_recommended_timer > getSendRecommendedInterval())
1275 m_send_recommended_timer -= getSendRecommendedInterval();
1276 send_recommended = true;
1279 for(std::map<u16, ServerActiveObject*>::iterator
1280 i = m_active_objects.begin();
1281 i != m_active_objects.end(); ++i)
1283 ServerActiveObject* obj = i->second;
1284 // Remove non-peaceful mobs on peaceful mode
1285 if(g_settings->getBool("only_peaceful_mobs")){
1286 if(!obj->isPeaceful())
1287 obj->m_removed = true;
1289 // Don't step if is to be removed or stored statically
1290 if(obj->m_removed || obj->m_pending_deactivation)
1293 obj->step(dtime, send_recommended);
1294 // Read messages from object
1295 while(!obj->m_messages_out.empty())
1297 m_active_object_messages.push_back(
1298 obj->m_messages_out.pop_front());
1304 Manage active objects
1306 if(m_object_management_interval.step(dtime, 0.5))
1308 ScopeProfiler sp(g_profiler, "SEnv: remove removed objs avg /.5s", SPT_AVG);
1310 Remove objects that satisfy (m_removed && m_known_by_count==0)
1312 removeRemovedObjects();
1316 ServerActiveObject* ServerEnvironment::getActiveObject(u16 id)
1318 std::map<u16, ServerActiveObject*>::iterator n;
1319 n = m_active_objects.find(id);
1320 if(n == m_active_objects.end())
1325 bool isFreeServerActiveObjectId(u16 id,
1326 std::map<u16, ServerActiveObject*> &objects)
1331 return objects.find(id) == objects.end();
1334 u16 getFreeServerActiveObjectId(
1335 std::map<u16, ServerActiveObject*> &objects)
1337 //try to reuse id's as late as possible
1338 static u16 last_used_id = 0;
1339 u16 startid = last_used_id;
1343 if(isFreeServerActiveObjectId(last_used_id, objects))
1344 return last_used_id;
1346 if(last_used_id == startid)
1351 u16 ServerEnvironment::addActiveObject(ServerActiveObject *object)
1354 u16 id = addActiveObjectRaw(object, true, 0);
1359 bool ServerEnvironment::addActiveObjectAsStatic(ServerActiveObject *obj)
1363 v3f objectpos = obj->getBasePosition();
1365 // The block in which the object resides in
1366 v3s16 blockpos_o = getNodeBlockPos(floatToInt(objectpos, BS));
1369 Update the static data
1372 // Create new static object
1373 std::string staticdata = obj->getStaticData();
1374 StaticObject s_obj(obj->getType(), objectpos, staticdata);
1375 // Add to the block where the object is located in
1376 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1377 // Get or generate the block
1378 MapBlock *block = m_map->emergeBlock(blockpos);
1380 bool succeeded = false;
1384 block->m_static_objects.insert(0, s_obj);
1385 block->raiseModified(MOD_STATE_WRITE_AT_UNLOAD,
1386 "addActiveObjectAsStatic");
1390 infostream<<"ServerEnvironment::addActiveObjectAsStatic: "
1391 <<"Could not find or generate "
1392 <<"a block for storing static object"<<std::endl;
1396 if(obj->environmentDeletes())
1404 Finds out what new objects have been added to
1405 inside a radius around a position
1407 void ServerEnvironment::getAddedActiveObjects(v3s16 pos, s16 radius,
1408 std::set<u16> ¤t_objects,
1409 std::set<u16> &added_objects)
1411 v3f pos_f = intToFloat(pos, BS);
1412 f32 radius_f = radius * BS;
1414 Go through the object list,
1415 - discard m_removed objects,
1416 - discard objects that are too far away,
1417 - discard objects that are found in current_objects.
1418 - add remaining objects to added_objects
1420 for(std::map<u16, ServerActiveObject*>::iterator
1421 i = m_active_objects.begin();
1422 i != m_active_objects.end(); ++i)
1426 ServerActiveObject *object = i->second;
1429 // Discard if removed
1430 if(object->m_removed)
1432 if(object->unlimitedTransferDistance() == false){
1433 // Discard if too far
1434 f32 distance_f = object->getBasePosition().getDistanceFrom(pos_f);
1435 if(distance_f > radius_f)
1438 // Discard if already on current_objects
1439 std::set<u16>::iterator n;
1440 n = current_objects.find(id);
1441 if(n != current_objects.end())
1443 // Add to added_objects
1444 added_objects.insert(id);
1449 Finds out what objects have been removed from
1450 inside a radius around a position
1452 void ServerEnvironment::getRemovedActiveObjects(v3s16 pos, s16 radius,
1453 std::set<u16> ¤t_objects,
1454 std::set<u16> &removed_objects)
1456 v3f pos_f = intToFloat(pos, BS);
1457 f32 radius_f = radius * BS;
1459 Go through current_objects; object is removed if:
1460 - object is not found in m_active_objects (this is actually an
1461 error condition; objects should be set m_removed=true and removed
1462 only after all clients have been informed about removal), or
1463 - object has m_removed=true, or
1464 - object is too far away
1466 for(std::set<u16>::iterator
1467 i = current_objects.begin();
1468 i != current_objects.end(); ++i)
1471 ServerActiveObject *object = getActiveObject(id);
1474 infostream<<"ServerEnvironment::getRemovedActiveObjects():"
1475 <<" object in current_objects is NULL"<<std::endl;
1476 removed_objects.insert(id);
1480 if(object->m_removed)
1482 removed_objects.insert(id);
1486 // If transfer distance is unlimited, don't remove
1487 if(object->unlimitedTransferDistance())
1490 f32 distance_f = object->getBasePosition().getDistanceFrom(pos_f);
1492 if(distance_f >= radius_f)
1494 removed_objects.insert(id);
1502 ActiveObjectMessage ServerEnvironment::getActiveObjectMessage()
1504 if(m_active_object_messages.empty())
1505 return ActiveObjectMessage(0);
1507 return m_active_object_messages.pop_front();
1511 ************ Private methods *************
1514 u16 ServerEnvironment::addActiveObjectRaw(ServerActiveObject *object,
1515 bool set_changed, u32 dtime_s)
1518 if(object->getId() == 0){
1519 u16 new_id = getFreeServerActiveObjectId(m_active_objects);
1522 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1523 <<"no free ids available"<<std::endl;
1524 if(object->environmentDeletes())
1528 object->setId(new_id);
1531 verbosestream<<"ServerEnvironment::addActiveObjectRaw(): "
1532 <<"supplied with id "<<object->getId()<<std::endl;
1534 if(isFreeServerActiveObjectId(object->getId(), m_active_objects) == false)
1536 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1537 <<"id is not free ("<<object->getId()<<")"<<std::endl;
1538 if(object->environmentDeletes())
1542 /*infostream<<"ServerEnvironment::addActiveObjectRaw(): "
1543 <<"added (id="<<object->getId()<<")"<<std::endl;*/
1545 m_active_objects[object->getId()] = object;
1547 verbosestream<<"ServerEnvironment::addActiveObjectRaw(): "
1548 <<"Added id="<<object->getId()<<"; there are now "
1549 <<m_active_objects.size()<<" active objects."
1552 // Register reference in scripting api (must be done before post-init)
1553 m_script->addObjectReference(object);
1554 // Post-initialize object
1555 object->addedToEnvironment(dtime_s);
1557 // Add static data to block
1558 if(object->isStaticAllowed())
1560 // Add static object to active static list of the block
1561 v3f objectpos = object->getBasePosition();
1562 std::string staticdata = object->getStaticData();
1563 StaticObject s_obj(object->getType(), objectpos, staticdata);
1564 // Add to the block where the object is located in
1565 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1566 MapBlock *block = m_map->getBlockNoCreateNoEx(blockpos);
1569 block->m_static_objects.m_active[object->getId()] = s_obj;
1570 object->m_static_exists = true;
1571 object->m_static_block = blockpos;
1574 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1575 "addActiveObjectRaw");
1578 v3s16 p = floatToInt(objectpos, BS);
1579 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1580 <<"could not find block for storing id="<<object->getId()
1581 <<" statically (pos="<<PP(p)<<")"<<std::endl;
1585 return object->getId();
1589 Remove objects that satisfy (m_removed && m_known_by_count==0)
1591 void ServerEnvironment::removeRemovedObjects()
1593 std::list<u16> objects_to_remove;
1594 for(std::map<u16, ServerActiveObject*>::iterator
1595 i = m_active_objects.begin();
1596 i != m_active_objects.end(); ++i)
1599 ServerActiveObject* obj = i->second;
1600 // This shouldn't happen but check it
1603 infostream<<"NULL object found in ServerEnvironment"
1604 <<" while finding removed objects. id="<<id<<std::endl;
1605 // Id to be removed from m_active_objects
1606 objects_to_remove.push_back(id);
1611 We will delete objects that are marked as removed or thatare
1612 waiting for deletion after deactivation
1614 if(obj->m_removed == false && obj->m_pending_deactivation == false)
1618 Delete static data from block if is marked as removed
1620 if(obj->m_static_exists && obj->m_removed)
1622 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
1624 block->m_static_objects.remove(id);
1625 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1626 "removeRemovedObjects");
1627 obj->m_static_exists = false;
1629 infostream << "failed to emerge block from which "
1630 "an object to be removed was loaded from. id="<<id<<std::endl;
1634 // If m_known_by_count > 0, don't actually remove.
1635 if(obj->m_known_by_count > 0)
1638 // Tell the object about removal
1639 obj->removingFromEnvironment();
1640 // Deregister in scripting api
1641 m_script->removeObjectReference(obj);
1644 if(obj->environmentDeletes())
1646 // Id to be removed from m_active_objects
1647 objects_to_remove.push_back(id);
1649 // Remove references from m_active_objects
1650 for(std::list<u16>::iterator i = objects_to_remove.begin();
1651 i != objects_to_remove.end(); ++i)
1653 m_active_objects.erase(*i);
1657 static void print_hexdump(std::ostream &o, const std::string &data)
1659 const int linelength = 16;
1660 for(int l=0; ; l++){
1661 int i0 = linelength * l;
1662 bool at_end = false;
1663 int thislinelength = linelength;
1664 if(i0 + thislinelength > (int)data.size()){
1665 thislinelength = data.size() - i0;
1668 for(int di=0; di<linelength; di++){
1671 if(di<thislinelength)
1672 snprintf(buf, 4, "%.2x ", data[i]);
1674 snprintf(buf, 4, " ");
1678 for(int di=0; di<thislinelength; di++){
1692 Convert stored objects from blocks near the players to active.
1694 void ServerEnvironment::activateObjects(MapBlock *block, u32 dtime_s)
1698 // Ignore if no stored objects (to not set changed flag)
1699 if(block->m_static_objects.m_stored.size() == 0)
1701 verbosestream<<"ServerEnvironment::activateObjects(): "
1702 <<"activating objects of block "<<PP(block->getPos())
1703 <<" ("<<block->m_static_objects.m_stored.size()
1704 <<" objects)"<<std::endl;
1705 bool large_amount = (block->m_static_objects.m_stored.size() > g_settings->getU16("max_objects_per_block"));
1707 errorstream<<"suspiciously large amount of objects detected: "
1708 <<block->m_static_objects.m_stored.size()<<" in "
1709 <<PP(block->getPos())
1710 <<"; removing all of them."<<std::endl;
1711 // Clear stored list
1712 block->m_static_objects.m_stored.clear();
1713 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1714 "stored list cleared in activateObjects due to "
1715 "large amount of objects");
1718 // A list for objects that couldn't be converted to active for some
1719 // reason. They will be stored back.
1720 std::list<StaticObject> new_stored;
1721 // Loop through stored static objects
1722 for(std::list<StaticObject>::iterator
1723 i = block->m_static_objects.m_stored.begin();
1724 i != block->m_static_objects.m_stored.end(); ++i)
1726 /*infostream<<"Server: Creating an active object from "
1727 <<"static data"<<std::endl;*/
1728 StaticObject &s_obj = *i;
1729 // Create an active object from the data
1730 ServerActiveObject *obj = ServerActiveObject::create
1731 (s_obj.type, this, 0, s_obj.pos, s_obj.data);
1732 // If couldn't create object, store static data back.
1735 errorstream<<"ServerEnvironment::activateObjects(): "
1736 <<"failed to create active object from static object "
1737 <<"in block "<<PP(s_obj.pos/BS)
1738 <<" type="<<(int)s_obj.type<<" data:"<<std::endl;
1739 print_hexdump(verbosestream, s_obj.data);
1741 new_stored.push_back(s_obj);
1744 verbosestream<<"ServerEnvironment::activateObjects(): "
1745 <<"activated static object pos="<<PP(s_obj.pos/BS)
1746 <<" type="<<(int)s_obj.type<<std::endl;
1747 // This will also add the object to the active static list
1748 addActiveObjectRaw(obj, false, dtime_s);
1750 // Clear stored list
1751 block->m_static_objects.m_stored.clear();
1752 // Add leftover failed stuff to stored list
1753 for(std::list<StaticObject>::iterator
1754 i = new_stored.begin();
1755 i != new_stored.end(); ++i)
1757 StaticObject &s_obj = *i;
1758 block->m_static_objects.m_stored.push_back(s_obj);
1761 Note: Block hasn't really been modified here.
1762 The objects have just been activated and moved from the stored
1763 static list to the active static list.
1764 As such, the block is essentially the same.
1765 Thus, do not call block->raiseModified(MOD_STATE_WRITE_NEEDED).
1766 Otherwise there would be a huge amount of unnecessary I/O.
1771 Convert objects that are not standing inside active blocks to static.
1773 If m_known_by_count != 0, active object is not deleted, but static
1774 data is still updated.
1776 If force_delete is set, active object is deleted nevertheless. It
1777 shall only be set so in the destructor of the environment.
1779 If block wasn't generated (not in memory or on disk),
1781 void ServerEnvironment::deactivateFarObjects(bool force_delete)
1783 std::list<u16> objects_to_remove;
1784 for(std::map<u16, ServerActiveObject*>::iterator
1785 i = m_active_objects.begin();
1786 i != m_active_objects.end(); ++i)
1788 ServerActiveObject* obj = i->second;
1791 // Do not deactivate if static data creation not allowed
1792 if(!force_delete && !obj->isStaticAllowed())
1795 // If pending deactivation, let removeRemovedObjects() do it
1796 if(!force_delete && obj->m_pending_deactivation)
1800 v3f objectpos = obj->getBasePosition();
1802 // The block in which the object resides in
1803 v3s16 blockpos_o = getNodeBlockPos(floatToInt(objectpos, BS));
1805 // If block is active, don't remove
1806 if(!force_delete && m_active_blocks.contains(blockpos_o))
1809 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
1810 <<"deactivating object id="<<id<<" on inactive block "
1811 <<PP(blockpos_o)<<std::endl;
1813 // If known by some client, don't immediately delete.
1814 bool pending_delete = (obj->m_known_by_count > 0 && !force_delete);
1817 Update the static data
1820 if(obj->isStaticAllowed())
1822 // Create new static object
1823 std::string staticdata_new = obj->getStaticData();
1824 StaticObject s_obj(obj->getType(), objectpos, staticdata_new);
1826 bool stays_in_same_block = false;
1827 bool data_changed = true;
1829 if(obj->m_static_exists){
1830 if(obj->m_static_block == blockpos_o)
1831 stays_in_same_block = true;
1833 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
1835 std::map<u16, StaticObject>::iterator n =
1836 block->m_static_objects.m_active.find(id);
1837 if(n != block->m_static_objects.m_active.end()){
1838 StaticObject static_old = n->second;
1840 float save_movem = obj->getMinimumSavedMovement();
1842 if(static_old.data == staticdata_new &&
1843 (static_old.pos - objectpos).getLength() < save_movem)
1844 data_changed = false;
1846 errorstream<<"ServerEnvironment::deactivateFarObjects(): "
1847 <<"id="<<id<<" m_static_exists=true but "
1848 <<"static data doesn't actually exist in "
1849 <<PP(obj->m_static_block)<<std::endl;
1853 bool shall_be_written = (!stays_in_same_block || data_changed);
1855 // Delete old static object
1856 if(obj->m_static_exists)
1858 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
1861 block->m_static_objects.remove(id);
1862 obj->m_static_exists = false;
1863 // Only mark block as modified if data changed considerably
1864 if(shall_be_written)
1865 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1866 "deactivateFarObjects: Static data "
1867 "changed considerably");
1871 // Add to the block where the object is located in
1872 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1873 // Get or generate the block
1874 MapBlock *block = NULL;
1876 block = m_map->emergeBlock(blockpos);
1877 } catch(InvalidPositionException &e){
1878 // Handled via NULL pointer
1883 if(block->m_static_objects.m_stored.size() >= g_settings->getU16("max_objects_per_block")){
1884 errorstream<<"ServerEnv: Trying to store id="<<obj->getId()
1885 <<" statically but block "<<PP(blockpos)
1886 <<" already contains "
1887 <<block->m_static_objects.m_stored.size()
1889 <<" Forcing delete."<<std::endl;
1890 force_delete = true;
1892 // If static counterpart already exists, remove it first.
1893 // This shouldn't happen, but happens rarely for some
1894 // unknown reason. Unsuccessful attempts have been made to
1895 // find said reason.
1896 if(id && block->m_static_objects.m_active.find(id) != block->m_static_objects.m_active.end()){
1897 infostream<<"ServerEnv: WARNING: Performing hack #83274"
1899 block->m_static_objects.remove(id);
1902 block->m_static_objects.insert(0, s_obj);
1904 // Only mark block as modified if data changed considerably
1905 if(shall_be_written)
1906 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1907 "deactivateFarObjects: Static data "
1908 "changed considerably");
1910 obj->m_static_exists = true;
1911 obj->m_static_block = block->getPos();
1916 v3s16 p = floatToInt(objectpos, BS);
1917 errorstream<<"ServerEnv: Could not find or generate "
1918 <<"a block for storing id="<<obj->getId()
1919 <<" statically (pos="<<PP(p)<<")"<<std::endl;
1926 If known by some client, set pending deactivation.
1927 Otherwise delete it immediately.
1930 if(pending_delete && !force_delete)
1932 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
1933 <<"object id="<<id<<" is known by clients"
1934 <<"; not deleting yet"<<std::endl;
1936 obj->m_pending_deactivation = true;
1940 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
1941 <<"object id="<<id<<" is not known by clients"
1942 <<"; deleting"<<std::endl;
1944 // Tell the object about removal
1945 obj->removingFromEnvironment();
1946 // Deregister in scripting api
1947 m_script->removeObjectReference(obj);
1949 // Delete active object
1950 if(obj->environmentDeletes())
1952 // Id to be removed from m_active_objects
1953 objects_to_remove.push_back(id);
1956 // Remove references from m_active_objects
1957 for(std::list<u16>::iterator i = objects_to_remove.begin();
1958 i != objects_to_remove.end(); ++i)
1960 m_active_objects.erase(*i);
1967 #include "clientsimpleobject.h"
1973 ClientEnvironment::ClientEnvironment(ClientMap *map, scene::ISceneManager *smgr,
1974 ITextureSource *texturesource, IGameDef *gamedef,
1975 IrrlichtDevice *irr):
1978 m_texturesource(texturesource),
1984 ClientEnvironment::~ClientEnvironment()
1986 // delete active objects
1987 for(std::map<u16, ClientActiveObject*>::iterator
1988 i = m_active_objects.begin();
1989 i != m_active_objects.end(); ++i)
1994 for(std::list<ClientSimpleObject*>::iterator
1995 i = m_simple_objects.begin(); i != m_simple_objects.end(); ++i)
2004 Map & ClientEnvironment::getMap()
2009 ClientMap & ClientEnvironment::getClientMap()
2014 void ClientEnvironment::addPlayer(Player *player)
2016 DSTACK(__FUNCTION_NAME);
2018 It is a failure if player is local and there already is a local
2021 assert(!(player->isLocal() == true && getLocalPlayer() != NULL));
2023 Environment::addPlayer(player);
2026 LocalPlayer * ClientEnvironment::getLocalPlayer()
2028 for(std::list<Player*>::iterator i = m_players.begin();
2029 i != m_players.end(); ++i)
2031 Player *player = *i;
2032 if(player->isLocal())
2033 return (LocalPlayer*)player;
2038 void ClientEnvironment::step(float dtime)
2040 DSTACK(__FUNCTION_NAME);
2042 /* Step time of day */
2043 stepTimeOfDay(dtime);
2045 // Get some settings
2046 bool fly_allowed = m_gamedef->checkLocalPrivilege("fly");
2047 bool free_move = fly_allowed && g_settings->getBool("free_move");
2050 LocalPlayer *lplayer = getLocalPlayer();
2052 // collision info queue
2053 std::list<CollisionInfo> player_collisions;
2056 Get the speed the player is going
2058 bool is_climbing = lplayer->is_climbing;
2060 f32 player_speed = lplayer->getSpeed().getLength();
2063 Maximum position increment
2065 //f32 position_max_increment = 0.05*BS;
2066 f32 position_max_increment = 0.1*BS;
2068 // Maximum time increment (for collision detection etc)
2069 // time = distance / speed
2070 f32 dtime_max_increment = 1;
2071 if(player_speed > 0.001)
2072 dtime_max_increment = position_max_increment / player_speed;
2074 // Maximum time increment is 10ms or lower
2075 if(dtime_max_increment > 0.01)
2076 dtime_max_increment = 0.01;
2078 // Don't allow overly huge dtime
2082 f32 dtime_downcount = dtime;
2085 Stuff that has a maximum time increment
2094 if(dtime_downcount > dtime_max_increment)
2096 dtime_part = dtime_max_increment;
2097 dtime_downcount -= dtime_part;
2101 dtime_part = dtime_downcount;
2103 Setting this to 0 (no -=dtime_part) disables an infinite loop
2104 when dtime_part is so small that dtime_downcount -= dtime_part
2107 dtime_downcount = 0;
2116 if(free_move == false && is_climbing == false)
2119 v3f speed = lplayer->getSpeed();
2120 if(lplayer->in_liquid == false)
2121 speed.Y -= lplayer->movement_gravity * lplayer->physics_override_gravity * dtime_part * 2;
2123 // Liquid floating / sinking
2124 if(lplayer->in_liquid && !lplayer->swimming_vertical)
2125 speed.Y -= lplayer->movement_liquid_sink * dtime_part * 2;
2127 // Liquid resistance
2128 if(lplayer->in_liquid_stable || lplayer->in_liquid)
2130 // How much the node's viscosity blocks movement, ranges between 0 and 1
2131 // Should match the scale at which viscosity increase affects other liquid attributes
2132 const f32 viscosity_factor = 0.3;
2134 v3f d_wanted = -speed / lplayer->movement_liquid_fluidity;
2135 f32 dl = d_wanted.getLength();
2136 if(dl > lplayer->movement_liquid_fluidity_smooth)
2137 dl = lplayer->movement_liquid_fluidity_smooth;
2138 dl *= (lplayer->liquid_viscosity * viscosity_factor) + (1 - viscosity_factor);
2140 v3f d = d_wanted.normalize() * dl;
2144 if(speed.X > lplayer->movement_liquid_fluidity + lplayer->movement_liquid_fluidity_smooth) speed.X -= lplayer->movement_liquid_fluidity_smooth;
2145 if(speed.X < -lplayer->movement_liquid_fluidity - lplayer->movement_liquid_fluidity_smooth) speed.X += lplayer->movement_liquid_fluidity_smooth;
2146 if(speed.Y > lplayer->movement_liquid_fluidity + lplayer->movement_liquid_fluidity_smooth) speed.Y -= 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.Z > lplayer->movement_liquid_fluidity + lplayer->movement_liquid_fluidity_smooth) speed.Z -= 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;
2153 lplayer->setSpeed(speed);
2158 This also does collision detection.
2160 lplayer->move(dtime_part, this, position_max_increment,
2161 &player_collisions);
2164 while(dtime_downcount > 0.001);
2166 //std::cout<<"Looped "<<loopcount<<" times."<<std::endl;
2168 for(std::list<CollisionInfo>::iterator
2169 i = player_collisions.begin();
2170 i != player_collisions.end(); ++i)
2172 CollisionInfo &info = *i;
2173 v3f speed_diff = info.new_speed - info.old_speed;;
2174 // Handle only fall damage
2175 // (because otherwise walking against something in fast_move kills you)
2176 if(speed_diff.Y < 0 || info.old_speed.Y >= 0)
2178 // Get rid of other components
2181 f32 pre_factor = 1; // 1 hp per node/s
2182 f32 tolerance = BS*14; // 5 without damage
2183 f32 post_factor = 1; // 1 hp per node/s
2184 if(info.type == COLLISION_NODE)
2186 const ContentFeatures &f = m_gamedef->ndef()->
2187 get(m_map->getNodeNoEx(info.node_p));
2188 // Determine fall damage multiplier
2189 int addp = itemgroup_get(f.groups, "fall_damage_add_percent");
2190 pre_factor = 1.0 + (float)addp/100.0;
2192 float speed = pre_factor * speed_diff.getLength();
2193 if(speed > tolerance)
2195 f32 damage_f = (speed - tolerance)/BS * post_factor;
2196 u16 damage = (u16)(damage_f+0.5);
2198 damageLocalPlayer(damage, true);
2199 MtEvent *e = new SimpleTriggerEvent("PlayerFallingDamage");
2200 m_gamedef->event()->put(e);
2206 A quick draft of lava damage
2208 if(m_lava_hurt_interval.step(dtime, 1.0))
2210 v3f pf = lplayer->getPosition();
2212 // Feet, middle and head
2213 v3s16 p1 = floatToInt(pf + v3f(0, BS*0.1, 0), BS);
2214 MapNode n1 = m_map->getNodeNoEx(p1);
2215 v3s16 p2 = floatToInt(pf + v3f(0, BS*0.8, 0), BS);
2216 MapNode n2 = m_map->getNodeNoEx(p2);
2217 v3s16 p3 = floatToInt(pf + v3f(0, BS*1.6, 0), BS);
2218 MapNode n3 = m_map->getNodeNoEx(p3);
2220 u32 damage_per_second = 0;
2221 damage_per_second = MYMAX(damage_per_second,
2222 m_gamedef->ndef()->get(n1).damage_per_second);
2223 damage_per_second = MYMAX(damage_per_second,
2224 m_gamedef->ndef()->get(n2).damage_per_second);
2225 damage_per_second = MYMAX(damage_per_second,
2226 m_gamedef->ndef()->get(n3).damage_per_second);
2228 if(damage_per_second != 0)
2230 damageLocalPlayer(damage_per_second, true);
2237 if(m_drowning_interval.step(dtime, 2.0))
2239 v3f pf = lplayer->getPosition();
2242 v3s16 p = floatToInt(pf + v3f(0, BS*1.6, 0), BS);
2243 MapNode n = m_map->getNodeNoEx(p);
2244 ContentFeatures c = m_gamedef->ndef()->get(n);
2245 u8 drowning_damage = c.drowning;
2246 if(drowning_damage > 0 && lplayer->hp > 0){
2247 u16 breath = lplayer->getBreath();
2254 lplayer->setBreath(breath);
2255 updateLocalPlayerBreath(breath);
2258 if(lplayer->getBreath() == 0 && drowning_damage > 0){
2259 damageLocalPlayer(drowning_damage, true);
2262 if(m_breathing_interval.step(dtime, 0.5))
2264 v3f pf = lplayer->getPosition();
2267 v3s16 p = floatToInt(pf + v3f(0, BS*1.6, 0), BS);
2268 MapNode n = m_map->getNodeNoEx(p);
2269 ContentFeatures c = m_gamedef->ndef()->get(n);
2271 lplayer->setBreath(11);
2273 else if(c.drowning == 0){
2274 u16 breath = lplayer->getBreath();
2277 lplayer->setBreath(breath);
2278 updateLocalPlayerBreath(breath);
2284 Stuff that can be done in an arbitarily large dtime
2286 for(std::list<Player*>::iterator i = m_players.begin();
2287 i != m_players.end(); ++i)
2289 Player *player = *i;
2292 Handle non-local players
2294 if(player->isLocal() == false)
2297 player->move(dtime, *m_map, 100*BS);
2301 // Update lighting on all players on client
2305 v3s16 p = player->getLightPosition();
2306 MapNode n = m_map->getNode(p);
2307 light = n.getLightBlendF1((float)getDayNightRatio()/1000, m_gamedef->ndef());
2309 catch(InvalidPositionException &e){
2310 light = blend_light_f1((float)getDayNightRatio()/1000, LIGHT_SUN, 0);
2312 player->light = light;
2316 Step active objects and update lighting of them
2319 g_profiler->avg("CEnv: num of objects", m_active_objects.size());
2320 bool update_lighting = m_active_object_light_update_interval.step(dtime, 0.21);
2321 for(std::map<u16, ClientActiveObject*>::iterator
2322 i = m_active_objects.begin();
2323 i != m_active_objects.end(); ++i)
2325 ClientActiveObject* obj = i->second;
2327 obj->step(dtime, this);
2335 v3s16 p = obj->getLightPosition();
2336 MapNode n = m_map->getNode(p);
2337 light = n.getLightBlend(getDayNightRatio(), m_gamedef->ndef());
2339 catch(InvalidPositionException &e){
2340 light = blend_light(getDayNightRatio(), LIGHT_SUN, 0);
2342 obj->updateLight(light);
2347 Step and handle simple objects
2349 g_profiler->avg("CEnv: num of simple objects", m_simple_objects.size());
2350 for(std::list<ClientSimpleObject*>::iterator
2351 i = m_simple_objects.begin(); i != m_simple_objects.end();)
2353 ClientSimpleObject *simple = *i;
2354 std::list<ClientSimpleObject*>::iterator cur = i;
2356 simple->step(dtime);
2357 if(simple->m_to_be_removed){
2359 m_simple_objects.erase(cur);
2364 void ClientEnvironment::addSimpleObject(ClientSimpleObject *simple)
2366 m_simple_objects.push_back(simple);
2369 ClientActiveObject* ClientEnvironment::getActiveObject(u16 id)
2371 std::map<u16, ClientActiveObject*>::iterator n;
2372 n = m_active_objects.find(id);
2373 if(n == m_active_objects.end())
2378 bool isFreeClientActiveObjectId(u16 id,
2379 std::map<u16, ClientActiveObject*> &objects)
2384 return objects.find(id) == objects.end();
2387 u16 getFreeClientActiveObjectId(
2388 std::map<u16, ClientActiveObject*> &objects)
2390 //try to reuse id's as late as possible
2391 static u16 last_used_id = 0;
2392 u16 startid = last_used_id;
2396 if(isFreeClientActiveObjectId(last_used_id, objects))
2397 return last_used_id;
2399 if(last_used_id == startid)
2404 u16 ClientEnvironment::addActiveObject(ClientActiveObject *object)
2407 if(object->getId() == 0)
2409 u16 new_id = getFreeClientActiveObjectId(m_active_objects);
2412 infostream<<"ClientEnvironment::addActiveObject(): "
2413 <<"no free ids available"<<std::endl;
2417 object->setId(new_id);
2419 if(isFreeClientActiveObjectId(object->getId(), m_active_objects) == false)
2421 infostream<<"ClientEnvironment::addActiveObject(): "
2422 <<"id is not free ("<<object->getId()<<")"<<std::endl;
2426 infostream<<"ClientEnvironment::addActiveObject(): "
2427 <<"added (id="<<object->getId()<<")"<<std::endl;
2428 m_active_objects[object->getId()] = object;
2429 object->addToScene(m_smgr, m_texturesource, m_irr);
2430 { // Update lighting immediately
2434 v3s16 p = object->getLightPosition();
2435 MapNode n = m_map->getNode(p);
2436 light = n.getLightBlend(getDayNightRatio(), m_gamedef->ndef());
2438 catch(InvalidPositionException &e){
2439 light = blend_light(getDayNightRatio(), LIGHT_SUN, 0);
2441 object->updateLight(light);
2443 return object->getId();
2446 void ClientEnvironment::addActiveObject(u16 id, u8 type,
2447 const std::string &init_data)
2449 ClientActiveObject* obj =
2450 ClientActiveObject::create(type, m_gamedef, this);
2453 infostream<<"ClientEnvironment::addActiveObject(): "
2454 <<"id="<<id<<" type="<<type<<": Couldn't create object"
2463 obj->initialize(init_data);
2465 catch(SerializationError &e)
2467 errorstream<<"ClientEnvironment::addActiveObject():"
2468 <<" id="<<id<<" type="<<type
2469 <<": SerializationError in initialize(): "
2471 <<": init_data="<<serializeJsonString(init_data)
2475 addActiveObject(obj);
2478 void ClientEnvironment::removeActiveObject(u16 id)
2480 verbosestream<<"ClientEnvironment::removeActiveObject(): "
2481 <<"id="<<id<<std::endl;
2482 ClientActiveObject* obj = getActiveObject(id);
2485 infostream<<"ClientEnvironment::removeActiveObject(): "
2486 <<"id="<<id<<" not found"<<std::endl;
2489 obj->removeFromScene(true);
2491 m_active_objects.erase(id);
2494 void ClientEnvironment::processActiveObjectMessage(u16 id,
2495 const std::string &data)
2497 ClientActiveObject* obj = getActiveObject(id);
2500 infostream<<"ClientEnvironment::processActiveObjectMessage():"
2501 <<" got message for id="<<id<<", which doesn't exist."
2507 obj->processMessage(data);
2509 catch(SerializationError &e)
2511 errorstream<<"ClientEnvironment::processActiveObjectMessage():"
2512 <<" id="<<id<<" type="<<obj->getType()
2513 <<" SerializationError in processMessage(),"
2514 <<" message="<<serializeJsonString(data)
2520 Callbacks for activeobjects
2523 void ClientEnvironment::damageLocalPlayer(u8 damage, bool handle_hp)
2525 LocalPlayer *lplayer = getLocalPlayer();
2529 if(lplayer->hp > damage)
2530 lplayer->hp -= damage;
2535 ClientEnvEvent event;
2536 event.type = CEE_PLAYER_DAMAGE;
2537 event.player_damage.amount = damage;
2538 event.player_damage.send_to_server = handle_hp;
2539 m_client_event_queue.push_back(event);
2542 void ClientEnvironment::updateLocalPlayerBreath(u16 breath)
2544 ClientEnvEvent event;
2545 event.type = CEE_PLAYER_BREATH;
2546 event.player_breath.amount = breath;
2547 m_client_event_queue.push_back(event);
2551 Client likes to call these
2554 void ClientEnvironment::getActiveObjects(v3f origin, f32 max_d,
2555 std::vector<DistanceSortedActiveObject> &dest)
2557 for(std::map<u16, ClientActiveObject*>::iterator
2558 i = m_active_objects.begin();
2559 i != m_active_objects.end(); ++i)
2561 ClientActiveObject* obj = i->second;
2563 f32 d = (obj->getPosition() - origin).getLength();
2568 DistanceSortedActiveObject dso(obj, d);
2570 dest.push_back(dso);
2574 ClientEnvEvent ClientEnvironment::getClientEvent()
2576 if(m_client_event_queue.empty())
2578 ClientEnvEvent event;
2579 event.type = CEE_NONE;
2582 return m_client_event_queue.pop_front();
2585 #endif // #ifndef SERVER