3 Copyright (C) 2010-2013 celeron55, Perttu Ahola <celeron55@gmail.com>
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU Lesser General Public License as published by
7 the Free Software Foundation; either version 2.1 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU Lesser General Public License for more details.
15 You should have received a copy of the GNU Lesser General Public License along
16 with this program; if not, write to the Free Software Foundation, Inc.,
17 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20 #include "environment.h"
23 #include "collision.h"
24 #include "content_mapnode.h"
26 #include "serverobject.h"
27 #include "content_sao.h"
31 #include "scripting_game.h"
33 #include "nodemetadata.h"
34 #include "main.h" // For g_settings, g_profiler
37 #include "clientmap.h"
38 #include "localplayer.h"
41 #include "daynightratio.h"
44 #include "util/serialize.h"
46 #define PP(x) "("<<(x).X<<","<<(x).Y<<","<<(x).Z<<")"
48 Environment::Environment():
50 m_time_of_day_f(9000./24000),
51 m_time_of_day_speed(0),
56 Environment::~Environment()
59 for(std::list<Player*>::iterator i = m_players.begin();
60 i != m_players.end(); ++i)
66 void Environment::addPlayer(Player *player)
68 DSTACK(__FUNCTION_NAME);
70 Check that peer_ids are unique.
71 Also check that names are unique.
72 Exception: there can be multiple players with peer_id=0
74 // If peer id is non-zero, it has to be unique.
75 if(player->peer_id != 0)
76 assert(getPlayer(player->peer_id) == NULL);
77 // Name has to be unique.
78 assert(getPlayer(player->getName()) == NULL);
80 m_players.push_back(player);
83 void Environment::removePlayer(u16 peer_id)
85 DSTACK(__FUNCTION_NAME);
87 for(std::list<Player*>::iterator i = m_players.begin();
88 i != m_players.end(); ++i)
91 if(player->peer_id != peer_id)
96 // See if there is an another one
97 // (shouldn't be, but just to be sure)
102 Player * Environment::getPlayer(u16 peer_id)
104 for(std::list<Player*>::iterator i = m_players.begin();
105 i != m_players.end(); ++i)
108 if(player->peer_id == peer_id)
114 Player * Environment::getPlayer(const char *name)
116 for(std::list<Player*>::iterator i = m_players.begin();
117 i != m_players.end(); ++i)
120 if(strcmp(player->getName(), name) == 0)
126 Player * Environment::getRandomConnectedPlayer()
128 std::list<Player*> connected_players = getPlayers(true);
129 u32 chosen_one = myrand() % connected_players.size();
131 for(std::list<Player*>::iterator
132 i = connected_players.begin();
133 i != connected_players.end(); ++i)
145 Player * Environment::getNearestConnectedPlayer(v3f pos)
147 std::list<Player*> connected_players = getPlayers(true);
149 Player *nearest_player = NULL;
150 for(std::list<Player*>::iterator
151 i = connected_players.begin();
152 i != connected_players.end(); ++i)
155 f32 d = player->getPosition().getDistanceFrom(pos);
156 if(d < nearest_d || nearest_player == NULL)
159 nearest_player = player;
162 return nearest_player;
165 std::list<Player*> Environment::getPlayers()
170 std::list<Player*> Environment::getPlayers(bool ignore_disconnected)
172 std::list<Player*> newlist;
173 for(std::list<Player*>::iterator
174 i = m_players.begin();
175 i != m_players.end(); ++i)
179 if(ignore_disconnected)
181 // Ignore disconnected players
182 if(player->peer_id == 0)
186 newlist.push_back(player);
191 u32 Environment::getDayNightRatio()
193 bool smooth = g_settings->getBool("enable_shaders");
194 return time_to_daynight_ratio(m_time_of_day_f*24000, smooth);
197 void Environment::stepTimeOfDay(float dtime)
199 m_time_counter += dtime;
200 f32 speed = m_time_of_day_speed * 24000./(24.*3600);
201 u32 units = (u32)(m_time_counter*speed);
202 m_time_counter -= (f32)units / speed;
206 if(m_time_of_day + units >= 24000)
208 m_time_of_day = (m_time_of_day + units) % 24000;
210 m_time_of_day_f = (float)m_time_of_day / 24000.0;
213 m_time_of_day_f += m_time_of_day_speed/24/3600*dtime;
214 if(m_time_of_day_f > 1.0)
215 m_time_of_day_f -= 1.0;
216 if(m_time_of_day_f < 0.0)
217 m_time_of_day_f += 1.0;
225 ABMWithState::ABMWithState(ActiveBlockModifier *abm_):
229 // Initialize timer to random value to spread processing
230 float itv = abm->getTriggerInterval();
231 itv = MYMAX(0.001, itv); // No less than 1ms
232 int minval = MYMAX(-0.51*itv, -60); // Clamp to
233 int maxval = MYMIN(0.51*itv, 60); // +-60 seconds
234 timer = myrand_range(minval, maxval);
241 void fillRadiusBlock(v3s16 p0, s16 r, std::set<v3s16> &list)
244 for(p.X=p0.X-r; p.X<=p0.X+r; p.X++)
245 for(p.Y=p0.Y-r; p.Y<=p0.Y+r; p.Y++)
246 for(p.Z=p0.Z-r; p.Z<=p0.Z+r; p.Z++)
253 void ActiveBlockList::update(std::list<v3s16> &active_positions,
255 std::set<v3s16> &blocks_removed,
256 std::set<v3s16> &blocks_added)
261 std::set<v3s16> newlist;
262 for(std::list<v3s16>::iterator i = active_positions.begin();
263 i != active_positions.end(); ++i)
265 fillRadiusBlock(*i, radius, newlist);
269 Find out which blocks on the old list are not on the new list
271 // Go through old list
272 for(std::set<v3s16>::iterator i = m_list.begin();
273 i != m_list.end(); ++i)
276 // If not on new list, it's been removed
277 if(newlist.find(p) == newlist.end())
278 blocks_removed.insert(p);
282 Find out which blocks on the new list are not on the old list
284 // Go through new list
285 for(std::set<v3s16>::iterator i = newlist.begin();
286 i != newlist.end(); ++i)
289 // If not on old list, it's been added
290 if(m_list.find(p) == m_list.end())
291 blocks_added.insert(p);
298 for(std::set<v3s16>::iterator i = newlist.begin();
299 i != newlist.end(); ++i)
310 ServerEnvironment::ServerEnvironment(ServerMap *map,
311 GameScripting *scriptIface,
312 IGameDef *gamedef, IBackgroundBlockEmerger *emerger):
314 m_script(scriptIface),
317 m_random_spawn_timer(3),
318 m_send_recommended_timer(0),
319 m_active_block_interval_overload_skip(0),
321 m_game_time_fraction_counter(0),
322 m_recommended_send_interval(0.1),
323 m_max_lag_estimate(0.1)
325 m_use_weather = g_settings->getBool("weather");
328 ServerEnvironment::~ServerEnvironment()
330 // Clear active block list.
331 // This makes the next one delete all active objects.
332 m_active_blocks.clear();
334 // Convert all objects to static and delete the active objects
335 deactivateFarObjects(true);
340 // Delete ActiveBlockModifiers
341 for(std::list<ABMWithState>::iterator
342 i = m_abms.begin(); i != m_abms.end(); ++i){
347 Map & ServerEnvironment::getMap()
352 ServerMap & ServerEnvironment::getServerMap()
357 bool ServerEnvironment::line_of_sight(v3f pos1, v3f pos2, float stepsize)
359 float distance = pos1.getDistanceFrom(pos2);
361 //calculate normalized direction vector
362 v3f normalized_vector = v3f((pos2.X - pos1.X)/distance,
363 (pos2.Y - pos1.Y)/distance,
364 (pos2.Z - pos1.Z)/distance);
366 //find out if there's a node on path between pos1 and pos2
367 for (float i = 1; i < distance; i += stepsize) {
368 v3s16 pos = floatToInt(v3f(normalized_vector.X * i,
369 normalized_vector.Y * i,
370 normalized_vector.Z * i) +pos1,BS);
372 MapNode n = getMap().getNodeNoEx(pos);
374 if(n.param0 != CONTENT_AIR) {
381 void ServerEnvironment::serializePlayers(const std::string &savedir)
383 std::string players_path = savedir + "/players";
384 fs::CreateDir(players_path);
386 std::set<Player*> saved_players;
388 std::vector<fs::DirListNode> player_files = fs::GetDirListing(players_path);
389 for(u32 i=0; i<player_files.size(); i++)
391 if(player_files[i].dir || player_files[i].name[0] == '.')
394 // Full path to this file
395 std::string path = players_path + "/" + player_files[i].name;
397 //infostream<<"Checking player file "<<path<<std::endl;
399 // Load player to see what is its name
400 RemotePlayer testplayer(m_gamedef);
402 // Open file and deserialize
403 std::ifstream is(path.c_str(), std::ios_base::binary);
404 if(is.good() == false)
406 infostream<<"Failed to read "<<path<<std::endl;
409 testplayer.deSerialize(is, player_files[i].name);
412 //infostream<<"Loaded test player with name "<<testplayer.getName()<<std::endl;
414 // Search for the player
415 std::string playername = testplayer.getName();
416 Player *player = getPlayer(playername.c_str());
419 infostream<<"Didn't find matching player, ignoring file "<<path<<std::endl;
423 //infostream<<"Found matching player, overwriting."<<std::endl;
425 // OK, found. Save player there.
426 if(player->checkModified())
428 // Open file and serialize
429 std::ostringstream ss(std::ios_base::binary);
430 player->serialize(ss);
431 if(!fs::safeWriteToFile(path, ss.str()))
433 infostream<<"Failed to write "<<path<<std::endl;
436 saved_players.insert(player);
438 saved_players.insert(player);
442 for(std::list<Player*>::iterator i = m_players.begin();
443 i != m_players.end(); ++i)
446 if(saved_players.find(player) != saved_players.end())
448 /*infostream<<"Player "<<player->getName()
449 <<" was already saved."<<std::endl;*/
452 std::string playername = player->getName();
453 // Don't save unnamed player
456 //infostream<<"Not saving unnamed player."<<std::endl;
462 if(string_allowed(playername, PLAYERNAME_ALLOWED_CHARS) == false)
463 playername = "player";
464 std::string path = players_path + "/" + playername;
466 for(u32 i=0; i<1000; i++)
468 if(fs::PathExists(path) == false)
473 path = players_path + "/" + playername + itos(i);
477 infostream<<"Didn't find free file for player"<<std::endl;
482 /*infostream<<"Saving player "<<player->getName()<<" to "
484 // Open file and serialize
485 std::ostringstream ss(std::ios_base::binary);
486 player->serialize(ss);
487 if(!fs::safeWriteToFile(path, ss.str()))
489 infostream<<"Failed to write "<<path<<std::endl;
492 saved_players.insert(player);
496 //infostream<<"Saved "<<saved_players.size()<<" players."<<std::endl;
499 void ServerEnvironment::deSerializePlayers(const std::string &savedir)
501 std::string players_path = savedir + "/players";
503 std::vector<fs::DirListNode> player_files = fs::GetDirListing(players_path);
504 for(u32 i=0; i<player_files.size(); i++)
506 if(player_files[i].dir)
509 // Full path to this file
510 std::string path = players_path + "/" + player_files[i].name;
512 //infostream<<"Checking player file "<<path<<std::endl;
514 // Load player to see what is its name
515 RemotePlayer testplayer(m_gamedef);
517 // Open file and deserialize
518 std::ifstream is(path.c_str(), std::ios_base::binary);
519 if(is.good() == false)
521 infostream<<"Failed to read "<<path<<std::endl;
524 testplayer.deSerialize(is, player_files[i].name);
527 if(!string_allowed(testplayer.getName(), PLAYERNAME_ALLOWED_CHARS))
529 infostream<<"Not loading player with invalid name: "
530 <<testplayer.getName()<<std::endl;
533 /*infostream<<"Loaded test player with name "<<testplayer.getName()
536 // Search for the player
537 std::string playername = testplayer.getName();
538 Player *player = getPlayer(playername.c_str());
539 bool newplayer = false;
542 //infostream<<"Is a new player"<<std::endl;
543 player = new RemotePlayer(m_gamedef);
549 verbosestream<<"Reading player "<<testplayer.getName()<<" from "
551 // Open file and deserialize
552 std::ifstream is(path.c_str(), std::ios_base::binary);
553 if(is.good() == false)
555 infostream<<"Failed to read "<<path<<std::endl;
558 player->deSerialize(is, player_files[i].name);
568 void ServerEnvironment::saveMeta(const std::string &savedir)
570 std::string path = savedir + "/env_meta.txt";
572 // Open file and serialize
573 std::ostringstream ss(std::ios_base::binary);
576 args.setU64("game_time", m_game_time);
577 args.setU64("time_of_day", getTimeOfDay());
581 if(!fs::safeWriteToFile(path, ss.str()))
583 infostream<<"ServerEnvironment::saveMeta(): Failed to write "
585 throw SerializationError("Couldn't save env meta");
589 void ServerEnvironment::loadMeta(const std::string &savedir)
591 std::string path = savedir + "/env_meta.txt";
593 // Open file and deserialize
594 std::ifstream is(path.c_str(), std::ios_base::binary);
595 if(is.good() == false)
597 infostream<<"ServerEnvironment::loadMeta(): Failed to open "
599 throw SerializationError("Couldn't load env meta");
607 throw SerializationError
608 ("ServerEnvironment::loadMeta(): EnvArgsEnd not found");
610 std::getline(is, line);
611 std::string trimmedline = trim(line);
612 if(trimmedline == "EnvArgsEnd")
614 args.parseConfigLine(line);
618 m_game_time = args.getU64("game_time");
619 }catch(SettingNotFoundException &e){
620 // Getting this is crucial, otherwise timestamps are useless
621 throw SerializationError("Couldn't load env meta game_time");
625 m_time_of_day = args.getU64("time_of_day");
626 }catch(SettingNotFoundException &e){
627 // This is not as important
628 m_time_of_day = 9000;
634 ActiveBlockModifier *abm;
636 std::set<content_t> required_neighbors;
642 ServerEnvironment *m_env;
643 std::map<content_t, std::list<ActiveABM> > m_aabms;
645 ABMHandler(std::list<ABMWithState> &abms,
646 float dtime_s, ServerEnvironment *env,
652 INodeDefManager *ndef = env->getGameDef()->ndef();
653 for(std::list<ABMWithState>::iterator
654 i = abms.begin(); i != abms.end(); ++i){
655 ActiveBlockModifier *abm = i->abm;
656 float trigger_interval = abm->getTriggerInterval();
657 if(trigger_interval < 0.001)
658 trigger_interval = 0.001;
659 float actual_interval = dtime_s;
662 if(i->timer < trigger_interval)
664 i->timer -= trigger_interval;
665 actual_interval = trigger_interval;
667 float intervals = actual_interval / trigger_interval;
670 float chance = abm->getTriggerChance();
675 aabm.chance = chance / intervals;
679 std::set<std::string> required_neighbors_s
680 = abm->getRequiredNeighbors();
681 for(std::set<std::string>::iterator
682 i = required_neighbors_s.begin();
683 i != required_neighbors_s.end(); i++)
685 ndef->getIds(*i, aabm.required_neighbors);
688 std::set<std::string> contents_s = abm->getTriggerContents();
689 for(std::set<std::string>::iterator
690 i = contents_s.begin(); i != contents_s.end(); i++)
692 std::set<content_t> ids;
693 ndef->getIds(*i, ids);
694 for(std::set<content_t>::const_iterator k = ids.begin();
698 std::map<content_t, std::list<ActiveABM> >::iterator j;
700 if(j == m_aabms.end()){
701 std::list<ActiveABM> aabmlist;
702 m_aabms[c] = aabmlist;
705 j->second.push_back(aabm);
710 void apply(MapBlock *block)
715 ServerMap *map = &m_env->getServerMap();
718 for(p0.X=0; p0.X<MAP_BLOCKSIZE; p0.X++)
719 for(p0.Y=0; p0.Y<MAP_BLOCKSIZE; p0.Y++)
720 for(p0.Z=0; p0.Z<MAP_BLOCKSIZE; p0.Z++)
722 MapNode n = block->getNodeNoEx(p0);
723 content_t c = n.getContent();
724 v3s16 p = p0 + block->getPosRelative();
726 std::map<content_t, std::list<ActiveABM> >::iterator j;
728 if(j == m_aabms.end())
731 for(std::list<ActiveABM>::iterator
732 i = j->second.begin(); i != j->second.end(); i++)
734 if(myrand() % i->chance != 0)
738 if(!i->required_neighbors.empty())
741 for(p1.X = p.X-1; p1.X <= p.X+1; p1.X++)
742 for(p1.Y = p.Y-1; p1.Y <= p.Y+1; p1.Y++)
743 for(p1.Z = p.Z-1; p1.Z <= p.Z+1; p1.Z++)
747 MapNode n = map->getNodeNoEx(p1);
748 content_t c = n.getContent();
749 std::set<content_t>::const_iterator k;
750 k = i->required_neighbors.find(c);
751 if(k != i->required_neighbors.end()){
755 // No required neighbor found
760 // Find out how many objects the block contains
761 u32 active_object_count = block->m_static_objects.m_active.size();
762 // Find out how many objects this and all the neighbors contain
763 u32 active_object_count_wider = 0;
764 u32 wider_unknown_count = 0;
765 for(s16 x=-1; x<=1; x++)
766 for(s16 y=-1; y<=1; y++)
767 for(s16 z=-1; z<=1; z++)
769 MapBlock *block2 = map->getBlockNoCreateNoEx(
770 block->getPos() + v3s16(x,y,z));
772 wider_unknown_count = 0;
775 active_object_count_wider +=
776 block2->m_static_objects.m_active.size()
777 + block2->m_static_objects.m_stored.size();
780 u32 wider_known_count = 3*3*3 - wider_unknown_count;
781 active_object_count_wider += wider_unknown_count * active_object_count_wider / wider_known_count;
783 // Call all the trigger variations
784 i->abm->trigger(m_env, p, n);
785 i->abm->trigger(m_env, p, n,
786 active_object_count, active_object_count_wider);
792 void ServerEnvironment::activateBlock(MapBlock *block, u32 additional_dtime)
794 // Get time difference
796 u32 stamp = block->getTimestamp();
797 if(m_game_time > stamp && stamp != BLOCK_TIMESTAMP_UNDEFINED)
798 dtime_s = m_game_time - block->getTimestamp();
799 dtime_s += additional_dtime;
801 /*infostream<<"ServerEnvironment::activateBlock(): block timestamp: "
802 <<stamp<<", game time: "<<m_game_time<<std::endl;*/
804 // Set current time as timestamp
805 block->setTimestampNoChangedFlag(m_game_time);
807 /*infostream<<"ServerEnvironment::activateBlock(): block is "
808 <<dtime_s<<" seconds old."<<std::endl;*/
810 // Activate stored objects
811 activateObjects(block, dtime_s);
814 std::map<v3s16, NodeTimer> elapsed_timers =
815 block->m_node_timers.step((float)dtime_s);
816 if(!elapsed_timers.empty()){
818 for(std::map<v3s16, NodeTimer>::iterator
819 i = elapsed_timers.begin();
820 i != elapsed_timers.end(); i++){
821 n = block->getNodeNoEx(i->first);
822 v3s16 p = i->first + block->getPosRelative();
823 if(m_script->node_on_timer(p,n,i->second.elapsed))
824 block->setNodeTimer(i->first,NodeTimer(i->second.timeout,0));
828 /* Handle ActiveBlockModifiers */
829 ABMHandler abmhandler(m_abms, dtime_s, this, false);
830 abmhandler.apply(block);
833 void ServerEnvironment::addActiveBlockModifier(ActiveBlockModifier *abm)
835 m_abms.push_back(ABMWithState(abm));
838 bool ServerEnvironment::setNode(v3s16 p, const MapNode &n)
840 INodeDefManager *ndef = m_gamedef->ndef();
841 MapNode n_old = m_map->getNodeNoEx(p);
843 if(ndef->get(n_old).has_on_destruct)
844 m_script->node_on_destruct(p, n_old);
846 bool succeeded = m_map->addNodeWithEvent(p, n);
849 // Call post-destructor
850 if(ndef->get(n_old).has_after_destruct)
851 m_script->node_after_destruct(p, n_old);
853 if(ndef->get(n).has_on_construct)
854 m_script->node_on_construct(p, n);
858 bool ServerEnvironment::removeNode(v3s16 p)
860 INodeDefManager *ndef = m_gamedef->ndef();
861 MapNode n_old = m_map->getNodeNoEx(p);
863 if(ndef->get(n_old).has_on_destruct)
864 m_script->node_on_destruct(p, n_old);
866 // This is slightly optimized compared to addNodeWithEvent(air)
867 bool succeeded = m_map->removeNodeWithEvent(p);
870 // Call post-destructor
871 if(ndef->get(n_old).has_after_destruct)
872 m_script->node_after_destruct(p, n_old);
873 // Air doesn't require constructor
877 bool ServerEnvironment::swapNode(v3s16 p, const MapNode &n)
879 return m_map->addNodeWithEvent(p, n, false);
882 std::set<u16> ServerEnvironment::getObjectsInsideRadius(v3f pos, float radius)
884 std::set<u16> objects;
885 for(std::map<u16, ServerActiveObject*>::iterator
886 i = m_active_objects.begin();
887 i != m_active_objects.end(); ++i)
889 ServerActiveObject* obj = i->second;
891 v3f objectpos = obj->getBasePosition();
892 if(objectpos.getDistanceFrom(pos) > radius)
899 void ServerEnvironment::clearAllObjects()
901 infostream<<"ServerEnvironment::clearAllObjects(): "
902 <<"Removing all active objects"<<std::endl;
903 std::list<u16> objects_to_remove;
904 for(std::map<u16, ServerActiveObject*>::iterator
905 i = m_active_objects.begin();
906 i != m_active_objects.end(); ++i)
908 ServerActiveObject* obj = i->second;
909 if(obj->getType() == ACTIVEOBJECT_TYPE_PLAYER)
912 // Delete static object if block is loaded
913 if(obj->m_static_exists){
914 MapBlock *block = m_map->getBlockNoCreateNoEx(obj->m_static_block);
916 block->m_static_objects.remove(id);
917 block->raiseModified(MOD_STATE_WRITE_NEEDED,
919 obj->m_static_exists = false;
922 // If known by some client, don't delete immediately
923 if(obj->m_known_by_count > 0){
924 obj->m_pending_deactivation = true;
925 obj->m_removed = true;
929 // Tell the object about removal
930 obj->removingFromEnvironment();
931 // Deregister in scripting api
932 m_script->removeObjectReference(obj);
934 // Delete active object
935 if(obj->environmentDeletes())
937 // Id to be removed from m_active_objects
938 objects_to_remove.push_back(id);
940 // Remove references from m_active_objects
941 for(std::list<u16>::iterator i = objects_to_remove.begin();
942 i != objects_to_remove.end(); ++i)
944 m_active_objects.erase(*i);
947 // Get list of loaded blocks
948 std::list<v3s16> loaded_blocks;
949 infostream<<"ServerEnvironment::clearAllObjects(): "
950 <<"Listing all loaded blocks"<<std::endl;
951 m_map->listAllLoadedBlocks(loaded_blocks);
952 infostream<<"ServerEnvironment::clearAllObjects(): "
953 <<"Done listing all loaded blocks: "
954 <<loaded_blocks.size()<<std::endl;
956 // Get list of loadable blocks
957 std::list<v3s16> loadable_blocks;
958 infostream<<"ServerEnvironment::clearAllObjects(): "
959 <<"Listing all loadable blocks"<<std::endl;
960 m_map->listAllLoadableBlocks(loadable_blocks);
961 infostream<<"ServerEnvironment::clearAllObjects(): "
962 <<"Done listing all loadable blocks: "
963 <<loadable_blocks.size()
964 <<", now clearing"<<std::endl;
966 // Grab a reference on each loaded block to avoid unloading it
967 for(std::list<v3s16>::iterator i = loaded_blocks.begin();
968 i != loaded_blocks.end(); ++i)
971 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
976 // Remove objects in all loadable blocks
977 u32 unload_interval = g_settings->getS32("max_clearobjects_extra_loaded_blocks");
978 unload_interval = MYMAX(unload_interval, 1);
979 u32 report_interval = loadable_blocks.size() / 10;
980 u32 num_blocks_checked = 0;
981 u32 num_blocks_cleared = 0;
982 u32 num_objs_cleared = 0;
983 for(std::list<v3s16>::iterator i = loadable_blocks.begin();
984 i != loadable_blocks.end(); ++i)
987 MapBlock *block = m_map->emergeBlock(p, false);
989 errorstream<<"ServerEnvironment::clearAllObjects(): "
990 <<"Failed to emerge block "<<PP(p)<<std::endl;
993 u32 num_stored = block->m_static_objects.m_stored.size();
994 u32 num_active = block->m_static_objects.m_active.size();
995 if(num_stored != 0 || num_active != 0){
996 block->m_static_objects.m_stored.clear();
997 block->m_static_objects.m_active.clear();
998 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1000 num_objs_cleared += num_stored + num_active;
1001 num_blocks_cleared++;
1003 num_blocks_checked++;
1005 if(num_blocks_checked % report_interval == 0){
1006 float percent = 100.0 * (float)num_blocks_checked /
1007 loadable_blocks.size();
1008 infostream<<"ServerEnvironment::clearAllObjects(): "
1009 <<"Cleared "<<num_objs_cleared<<" objects"
1010 <<" in "<<num_blocks_cleared<<" blocks ("
1011 <<percent<<"%)"<<std::endl;
1013 if(num_blocks_checked % unload_interval == 0){
1014 m_map->unloadUnreferencedBlocks();
1017 m_map->unloadUnreferencedBlocks();
1019 // Drop references that were added above
1020 for(std::list<v3s16>::iterator i = loaded_blocks.begin();
1021 i != loaded_blocks.end(); ++i)
1024 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1029 infostream<<"ServerEnvironment::clearAllObjects(): "
1030 <<"Finished: Cleared "<<num_objs_cleared<<" objects"
1031 <<" in "<<num_blocks_cleared<<" blocks"<<std::endl;
1034 void ServerEnvironment::step(float dtime)
1036 DSTACK(__FUNCTION_NAME);
1038 //TimeTaker timer("ServerEnv step");
1040 /* Step time of day */
1041 stepTimeOfDay(dtime);
1044 // NOTE: This is kind of funny on a singleplayer game, but doesn't
1045 // really matter that much.
1046 m_recommended_send_interval = g_settings->getFloat("dedicated_server_step");
1052 m_game_time_fraction_counter += dtime;
1053 u32 inc_i = (u32)m_game_time_fraction_counter;
1054 m_game_time += inc_i;
1055 m_game_time_fraction_counter -= (float)inc_i;
1062 ScopeProfiler sp(g_profiler, "SEnv: handle players avg", SPT_AVG);
1063 for(std::list<Player*>::iterator i = m_players.begin();
1064 i != m_players.end(); ++i)
1066 Player *player = *i;
1068 // Ignore disconnected players
1069 if(player->peer_id == 0)
1073 player->move(dtime, *m_map, 100*BS);
1078 Manage active block list
1080 if(m_active_blocks_management_interval.step(dtime, 2.0))
1082 ScopeProfiler sp(g_profiler, "SEnv: manage act. block list avg /2s", SPT_AVG);
1084 Get player block positions
1086 std::list<v3s16> players_blockpos;
1087 for(std::list<Player*>::iterator
1088 i = m_players.begin();
1089 i != m_players.end(); ++i)
1091 Player *player = *i;
1092 // Ignore disconnected players
1093 if(player->peer_id == 0)
1095 v3s16 blockpos = getNodeBlockPos(
1096 floatToInt(player->getPosition(), BS));
1097 players_blockpos.push_back(blockpos);
1101 Update list of active blocks, collecting changes
1103 const s16 active_block_range = g_settings->getS16("active_block_range");
1104 std::set<v3s16> blocks_removed;
1105 std::set<v3s16> blocks_added;
1106 m_active_blocks.update(players_blockpos, active_block_range,
1107 blocks_removed, blocks_added);
1110 Handle removed blocks
1113 // Convert active objects that are no more in active blocks to static
1114 deactivateFarObjects(false);
1116 for(std::set<v3s16>::iterator
1117 i = blocks_removed.begin();
1118 i != blocks_removed.end(); ++i)
1122 /* infostream<<"Server: Block " << PP(p)
1123 << " became inactive"<<std::endl; */
1125 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1129 // Set current time as timestamp (and let it set ChangedFlag)
1130 block->setTimestamp(m_game_time);
1137 for(std::set<v3s16>::iterator
1138 i = blocks_added.begin();
1139 i != blocks_added.end(); ++i)
1143 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1145 // Block needs to be fetched first
1146 m_emerger->enqueueBlockEmerge(
1147 PEER_ID_INEXISTENT, p, false);
1148 m_active_blocks.m_list.erase(p);
1152 activateBlock(block);
1153 /* infostream<<"Server: Block " << PP(p)
1154 << " became active"<<std::endl; */
1159 Mess around in active blocks
1161 if(m_active_blocks_nodemetadata_interval.step(dtime, 1.0))
1163 ScopeProfiler sp(g_profiler, "SEnv: mess in act. blocks avg /1s", SPT_AVG);
1167 for(std::set<v3s16>::iterator
1168 i = m_active_blocks.m_list.begin();
1169 i != m_active_blocks.m_list.end(); ++i)
1173 /*infostream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
1174 <<") being handled"<<std::endl;*/
1176 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1180 // Reset block usage timer
1181 block->resetUsageTimer();
1183 // Set current time as timestamp
1184 block->setTimestampNoChangedFlag(m_game_time);
1185 // If time has changed much from the one on disk,
1186 // set block to be saved when it is unloaded
1187 if(block->getTimestamp() > block->getDiskTimestamp() + 60)
1188 block->raiseModified(MOD_STATE_WRITE_AT_UNLOAD,
1189 "Timestamp older than 60s (step)");
1192 std::map<v3s16, NodeTimer> elapsed_timers =
1193 block->m_node_timers.step((float)dtime);
1194 if(!elapsed_timers.empty()){
1196 for(std::map<v3s16, NodeTimer>::iterator
1197 i = elapsed_timers.begin();
1198 i != elapsed_timers.end(); i++){
1199 n = block->getNodeNoEx(i->first);
1200 p = i->first + block->getPosRelative();
1201 if(m_script->node_on_timer(p,n,i->second.elapsed))
1202 block->setNodeTimer(i->first,NodeTimer(i->second.timeout,0));
1208 const float abm_interval = 1.0;
1209 if(m_active_block_modifier_interval.step(dtime, abm_interval))
1211 if(m_active_block_interval_overload_skip > 0){
1212 ScopeProfiler sp(g_profiler, "SEnv: ABM overload skips");
1213 m_active_block_interval_overload_skip--;
1216 ScopeProfiler sp(g_profiler, "SEnv: modify in blocks avg /1s", SPT_AVG);
1217 TimeTaker timer("modify in active blocks");
1219 // Initialize handling of ActiveBlockModifiers
1220 ABMHandler abmhandler(m_abms, abm_interval, this, true);
1222 for(std::set<v3s16>::iterator
1223 i = m_active_blocks.m_list.begin();
1224 i != m_active_blocks.m_list.end(); ++i)
1228 /*infostream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
1229 <<") being handled"<<std::endl;*/
1231 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1235 // Set current time as timestamp
1236 block->setTimestampNoChangedFlag(m_game_time);
1238 /* Handle ActiveBlockModifiers */
1239 abmhandler.apply(block);
1242 u32 time_ms = timer.stop(true);
1243 u32 max_time_ms = 200;
1244 if(time_ms > max_time_ms){
1245 infostream<<"WARNING: active block modifiers took "
1246 <<time_ms<<"ms (longer than "
1247 <<max_time_ms<<"ms)"<<std::endl;
1248 m_active_block_interval_overload_skip = (time_ms / max_time_ms) + 1;
1253 Step script environment (run global on_step())
1255 m_script->environment_Step(dtime);
1261 ScopeProfiler sp(g_profiler, "SEnv: step act. objs avg", SPT_AVG);
1262 //TimeTaker timer("Step active objects");
1264 g_profiler->avg("SEnv: num of objects", m_active_objects.size());
1266 // This helps the objects to send data at the same time
1267 bool send_recommended = false;
1268 m_send_recommended_timer += dtime;
1269 if(m_send_recommended_timer > getSendRecommendedInterval())
1271 m_send_recommended_timer -= getSendRecommendedInterval();
1272 send_recommended = true;
1275 for(std::map<u16, ServerActiveObject*>::iterator
1276 i = m_active_objects.begin();
1277 i != m_active_objects.end(); ++i)
1279 ServerActiveObject* obj = i->second;
1280 // Remove non-peaceful mobs on peaceful mode
1281 if(g_settings->getBool("only_peaceful_mobs")){
1282 if(!obj->isPeaceful())
1283 obj->m_removed = true;
1285 // Don't step if is to be removed or stored statically
1286 if(obj->m_removed || obj->m_pending_deactivation)
1289 obj->step(dtime, send_recommended);
1290 // Read messages from object
1291 while(!obj->m_messages_out.empty())
1293 m_active_object_messages.push_back(
1294 obj->m_messages_out.pop_front());
1300 Manage active objects
1302 if(m_object_management_interval.step(dtime, 0.5))
1304 ScopeProfiler sp(g_profiler, "SEnv: remove removed objs avg /.5s", SPT_AVG);
1306 Remove objects that satisfy (m_removed && m_known_by_count==0)
1308 removeRemovedObjects();
1312 ServerActiveObject* ServerEnvironment::getActiveObject(u16 id)
1314 std::map<u16, ServerActiveObject*>::iterator n;
1315 n = m_active_objects.find(id);
1316 if(n == m_active_objects.end())
1321 bool isFreeServerActiveObjectId(u16 id,
1322 std::map<u16, ServerActiveObject*> &objects)
1327 return objects.find(id) == objects.end();
1330 u16 getFreeServerActiveObjectId(
1331 std::map<u16, ServerActiveObject*> &objects)
1333 //try to reuse id's as late as possible
1334 static u16 last_used_id = 0;
1335 u16 startid = last_used_id;
1339 if(isFreeServerActiveObjectId(last_used_id, objects))
1340 return last_used_id;
1342 if(last_used_id == startid)
1347 u16 ServerEnvironment::addActiveObject(ServerActiveObject *object)
1350 u16 id = addActiveObjectRaw(object, true, 0);
1355 bool ServerEnvironment::addActiveObjectAsStatic(ServerActiveObject *obj)
1359 v3f objectpos = obj->getBasePosition();
1361 // The block in which the object resides in
1362 v3s16 blockpos_o = getNodeBlockPos(floatToInt(objectpos, BS));
1365 Update the static data
1368 // Create new static object
1369 std::string staticdata = obj->getStaticData();
1370 StaticObject s_obj(obj->getType(), objectpos, staticdata);
1371 // Add to the block where the object is located in
1372 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1373 // Get or generate the block
1374 MapBlock *block = m_map->emergeBlock(blockpos);
1376 bool succeeded = false;
1380 block->m_static_objects.insert(0, s_obj);
1381 block->raiseModified(MOD_STATE_WRITE_AT_UNLOAD,
1382 "addActiveObjectAsStatic");
1386 infostream<<"ServerEnvironment::addActiveObjectAsStatic: "
1387 <<"Could not find or generate "
1388 <<"a block for storing static object"<<std::endl;
1392 if(obj->environmentDeletes())
1400 Finds out what new objects have been added to
1401 inside a radius around a position
1403 void ServerEnvironment::getAddedActiveObjects(v3s16 pos, s16 radius,
1404 std::set<u16> ¤t_objects,
1405 std::set<u16> &added_objects)
1407 v3f pos_f = intToFloat(pos, BS);
1408 f32 radius_f = radius * BS;
1410 Go through the object list,
1411 - discard m_removed objects,
1412 - discard objects that are too far away,
1413 - discard objects that are found in current_objects.
1414 - add remaining objects to added_objects
1416 for(std::map<u16, ServerActiveObject*>::iterator
1417 i = m_active_objects.begin();
1418 i != m_active_objects.end(); ++i)
1422 ServerActiveObject *object = i->second;
1425 // Discard if removed or deactivating
1426 if(object->m_removed || object->m_pending_deactivation)
1428 if(object->unlimitedTransferDistance() == false){
1429 // Discard if too far
1430 f32 distance_f = object->getBasePosition().getDistanceFrom(pos_f);
1431 if(distance_f > radius_f)
1434 // Discard if already on current_objects
1435 std::set<u16>::iterator n;
1436 n = current_objects.find(id);
1437 if(n != current_objects.end())
1439 // Add to added_objects
1440 added_objects.insert(id);
1445 Finds out what objects have been removed from
1446 inside a radius around a position
1448 void ServerEnvironment::getRemovedActiveObjects(v3s16 pos, s16 radius,
1449 std::set<u16> ¤t_objects,
1450 std::set<u16> &removed_objects)
1452 v3f pos_f = intToFloat(pos, BS);
1453 f32 radius_f = radius * BS;
1455 Go through current_objects; object is removed if:
1456 - object is not found in m_active_objects (this is actually an
1457 error condition; objects should be set m_removed=true and removed
1458 only after all clients have been informed about removal), or
1459 - object has m_removed=true, or
1460 - object is too far away
1462 for(std::set<u16>::iterator
1463 i = current_objects.begin();
1464 i != current_objects.end(); ++i)
1467 ServerActiveObject *object = getActiveObject(id);
1470 infostream<<"ServerEnvironment::getRemovedActiveObjects():"
1471 <<" object in current_objects is NULL"<<std::endl;
1472 removed_objects.insert(id);
1476 if(object->m_removed || object->m_pending_deactivation)
1478 removed_objects.insert(id);
1482 // If transfer distance is unlimited, don't remove
1483 if(object->unlimitedTransferDistance())
1486 f32 distance_f = object->getBasePosition().getDistanceFrom(pos_f);
1488 if(distance_f >= radius_f)
1490 removed_objects.insert(id);
1498 ActiveObjectMessage ServerEnvironment::getActiveObjectMessage()
1500 if(m_active_object_messages.empty())
1501 return ActiveObjectMessage(0);
1503 ActiveObjectMessage message = m_active_object_messages.front();
1504 m_active_object_messages.pop_front();
1509 ************ Private methods *************
1512 u16 ServerEnvironment::addActiveObjectRaw(ServerActiveObject *object,
1513 bool set_changed, u32 dtime_s)
1516 if(object->getId() == 0){
1517 u16 new_id = getFreeServerActiveObjectId(m_active_objects);
1520 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1521 <<"no free ids available"<<std::endl;
1522 if(object->environmentDeletes())
1526 object->setId(new_id);
1529 verbosestream<<"ServerEnvironment::addActiveObjectRaw(): "
1530 <<"supplied with id "<<object->getId()<<std::endl;
1532 if(isFreeServerActiveObjectId(object->getId(), m_active_objects) == false)
1534 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1535 <<"id is not free ("<<object->getId()<<")"<<std::endl;
1536 if(object->environmentDeletes())
1540 /*infostream<<"ServerEnvironment::addActiveObjectRaw(): "
1541 <<"added (id="<<object->getId()<<")"<<std::endl;*/
1543 m_active_objects[object->getId()] = object;
1545 verbosestream<<"ServerEnvironment::addActiveObjectRaw(): "
1546 <<"Added id="<<object->getId()<<"; there are now "
1547 <<m_active_objects.size()<<" active objects."
1550 // Register reference in scripting api (must be done before post-init)
1551 m_script->addObjectReference(object);
1552 // Post-initialize object
1553 object->addedToEnvironment(dtime_s);
1555 // Add static data to block
1556 if(object->isStaticAllowed())
1558 // Add static object to active static list of the block
1559 v3f objectpos = object->getBasePosition();
1560 std::string staticdata = object->getStaticData();
1561 StaticObject s_obj(object->getType(), objectpos, staticdata);
1562 // Add to the block where the object is located in
1563 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1564 MapBlock *block = m_map->emergeBlock(blockpos);
1566 block->m_static_objects.m_active[object->getId()] = s_obj;
1567 object->m_static_exists = true;
1568 object->m_static_block = blockpos;
1571 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1572 "addActiveObjectRaw");
1574 v3s16 p = floatToInt(objectpos, BS);
1575 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1576 <<"could not emerge block for storing id="<<object->getId()
1577 <<" statically (pos="<<PP(p)<<")"<<std::endl;
1581 return object->getId();
1585 Remove objects that satisfy (m_removed && m_known_by_count==0)
1587 void ServerEnvironment::removeRemovedObjects()
1589 std::list<u16> objects_to_remove;
1590 for(std::map<u16, ServerActiveObject*>::iterator
1591 i = m_active_objects.begin();
1592 i != m_active_objects.end(); ++i)
1595 ServerActiveObject* obj = i->second;
1596 // This shouldn't happen but check it
1599 infostream<<"NULL object found in ServerEnvironment"
1600 <<" while finding removed objects. id="<<id<<std::endl;
1601 // Id to be removed from m_active_objects
1602 objects_to_remove.push_back(id);
1607 We will delete objects that are marked as removed or thatare
1608 waiting for deletion after deactivation
1610 if(obj->m_removed == false && obj->m_pending_deactivation == false)
1614 Delete static data from block if is marked as removed
1616 if(obj->m_static_exists && obj->m_removed)
1618 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
1620 block->m_static_objects.remove(id);
1621 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1622 "removeRemovedObjects/remove");
1623 obj->m_static_exists = false;
1625 infostream<<"Failed to emerge block from which an object to "
1626 <<"be removed was loaded from. id="<<id<<std::endl;
1630 // If m_known_by_count > 0, don't actually remove. On some future
1631 // invocation this will be 0, which is when removal will continue.
1632 if(obj->m_known_by_count > 0)
1636 Move static data from active to stored if not marked as removed
1638 if(obj->m_static_exists && !obj->m_removed){
1639 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
1641 std::map<u16, StaticObject>::iterator i =
1642 block->m_static_objects.m_active.find(id);
1643 if(i != block->m_static_objects.m_active.end()){
1644 block->m_static_objects.m_stored.push_back(i->second);
1645 block->m_static_objects.m_active.erase(id);
1646 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1647 "removeRemovedObjects/deactivate");
1650 infostream<<"Failed to emerge block from which an object to "
1651 <<"be deactivated was loaded from. id="<<id<<std::endl;
1655 // Tell the object about removal
1656 obj->removingFromEnvironment();
1657 // Deregister in scripting api
1658 m_script->removeObjectReference(obj);
1661 if(obj->environmentDeletes())
1663 // Id to be removed from m_active_objects
1664 objects_to_remove.push_back(id);
1666 // Remove references from m_active_objects
1667 for(std::list<u16>::iterator i = objects_to_remove.begin();
1668 i != objects_to_remove.end(); ++i)
1670 m_active_objects.erase(*i);
1674 static void print_hexdump(std::ostream &o, const std::string &data)
1676 const int linelength = 16;
1677 for(int l=0; ; l++){
1678 int i0 = linelength * l;
1679 bool at_end = false;
1680 int thislinelength = linelength;
1681 if(i0 + thislinelength > (int)data.size()){
1682 thislinelength = data.size() - i0;
1685 for(int di=0; di<linelength; di++){
1688 if(di<thislinelength)
1689 snprintf(buf, 4, "%.2x ", data[i]);
1691 snprintf(buf, 4, " ");
1695 for(int di=0; di<thislinelength; di++){
1709 Convert stored objects from blocks near the players to active.
1711 void ServerEnvironment::activateObjects(MapBlock *block, u32 dtime_s)
1715 // Ignore if no stored objects (to not set changed flag)
1716 if(block->m_static_objects.m_stored.size() == 0)
1718 verbosestream<<"ServerEnvironment::activateObjects(): "
1719 <<"activating objects of block "<<PP(block->getPos())
1720 <<" ("<<block->m_static_objects.m_stored.size()
1721 <<" objects)"<<std::endl;
1722 bool large_amount = (block->m_static_objects.m_stored.size() > g_settings->getU16("max_objects_per_block"));
1724 errorstream<<"suspiciously large amount of objects detected: "
1725 <<block->m_static_objects.m_stored.size()<<" in "
1726 <<PP(block->getPos())
1727 <<"; removing all of them."<<std::endl;
1728 // Clear stored list
1729 block->m_static_objects.m_stored.clear();
1730 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1731 "stored list cleared in activateObjects due to "
1732 "large amount of objects");
1736 // Activate stored objects
1737 std::list<StaticObject> new_stored;
1738 for(std::list<StaticObject>::iterator
1739 i = block->m_static_objects.m_stored.begin();
1740 i != block->m_static_objects.m_stored.end(); ++i)
1742 /*infostream<<"Server: Creating an active object from "
1743 <<"static data"<<std::endl;*/
1744 StaticObject &s_obj = *i;
1745 // Create an active object from the data
1746 ServerActiveObject *obj = ServerActiveObject::create
1747 (s_obj.type, this, 0, s_obj.pos, s_obj.data);
1748 // If couldn't create object, store static data back.
1751 errorstream<<"ServerEnvironment::activateObjects(): "
1752 <<"failed to create active object from static object "
1753 <<"in block "<<PP(s_obj.pos/BS)
1754 <<" type="<<(int)s_obj.type<<" data:"<<std::endl;
1755 print_hexdump(verbosestream, s_obj.data);
1757 new_stored.push_back(s_obj);
1760 verbosestream<<"ServerEnvironment::activateObjects(): "
1761 <<"activated static object pos="<<PP(s_obj.pos/BS)
1762 <<" type="<<(int)s_obj.type<<std::endl;
1763 // This will also add the object to the active static list
1764 addActiveObjectRaw(obj, false, dtime_s);
1766 // Clear stored list
1767 block->m_static_objects.m_stored.clear();
1768 // Add leftover failed stuff to stored list
1769 for(std::list<StaticObject>::iterator
1770 i = new_stored.begin();
1771 i != new_stored.end(); ++i)
1773 StaticObject &s_obj = *i;
1774 block->m_static_objects.m_stored.push_back(s_obj);
1777 // Turn the active counterparts of activated objects not pending for
1779 for(std::map<u16, StaticObject>::iterator
1780 i = block->m_static_objects.m_active.begin();
1781 i != block->m_static_objects.m_active.end(); ++i)
1784 ServerActiveObject *object = getActiveObject(id);
1786 object->m_pending_deactivation = false;
1790 Note: Block hasn't really been modified here.
1791 The objects have just been activated and moved from the stored
1792 static list to the active static list.
1793 As such, the block is essentially the same.
1794 Thus, do not call block->raiseModified(MOD_STATE_WRITE_NEEDED).
1795 Otherwise there would be a huge amount of unnecessary I/O.
1800 Convert objects that are not standing inside active blocks to static.
1802 If m_known_by_count != 0, active object is not deleted, but static
1803 data is still updated.
1805 If force_delete is set, active object is deleted nevertheless. It
1806 shall only be set so in the destructor of the environment.
1808 If block wasn't generated (not in memory or on disk),
1810 void ServerEnvironment::deactivateFarObjects(bool force_delete)
1812 std::list<u16> objects_to_remove;
1813 for(std::map<u16, ServerActiveObject*>::iterator
1814 i = m_active_objects.begin();
1815 i != m_active_objects.end(); ++i)
1817 ServerActiveObject* obj = i->second;
1820 // Do not deactivate if static data creation not allowed
1821 if(!force_delete && !obj->isStaticAllowed())
1824 // If pending deactivation, let removeRemovedObjects() do it
1825 if(!force_delete && obj->m_pending_deactivation)
1829 v3f objectpos = obj->getBasePosition();
1831 // The block in which the object resides in
1832 v3s16 blockpos_o = getNodeBlockPos(floatToInt(objectpos, BS));
1834 // If object's static data is stored in a deactivated block and object
1835 // is actually located in an active block, re-save to the block in
1836 // which the object is actually located in.
1838 obj->m_static_exists &&
1839 !m_active_blocks.contains(obj->m_static_block) &&
1840 m_active_blocks.contains(blockpos_o))
1842 v3s16 old_static_block = obj->m_static_block;
1844 // Save to block where object is located
1845 MapBlock *block = m_map->emergeBlock(blockpos_o, false);
1847 errorstream<<"ServerEnvironment::deactivateFarObjects(): "
1848 <<"Could not save object id="<<id
1849 <<" to it's current block "<<PP(blockpos_o)
1853 std::string staticdata_new = obj->getStaticData();
1854 StaticObject s_obj(obj->getType(), objectpos, staticdata_new);
1855 block->m_static_objects.insert(id, s_obj);
1856 obj->m_static_block = blockpos_o;
1857 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1858 "deactivateFarObjects: Static data moved in");
1860 // Delete from block where object was located
1861 block = m_map->emergeBlock(old_static_block, false);
1863 errorstream<<"ServerEnvironment::deactivateFarObjects(): "
1864 <<"Could not delete object id="<<id
1865 <<" from it's previous block "<<PP(old_static_block)
1869 block->m_static_objects.remove(id);
1870 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1871 "deactivateFarObjects: Static data moved out");
1875 // If block is active, don't remove
1876 if(!force_delete && m_active_blocks.contains(blockpos_o))
1879 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
1880 <<"deactivating object id="<<id<<" on inactive block "
1881 <<PP(blockpos_o)<<std::endl;
1883 // If known by some client, don't immediately delete.
1884 bool pending_delete = (obj->m_known_by_count > 0 && !force_delete);
1887 Update the static data
1890 if(obj->isStaticAllowed())
1892 // Create new static object
1893 std::string staticdata_new = obj->getStaticData();
1894 StaticObject s_obj(obj->getType(), objectpos, staticdata_new);
1896 bool stays_in_same_block = false;
1897 bool data_changed = true;
1899 if(obj->m_static_exists){
1900 if(obj->m_static_block == blockpos_o)
1901 stays_in_same_block = true;
1903 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
1905 std::map<u16, StaticObject>::iterator n =
1906 block->m_static_objects.m_active.find(id);
1907 if(n != block->m_static_objects.m_active.end()){
1908 StaticObject static_old = n->second;
1910 float save_movem = obj->getMinimumSavedMovement();
1912 if(static_old.data == staticdata_new &&
1913 (static_old.pos - objectpos).getLength() < save_movem)
1914 data_changed = false;
1916 errorstream<<"ServerEnvironment::deactivateFarObjects(): "
1917 <<"id="<<id<<" m_static_exists=true but "
1918 <<"static data doesn't actually exist in "
1919 <<PP(obj->m_static_block)<<std::endl;
1923 bool shall_be_written = (!stays_in_same_block || data_changed);
1925 // Delete old static object
1926 if(obj->m_static_exists)
1928 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
1931 block->m_static_objects.remove(id);
1932 obj->m_static_exists = false;
1933 // Only mark block as modified if data changed considerably
1934 if(shall_be_written)
1935 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1936 "deactivateFarObjects: Static data "
1937 "changed considerably");
1941 // Add to the block where the object is located in
1942 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1943 // Get or generate the block
1944 MapBlock *block = NULL;
1946 block = m_map->emergeBlock(blockpos);
1947 } catch(InvalidPositionException &e){
1948 // Handled via NULL pointer
1949 // NOTE: emergeBlock's failure is usually determined by it
1950 // actually returning NULL
1955 if(block->m_static_objects.m_stored.size() >= g_settings->getU16("max_objects_per_block")){
1956 errorstream<<"ServerEnv: Trying to store id="<<obj->getId()
1957 <<" statically but block "<<PP(blockpos)
1958 <<" already contains "
1959 <<block->m_static_objects.m_stored.size()
1961 <<" Forcing delete."<<std::endl;
1962 force_delete = true;
1964 // If static counterpart already exists in target block,
1966 // This shouldn't happen because the object is removed from
1967 // the previous block before this according to
1968 // obj->m_static_block, but happens rarely for some unknown
1969 // reason. Unsuccessful attempts have been made to find
1971 if(id && block->m_static_objects.m_active.find(id) != block->m_static_objects.m_active.end()){
1972 infostream<<"ServerEnv: WARNING: Performing hack #83274"
1974 block->m_static_objects.remove(id);
1976 // Store static data
1977 u16 store_id = pending_delete ? id : 0;
1978 block->m_static_objects.insert(store_id, s_obj);
1980 // Only mark block as modified if data changed considerably
1981 if(shall_be_written)
1982 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1983 "deactivateFarObjects: Static data "
1984 "changed considerably");
1986 obj->m_static_exists = true;
1987 obj->m_static_block = block->getPos();
1992 v3s16 p = floatToInt(objectpos, BS);
1993 errorstream<<"ServerEnv: Could not find or generate "
1994 <<"a block for storing id="<<obj->getId()
1995 <<" statically (pos="<<PP(p)<<")"<<std::endl;
2002 If known by some client, set pending deactivation.
2003 Otherwise delete it immediately.
2006 if(pending_delete && !force_delete)
2008 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
2009 <<"object id="<<id<<" is known by clients"
2010 <<"; not deleting yet"<<std::endl;
2012 obj->m_pending_deactivation = true;
2016 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
2017 <<"object id="<<id<<" is not known by clients"
2018 <<"; deleting"<<std::endl;
2020 // Tell the object about removal
2021 obj->removingFromEnvironment();
2022 // Deregister in scripting api
2023 m_script->removeObjectReference(obj);
2025 // Delete active object
2026 if(obj->environmentDeletes())
2028 // Id to be removed from m_active_objects
2029 objects_to_remove.push_back(id);
2032 // Remove references from m_active_objects
2033 for(std::list<u16>::iterator i = objects_to_remove.begin();
2034 i != objects_to_remove.end(); ++i)
2036 m_active_objects.erase(*i);
2043 #include "clientsimpleobject.h"
2049 ClientEnvironment::ClientEnvironment(ClientMap *map, scene::ISceneManager *smgr,
2050 ITextureSource *texturesource, IGameDef *gamedef,
2051 IrrlichtDevice *irr):
2054 m_texturesource(texturesource),
2060 ClientEnvironment::~ClientEnvironment()
2062 // delete active objects
2063 for(std::map<u16, ClientActiveObject*>::iterator
2064 i = m_active_objects.begin();
2065 i != m_active_objects.end(); ++i)
2070 for(std::list<ClientSimpleObject*>::iterator
2071 i = m_simple_objects.begin(); i != m_simple_objects.end(); ++i)
2080 Map & ClientEnvironment::getMap()
2085 ClientMap & ClientEnvironment::getClientMap()
2090 void ClientEnvironment::addPlayer(Player *player)
2092 DSTACK(__FUNCTION_NAME);
2094 It is a failure if player is local and there already is a local
2097 assert(!(player->isLocal() == true && getLocalPlayer() != NULL));
2099 Environment::addPlayer(player);
2102 LocalPlayer * ClientEnvironment::getLocalPlayer()
2104 for(std::list<Player*>::iterator i = m_players.begin();
2105 i != m_players.end(); ++i)
2107 Player *player = *i;
2108 if(player->isLocal())
2109 return (LocalPlayer*)player;
2114 void ClientEnvironment::step(float dtime)
2116 DSTACK(__FUNCTION_NAME);
2118 /* Step time of day */
2119 stepTimeOfDay(dtime);
2121 // Get some settings
2122 bool fly_allowed = m_gamedef->checkLocalPrivilege("fly");
2123 bool free_move = fly_allowed && g_settings->getBool("free_move");
2126 LocalPlayer *lplayer = getLocalPlayer();
2128 // collision info queue
2129 std::list<CollisionInfo> player_collisions;
2132 Get the speed the player is going
2134 bool is_climbing = lplayer->is_climbing;
2136 f32 player_speed = lplayer->getSpeed().getLength();
2139 Maximum position increment
2141 //f32 position_max_increment = 0.05*BS;
2142 f32 position_max_increment = 0.1*BS;
2144 // Maximum time increment (for collision detection etc)
2145 // time = distance / speed
2146 f32 dtime_max_increment = 1;
2147 if(player_speed > 0.001)
2148 dtime_max_increment = position_max_increment / player_speed;
2150 // Maximum time increment is 10ms or lower
2151 if(dtime_max_increment > 0.01)
2152 dtime_max_increment = 0.01;
2154 // Don't allow overly huge dtime
2158 f32 dtime_downcount = dtime;
2161 Stuff that has a maximum time increment
2170 if(dtime_downcount > dtime_max_increment)
2172 dtime_part = dtime_max_increment;
2173 dtime_downcount -= dtime_part;
2177 dtime_part = dtime_downcount;
2179 Setting this to 0 (no -=dtime_part) disables an infinite loop
2180 when dtime_part is so small that dtime_downcount -= dtime_part
2183 dtime_downcount = 0;
2192 if(free_move == false && is_climbing == false)
2195 v3f speed = lplayer->getSpeed();
2196 if(lplayer->in_liquid == false)
2197 speed.Y -= lplayer->movement_gravity * lplayer->physics_override_gravity * dtime_part * 2;
2199 // Liquid floating / sinking
2200 if(lplayer->in_liquid && !lplayer->swimming_vertical)
2201 speed.Y -= lplayer->movement_liquid_sink * dtime_part * 2;
2203 // Liquid resistance
2204 if(lplayer->in_liquid_stable || lplayer->in_liquid)
2206 // How much the node's viscosity blocks movement, ranges between 0 and 1
2207 // Should match the scale at which viscosity increase affects other liquid attributes
2208 const f32 viscosity_factor = 0.3;
2210 v3f d_wanted = -speed / lplayer->movement_liquid_fluidity;
2211 f32 dl = d_wanted.getLength();
2212 if(dl > lplayer->movement_liquid_fluidity_smooth)
2213 dl = lplayer->movement_liquid_fluidity_smooth;
2214 dl *= (lplayer->liquid_viscosity * viscosity_factor) + (1 - viscosity_factor);
2216 v3f d = d_wanted.normalize() * dl;
2220 if(speed.X > lplayer->movement_liquid_fluidity + lplayer->movement_liquid_fluidity_smooth) speed.X -= lplayer->movement_liquid_fluidity_smooth;
2221 if(speed.X < -lplayer->movement_liquid_fluidity - lplayer->movement_liquid_fluidity_smooth) speed.X += lplayer->movement_liquid_fluidity_smooth;
2222 if(speed.Y > lplayer->movement_liquid_fluidity + lplayer->movement_liquid_fluidity_smooth) speed.Y -= lplayer->movement_liquid_fluidity_smooth;
2223 if(speed.Y < -lplayer->movement_liquid_fluidity - lplayer->movement_liquid_fluidity_smooth) speed.Y += lplayer->movement_liquid_fluidity_smooth;
2224 if(speed.Z > lplayer->movement_liquid_fluidity + lplayer->movement_liquid_fluidity_smooth) speed.Z -= lplayer->movement_liquid_fluidity_smooth;
2225 if(speed.Z < -lplayer->movement_liquid_fluidity - lplayer->movement_liquid_fluidity_smooth) speed.Z += lplayer->movement_liquid_fluidity_smooth;
2229 lplayer->setSpeed(speed);
2234 This also does collision detection.
2236 lplayer->move(dtime_part, this, position_max_increment,
2237 &player_collisions);
2240 while(dtime_downcount > 0.001);
2242 //std::cout<<"Looped "<<loopcount<<" times."<<std::endl;
2244 for(std::list<CollisionInfo>::iterator
2245 i = player_collisions.begin();
2246 i != player_collisions.end(); ++i)
2248 CollisionInfo &info = *i;
2249 v3f speed_diff = info.new_speed - info.old_speed;;
2250 // Handle only fall damage
2251 // (because otherwise walking against something in fast_move kills you)
2252 if(speed_diff.Y < 0 || info.old_speed.Y >= 0)
2254 // Get rid of other components
2257 f32 pre_factor = 1; // 1 hp per node/s
2258 f32 tolerance = BS*14; // 5 without damage
2259 f32 post_factor = 1; // 1 hp per node/s
2260 if(info.type == COLLISION_NODE)
2262 const ContentFeatures &f = m_gamedef->ndef()->
2263 get(m_map->getNodeNoEx(info.node_p));
2264 // Determine fall damage multiplier
2265 int addp = itemgroup_get(f.groups, "fall_damage_add_percent");
2266 pre_factor = 1.0 + (float)addp/100.0;
2268 float speed = pre_factor * speed_diff.getLength();
2269 if(speed > tolerance)
2271 f32 damage_f = (speed - tolerance)/BS * post_factor;
2272 u16 damage = (u16)(damage_f+0.5);
2274 damageLocalPlayer(damage, true);
2275 MtEvent *e = new SimpleTriggerEvent("PlayerFallingDamage");
2276 m_gamedef->event()->put(e);
2282 A quick draft of lava damage
2284 if(m_lava_hurt_interval.step(dtime, 1.0))
2286 v3f pf = lplayer->getPosition();
2288 // Feet, middle and head
2289 v3s16 p1 = floatToInt(pf + v3f(0, BS*0.1, 0), BS);
2290 MapNode n1 = m_map->getNodeNoEx(p1);
2291 v3s16 p2 = floatToInt(pf + v3f(0, BS*0.8, 0), BS);
2292 MapNode n2 = m_map->getNodeNoEx(p2);
2293 v3s16 p3 = floatToInt(pf + v3f(0, BS*1.6, 0), BS);
2294 MapNode n3 = m_map->getNodeNoEx(p3);
2296 u32 damage_per_second = 0;
2297 damage_per_second = MYMAX(damage_per_second,
2298 m_gamedef->ndef()->get(n1).damage_per_second);
2299 damage_per_second = MYMAX(damage_per_second,
2300 m_gamedef->ndef()->get(n2).damage_per_second);
2301 damage_per_second = MYMAX(damage_per_second,
2302 m_gamedef->ndef()->get(n3).damage_per_second);
2304 if(damage_per_second != 0)
2306 damageLocalPlayer(damage_per_second, true);
2313 if(m_drowning_interval.step(dtime, 2.0))
2315 v3f pf = lplayer->getPosition();
2318 v3s16 p = floatToInt(pf + v3f(0, BS*1.6, 0), BS);
2319 MapNode n = m_map->getNodeNoEx(p);
2320 ContentFeatures c = m_gamedef->ndef()->get(n);
2321 u8 drowning_damage = c.drowning;
2322 if(drowning_damage > 0 && lplayer->hp > 0){
2323 u16 breath = lplayer->getBreath();
2330 lplayer->setBreath(breath);
2331 updateLocalPlayerBreath(breath);
2334 if(lplayer->getBreath() == 0 && drowning_damage > 0){
2335 damageLocalPlayer(drowning_damage, true);
2338 if(m_breathing_interval.step(dtime, 0.5))
2340 v3f pf = lplayer->getPosition();
2343 v3s16 p = floatToInt(pf + v3f(0, BS*1.6, 0), BS);
2344 MapNode n = m_map->getNodeNoEx(p);
2345 ContentFeatures c = m_gamedef->ndef()->get(n);
2347 lplayer->setBreath(11);
2349 else if(c.drowning == 0){
2350 u16 breath = lplayer->getBreath();
2353 lplayer->setBreath(breath);
2354 updateLocalPlayerBreath(breath);
2360 Stuff that can be done in an arbitarily large dtime
2362 for(std::list<Player*>::iterator i = m_players.begin();
2363 i != m_players.end(); ++i)
2365 Player *player = *i;
2368 Handle non-local players
2370 if(player->isLocal() == false)
2373 player->move(dtime, *m_map, 100*BS);
2377 // Update lighting on all players on client
2381 v3s16 p = player->getLightPosition();
2382 MapNode n = m_map->getNode(p);
2383 light = n.getLightBlendF1((float)getDayNightRatio()/1000, m_gamedef->ndef());
2385 catch(InvalidPositionException &e){
2386 light = blend_light_f1((float)getDayNightRatio()/1000, LIGHT_SUN, 0);
2388 player->light = light;
2392 Step active objects and update lighting of them
2395 g_profiler->avg("CEnv: num of objects", m_active_objects.size());
2396 bool update_lighting = m_active_object_light_update_interval.step(dtime, 0.21);
2397 for(std::map<u16, ClientActiveObject*>::iterator
2398 i = m_active_objects.begin();
2399 i != m_active_objects.end(); ++i)
2401 ClientActiveObject* obj = i->second;
2403 obj->step(dtime, this);
2411 v3s16 p = obj->getLightPosition();
2412 MapNode n = m_map->getNode(p);
2413 light = n.getLightBlend(getDayNightRatio(), m_gamedef->ndef());
2415 catch(InvalidPositionException &e){
2416 light = blend_light(getDayNightRatio(), LIGHT_SUN, 0);
2418 obj->updateLight(light);
2423 Step and handle simple objects
2425 g_profiler->avg("CEnv: num of simple objects", m_simple_objects.size());
2426 for(std::list<ClientSimpleObject*>::iterator
2427 i = m_simple_objects.begin(); i != m_simple_objects.end();)
2429 ClientSimpleObject *simple = *i;
2430 std::list<ClientSimpleObject*>::iterator cur = i;
2432 simple->step(dtime);
2433 if(simple->m_to_be_removed){
2435 m_simple_objects.erase(cur);
2440 void ClientEnvironment::addSimpleObject(ClientSimpleObject *simple)
2442 m_simple_objects.push_back(simple);
2445 ClientActiveObject* ClientEnvironment::getActiveObject(u16 id)
2447 std::map<u16, ClientActiveObject*>::iterator n;
2448 n = m_active_objects.find(id);
2449 if(n == m_active_objects.end())
2454 bool isFreeClientActiveObjectId(u16 id,
2455 std::map<u16, ClientActiveObject*> &objects)
2460 return objects.find(id) == objects.end();
2463 u16 getFreeClientActiveObjectId(
2464 std::map<u16, ClientActiveObject*> &objects)
2466 //try to reuse id's as late as possible
2467 static u16 last_used_id = 0;
2468 u16 startid = last_used_id;
2472 if(isFreeClientActiveObjectId(last_used_id, objects))
2473 return last_used_id;
2475 if(last_used_id == startid)
2480 u16 ClientEnvironment::addActiveObject(ClientActiveObject *object)
2483 if(object->getId() == 0)
2485 u16 new_id = getFreeClientActiveObjectId(m_active_objects);
2488 infostream<<"ClientEnvironment::addActiveObject(): "
2489 <<"no free ids available"<<std::endl;
2493 object->setId(new_id);
2495 if(isFreeClientActiveObjectId(object->getId(), m_active_objects) == false)
2497 infostream<<"ClientEnvironment::addActiveObject(): "
2498 <<"id is not free ("<<object->getId()<<")"<<std::endl;
2502 infostream<<"ClientEnvironment::addActiveObject(): "
2503 <<"added (id="<<object->getId()<<")"<<std::endl;
2504 m_active_objects[object->getId()] = object;
2505 object->addToScene(m_smgr, m_texturesource, m_irr);
2506 { // Update lighting immediately
2510 v3s16 p = object->getLightPosition();
2511 MapNode n = m_map->getNode(p);
2512 light = n.getLightBlend(getDayNightRatio(), m_gamedef->ndef());
2514 catch(InvalidPositionException &e){
2515 light = blend_light(getDayNightRatio(), LIGHT_SUN, 0);
2517 object->updateLight(light);
2519 return object->getId();
2522 void ClientEnvironment::addActiveObject(u16 id, u8 type,
2523 const std::string &init_data)
2525 ClientActiveObject* obj =
2526 ClientActiveObject::create(type, m_gamedef, this);
2529 infostream<<"ClientEnvironment::addActiveObject(): "
2530 <<"id="<<id<<" type="<<type<<": Couldn't create object"
2539 obj->initialize(init_data);
2541 catch(SerializationError &e)
2543 errorstream<<"ClientEnvironment::addActiveObject():"
2544 <<" id="<<id<<" type="<<type
2545 <<": SerializationError in initialize(): "
2547 <<": init_data="<<serializeJsonString(init_data)
2551 addActiveObject(obj);
2554 void ClientEnvironment::removeActiveObject(u16 id)
2556 verbosestream<<"ClientEnvironment::removeActiveObject(): "
2557 <<"id="<<id<<std::endl;
2558 ClientActiveObject* obj = getActiveObject(id);
2561 infostream<<"ClientEnvironment::removeActiveObject(): "
2562 <<"id="<<id<<" not found"<<std::endl;
2565 obj->removeFromScene(true);
2567 m_active_objects.erase(id);
2570 void ClientEnvironment::processActiveObjectMessage(u16 id,
2571 const std::string &data)
2573 ClientActiveObject* obj = getActiveObject(id);
2576 infostream<<"ClientEnvironment::processActiveObjectMessage():"
2577 <<" got message for id="<<id<<", which doesn't exist."
2583 obj->processMessage(data);
2585 catch(SerializationError &e)
2587 errorstream<<"ClientEnvironment::processActiveObjectMessage():"
2588 <<" id="<<id<<" type="<<obj->getType()
2589 <<" SerializationError in processMessage(),"
2590 <<" message="<<serializeJsonString(data)
2596 Callbacks for activeobjects
2599 void ClientEnvironment::damageLocalPlayer(u8 damage, bool handle_hp)
2601 LocalPlayer *lplayer = getLocalPlayer();
2605 if(lplayer->hp > damage)
2606 lplayer->hp -= damage;
2611 ClientEnvEvent event;
2612 event.type = CEE_PLAYER_DAMAGE;
2613 event.player_damage.amount = damage;
2614 event.player_damage.send_to_server = handle_hp;
2615 m_client_event_queue.push_back(event);
2618 void ClientEnvironment::updateLocalPlayerBreath(u16 breath)
2620 ClientEnvEvent event;
2621 event.type = CEE_PLAYER_BREATH;
2622 event.player_breath.amount = breath;
2623 m_client_event_queue.push_back(event);
2627 Client likes to call these
2630 void ClientEnvironment::getActiveObjects(v3f origin, f32 max_d,
2631 std::vector<DistanceSortedActiveObject> &dest)
2633 for(std::map<u16, ClientActiveObject*>::iterator
2634 i = m_active_objects.begin();
2635 i != m_active_objects.end(); ++i)
2637 ClientActiveObject* obj = i->second;
2639 f32 d = (obj->getPosition() - origin).getLength();
2644 DistanceSortedActiveObject dso(obj, d);
2646 dest.push_back(dso);
2650 ClientEnvEvent ClientEnvironment::getClientEvent()
2652 ClientEnvEvent event;
2653 if(m_client_event_queue.empty())
2654 event.type = CEE_NONE;
2656 event = m_client_event_queue.front();
2657 m_client_event_queue.pop_front();
2662 #endif // #ifndef SERVER