3 Copyright (C) 2010-2013 celeron55, Perttu Ahola <celeron55@gmail.com>
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU Lesser General Public License as published by
7 the Free Software Foundation; either version 2.1 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU Lesser General Public License for more details.
15 You should have received a copy of the GNU Lesser General Public License along
16 with this program; if not, write to the Free Software Foundation, Inc.,
17 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20 #include "environment.h"
23 #include "collision.h"
24 #include "content_mapnode.h"
26 #include "serverobject.h"
27 #include "content_sao.h"
31 #include "scripting_game.h"
33 #include "nodemetadata.h"
34 #include "main.h" // For g_settings, g_profiler
37 #include "clientmap.h"
38 #include "localplayer.h"
41 #include "daynightratio.h"
44 #include "util/serialize.h"
46 #define PP(x) "("<<(x).X<<","<<(x).Y<<","<<(x).Z<<")"
48 Environment::Environment():
50 m_time_of_day_f(9000./24000),
51 m_time_of_day_speed(0),
56 Environment::~Environment()
59 for(std::list<Player*>::iterator i = m_players.begin();
60 i != m_players.end(); ++i)
66 void Environment::addPlayer(Player *player)
68 DSTACK(__FUNCTION_NAME);
70 Check that peer_ids are unique.
71 Also check that names are unique.
72 Exception: there can be multiple players with peer_id=0
74 // If peer id is non-zero, it has to be unique.
75 if(player->peer_id != 0)
76 assert(getPlayer(player->peer_id) == NULL);
77 // Name has to be unique.
78 assert(getPlayer(player->getName()) == NULL);
80 m_players.push_back(player);
83 void Environment::removePlayer(u16 peer_id)
85 DSTACK(__FUNCTION_NAME);
87 for(std::list<Player*>::iterator i = m_players.begin();
88 i != m_players.end(); ++i)
91 if(player->peer_id != peer_id)
96 // See if there is an another one
97 // (shouldn't be, but just to be sure)
102 Player * Environment::getPlayer(u16 peer_id)
104 for(std::list<Player*>::iterator i = m_players.begin();
105 i != m_players.end(); ++i)
108 if(player->peer_id == peer_id)
114 Player * Environment::getPlayer(const char *name)
116 for(std::list<Player*>::iterator i = m_players.begin();
117 i != m_players.end(); ++i)
120 if(strcmp(player->getName(), name) == 0)
126 Player * Environment::getRandomConnectedPlayer()
128 std::list<Player*> connected_players = getPlayers(true);
129 u32 chosen_one = myrand() % connected_players.size();
131 for(std::list<Player*>::iterator
132 i = connected_players.begin();
133 i != connected_players.end(); ++i)
145 Player * Environment::getNearestConnectedPlayer(v3f pos)
147 std::list<Player*> connected_players = getPlayers(true);
149 Player *nearest_player = NULL;
150 for(std::list<Player*>::iterator
151 i = connected_players.begin();
152 i != connected_players.end(); ++i)
155 f32 d = player->getPosition().getDistanceFrom(pos);
156 if(d < nearest_d || nearest_player == NULL)
159 nearest_player = player;
162 return nearest_player;
165 std::list<Player*> Environment::getPlayers()
170 std::list<Player*> Environment::getPlayers(bool ignore_disconnected)
172 std::list<Player*> newlist;
173 for(std::list<Player*>::iterator
174 i = m_players.begin();
175 i != m_players.end(); ++i)
179 if(ignore_disconnected)
181 // Ignore disconnected players
182 if(player->peer_id == 0)
186 newlist.push_back(player);
191 u32 Environment::getDayNightRatio()
193 bool smooth = g_settings->getBool("enable_shaders");
194 return time_to_daynight_ratio(m_time_of_day_f*24000, smooth);
197 void Environment::stepTimeOfDay(float dtime)
199 m_time_counter += dtime;
200 f32 speed = m_time_of_day_speed * 24000./(24.*3600);
201 u32 units = (u32)(m_time_counter*speed);
202 m_time_counter -= (f32)units / speed;
206 if(m_time_of_day + units >= 24000)
208 m_time_of_day = (m_time_of_day + units) % 24000;
210 m_time_of_day_f = (float)m_time_of_day / 24000.0;
213 m_time_of_day_f += m_time_of_day_speed/24/3600*dtime;
214 if(m_time_of_day_f > 1.0)
215 m_time_of_day_f -= 1.0;
216 if(m_time_of_day_f < 0.0)
217 m_time_of_day_f += 1.0;
225 ABMWithState::ABMWithState(ActiveBlockModifier *abm_):
229 // Initialize timer to random value to spread processing
230 float itv = abm->getTriggerInterval();
231 itv = MYMAX(0.001, itv); // No less than 1ms
232 int minval = MYMAX(-0.51*itv, -60); // Clamp to
233 int maxval = MYMIN(0.51*itv, 60); // +-60 seconds
234 timer = myrand_range(minval, maxval);
241 void fillRadiusBlock(v3s16 p0, s16 r, std::set<v3s16> &list)
244 for(p.X=p0.X-r; p.X<=p0.X+r; p.X++)
245 for(p.Y=p0.Y-r; p.Y<=p0.Y+r; p.Y++)
246 for(p.Z=p0.Z-r; p.Z<=p0.Z+r; p.Z++)
253 void ActiveBlockList::update(std::list<v3s16> &active_positions,
255 std::set<v3s16> &blocks_removed,
256 std::set<v3s16> &blocks_added)
261 std::set<v3s16> newlist;
262 for(std::list<v3s16>::iterator i = active_positions.begin();
263 i != active_positions.end(); ++i)
265 fillRadiusBlock(*i, radius, newlist);
269 Find out which blocks on the old list are not on the new list
271 // Go through old list
272 for(std::set<v3s16>::iterator i = m_list.begin();
273 i != m_list.end(); ++i)
276 // If not on new list, it's been removed
277 if(newlist.find(p) == newlist.end())
278 blocks_removed.insert(p);
282 Find out which blocks on the new list are not on the old list
284 // Go through new list
285 for(std::set<v3s16>::iterator i = newlist.begin();
286 i != newlist.end(); ++i)
289 // If not on old list, it's been added
290 if(m_list.find(p) == m_list.end())
291 blocks_added.insert(p);
298 for(std::set<v3s16>::iterator i = newlist.begin();
299 i != newlist.end(); ++i)
310 ServerEnvironment::ServerEnvironment(ServerMap *map,
311 GameScripting *scriptIface,
312 IGameDef *gamedef, IBackgroundBlockEmerger *emerger):
314 m_script(scriptIface),
317 m_random_spawn_timer(3),
318 m_send_recommended_timer(0),
319 m_active_block_interval_overload_skip(0),
321 m_game_time_fraction_counter(0),
322 m_recommended_send_interval(0.1),
323 m_max_lag_estimate(0.1)
325 m_use_weather = g_settings->getBool("weather");
328 ServerEnvironment::~ServerEnvironment()
330 // Clear active block list.
331 // This makes the next one delete all active objects.
332 m_active_blocks.clear();
334 // Convert all objects to static and delete the active objects
335 deactivateFarObjects(true);
340 // Delete ActiveBlockModifiers
341 for(std::list<ABMWithState>::iterator
342 i = m_abms.begin(); i != m_abms.end(); ++i){
347 Map & ServerEnvironment::getMap()
352 ServerMap & ServerEnvironment::getServerMap()
357 bool ServerEnvironment::line_of_sight(v3f pos1, v3f pos2, float stepsize)
359 float distance = pos1.getDistanceFrom(pos2);
361 //calculate normalized direction vector
362 v3f normalized_vector = v3f((pos2.X - pos1.X)/distance,
363 (pos2.Y - pos1.Y)/distance,
364 (pos2.Z - pos1.Z)/distance);
366 //find out if there's a node on path between pos1 and pos2
367 for (float i = 1; i < distance; i += stepsize) {
368 v3s16 pos = floatToInt(v3f(normalized_vector.X * i,
369 normalized_vector.Y * i,
370 normalized_vector.Z * i) +pos1,BS);
372 MapNode n = getMap().getNodeNoEx(pos);
374 if(n.param0 != CONTENT_AIR) {
381 void ServerEnvironment::serializePlayers(const std::string &savedir)
383 std::string players_path = savedir + "/players";
384 fs::CreateDir(players_path);
386 std::set<Player*> saved_players;
388 std::vector<fs::DirListNode> player_files = fs::GetDirListing(players_path);
389 for(u32 i=0; i<player_files.size(); i++)
391 if(player_files[i].dir || player_files[i].name[0] == '.')
394 // Full path to this file
395 std::string path = players_path + "/" + player_files[i].name;
397 //infostream<<"Checking player file "<<path<<std::endl;
399 // Load player to see what is its name
400 RemotePlayer testplayer(m_gamedef);
402 // Open file and deserialize
403 std::ifstream is(path.c_str(), std::ios_base::binary);
404 if(is.good() == false)
406 infostream<<"Failed to read "<<path<<std::endl;
409 testplayer.deSerialize(is, player_files[i].name);
412 //infostream<<"Loaded test player with name "<<testplayer.getName()<<std::endl;
414 // Search for the player
415 std::string playername = testplayer.getName();
416 Player *player = getPlayer(playername.c_str());
419 infostream<<"Didn't find matching player, ignoring file "<<path<<std::endl;
423 //infostream<<"Found matching player, overwriting."<<std::endl;
425 // OK, found. Save player there.
426 if(player->checkModified())
428 // Open file and serialize
429 std::ostringstream ss(std::ios_base::binary);
430 player->serialize(ss);
431 if(!fs::safeWriteToFile(path, ss.str()))
433 infostream<<"Failed to write "<<path<<std::endl;
436 saved_players.insert(player);
438 saved_players.insert(player);
442 for(std::list<Player*>::iterator i = m_players.begin();
443 i != m_players.end(); ++i)
446 if(saved_players.find(player) != saved_players.end())
448 /*infostream<<"Player "<<player->getName()
449 <<" was already saved."<<std::endl;*/
452 std::string playername = player->getName();
453 // Don't save unnamed player
456 //infostream<<"Not saving unnamed player."<<std::endl;
462 if(string_allowed(playername, PLAYERNAME_ALLOWED_CHARS) == false)
463 playername = "player";
464 std::string path = players_path + "/" + playername;
466 for(u32 i=0; i<1000; i++)
468 if(fs::PathExists(path) == false)
473 path = players_path + "/" + playername + itos(i);
477 infostream<<"Didn't find free file for player"<<std::endl;
482 /*infostream<<"Saving player "<<player->getName()<<" to "
484 // Open file and serialize
485 std::ostringstream ss(std::ios_base::binary);
486 player->serialize(ss);
487 if(!fs::safeWriteToFile(path, ss.str()))
489 infostream<<"Failed to write "<<path<<std::endl;
492 saved_players.insert(player);
496 //infostream<<"Saved "<<saved_players.size()<<" players."<<std::endl;
499 void ServerEnvironment::deSerializePlayers(const std::string &savedir)
501 std::string players_path = savedir + "/players";
503 std::vector<fs::DirListNode> player_files = fs::GetDirListing(players_path);
504 for(u32 i=0; i<player_files.size(); i++)
506 if(player_files[i].dir)
509 // Full path to this file
510 std::string path = players_path + "/" + player_files[i].name;
512 //infostream<<"Checking player file "<<path<<std::endl;
514 // Load player to see what is its name
515 RemotePlayer testplayer(m_gamedef);
517 // Open file and deserialize
518 std::ifstream is(path.c_str(), std::ios_base::binary);
519 if(is.good() == false)
521 infostream<<"Failed to read "<<path<<std::endl;
524 testplayer.deSerialize(is, player_files[i].name);
527 if(!string_allowed(testplayer.getName(), PLAYERNAME_ALLOWED_CHARS))
529 infostream<<"Not loading player with invalid name: "
530 <<testplayer.getName()<<std::endl;
533 /*infostream<<"Loaded test player with name "<<testplayer.getName()
536 // Search for the player
537 std::string playername = testplayer.getName();
538 Player *player = getPlayer(playername.c_str());
539 bool newplayer = false;
542 //infostream<<"Is a new player"<<std::endl;
543 player = new RemotePlayer(m_gamedef);
549 verbosestream<<"Reading player "<<testplayer.getName()<<" from "
551 // Open file and deserialize
552 std::ifstream is(path.c_str(), std::ios_base::binary);
553 if(is.good() == false)
555 infostream<<"Failed to read "<<path<<std::endl;
558 player->deSerialize(is, player_files[i].name);
568 void ServerEnvironment::saveMeta(const std::string &savedir)
570 std::string path = savedir + "/env_meta.txt";
572 // Open file and serialize
573 std::ostringstream ss(std::ios_base::binary);
576 args.setU64("game_time", m_game_time);
577 args.setU64("time_of_day", getTimeOfDay());
581 if(!fs::safeWriteToFile(path, ss.str()))
583 infostream<<"ServerEnvironment::saveMeta(): Failed to write "
585 throw SerializationError("Couldn't save env meta");
589 void ServerEnvironment::loadMeta(const std::string &savedir)
591 std::string path = savedir + "/env_meta.txt";
593 // Open file and deserialize
594 std::ifstream is(path.c_str(), std::ios_base::binary);
595 if(is.good() == false)
597 infostream<<"ServerEnvironment::loadMeta(): Failed to open "
599 throw SerializationError("Couldn't load env meta");
607 throw SerializationError
608 ("ServerEnvironment::loadMeta(): EnvArgsEnd not found");
610 std::getline(is, line);
611 std::string trimmedline = trim(line);
612 if(trimmedline == "EnvArgsEnd")
614 args.parseConfigLine(line);
618 m_game_time = args.getU64("game_time");
619 }catch(SettingNotFoundException &e){
620 // Getting this is crucial, otherwise timestamps are useless
621 throw SerializationError("Couldn't load env meta game_time");
625 m_time_of_day = args.getU64("time_of_day");
626 }catch(SettingNotFoundException &e){
627 // This is not as important
628 m_time_of_day = 9000;
634 ActiveBlockModifier *abm;
636 std::set<content_t> required_neighbors;
642 ServerEnvironment *m_env;
643 std::map<content_t, std::list<ActiveABM> > m_aabms;
645 ABMHandler(std::list<ABMWithState> &abms,
646 float dtime_s, ServerEnvironment *env,
652 INodeDefManager *ndef = env->getGameDef()->ndef();
653 for(std::list<ABMWithState>::iterator
654 i = abms.begin(); i != abms.end(); ++i){
655 ActiveBlockModifier *abm = i->abm;
656 float trigger_interval = abm->getTriggerInterval();
657 if(trigger_interval < 0.001)
658 trigger_interval = 0.001;
659 float actual_interval = dtime_s;
662 if(i->timer < trigger_interval)
664 i->timer -= trigger_interval;
665 actual_interval = trigger_interval;
667 float intervals = actual_interval / trigger_interval;
670 float chance = abm->getTriggerChance();
675 aabm.chance = chance / intervals;
679 std::set<std::string> required_neighbors_s
680 = abm->getRequiredNeighbors();
681 for(std::set<std::string>::iterator
682 i = required_neighbors_s.begin();
683 i != required_neighbors_s.end(); i++)
685 ndef->getIds(*i, aabm.required_neighbors);
688 std::set<std::string> contents_s = abm->getTriggerContents();
689 for(std::set<std::string>::iterator
690 i = contents_s.begin(); i != contents_s.end(); i++)
692 std::set<content_t> ids;
693 ndef->getIds(*i, ids);
694 for(std::set<content_t>::const_iterator k = ids.begin();
698 std::map<content_t, std::list<ActiveABM> >::iterator j;
700 if(j == m_aabms.end()){
701 std::list<ActiveABM> aabmlist;
702 m_aabms[c] = aabmlist;
705 j->second.push_back(aabm);
710 void apply(MapBlock *block)
715 ServerMap *map = &m_env->getServerMap();
718 for(p0.X=0; p0.X<MAP_BLOCKSIZE; p0.X++)
719 for(p0.Y=0; p0.Y<MAP_BLOCKSIZE; p0.Y++)
720 for(p0.Z=0; p0.Z<MAP_BLOCKSIZE; p0.Z++)
722 MapNode n = block->getNodeNoEx(p0);
723 content_t c = n.getContent();
724 v3s16 p = p0 + block->getPosRelative();
726 std::map<content_t, std::list<ActiveABM> >::iterator j;
728 if(j == m_aabms.end())
731 for(std::list<ActiveABM>::iterator
732 i = j->second.begin(); i != j->second.end(); i++)
734 if(myrand() % i->chance != 0)
738 if(!i->required_neighbors.empty())
741 for(p1.X = p.X-1; p1.X <= p.X+1; p1.X++)
742 for(p1.Y = p.Y-1; p1.Y <= p.Y+1; p1.Y++)
743 for(p1.Z = p.Z-1; p1.Z <= p.Z+1; p1.Z++)
747 MapNode n = map->getNodeNoEx(p1);
748 content_t c = n.getContent();
749 std::set<content_t>::const_iterator k;
750 k = i->required_neighbors.find(c);
751 if(k != i->required_neighbors.end()){
755 // No required neighbor found
760 // Find out how many objects the block contains
761 u32 active_object_count = block->m_static_objects.m_active.size();
762 // Find out how many objects this and all the neighbors contain
763 u32 active_object_count_wider = 0;
764 u32 wider_unknown_count = 0;
765 for(s16 x=-1; x<=1; x++)
766 for(s16 y=-1; y<=1; y++)
767 for(s16 z=-1; z<=1; z++)
769 MapBlock *block2 = map->getBlockNoCreateNoEx(
770 block->getPos() + v3s16(x,y,z));
772 wider_unknown_count = 0;
775 active_object_count_wider +=
776 block2->m_static_objects.m_active.size()
777 + block2->m_static_objects.m_stored.size();
780 u32 wider_known_count = 3*3*3 - wider_unknown_count;
781 active_object_count_wider += wider_unknown_count * active_object_count_wider / wider_known_count;
783 // Call all the trigger variations
784 i->abm->trigger(m_env, p, n);
785 i->abm->trigger(m_env, p, n,
786 active_object_count, active_object_count_wider);
792 void ServerEnvironment::activateBlock(MapBlock *block, u32 additional_dtime)
794 // Get time difference
796 u32 stamp = block->getTimestamp();
797 if(m_game_time > stamp && stamp != BLOCK_TIMESTAMP_UNDEFINED)
798 dtime_s = m_game_time - block->getTimestamp();
799 dtime_s += additional_dtime;
801 /*infostream<<"ServerEnvironment::activateBlock(): block timestamp: "
802 <<stamp<<", game time: "<<m_game_time<<std::endl;*/
804 // Set current time as timestamp
805 block->setTimestampNoChangedFlag(m_game_time);
807 /*infostream<<"ServerEnvironment::activateBlock(): block is "
808 <<dtime_s<<" seconds old."<<std::endl;*/
810 // Activate stored objects
811 activateObjects(block, dtime_s);
814 std::map<v3s16, NodeTimer> elapsed_timers =
815 block->m_node_timers.step((float)dtime_s);
816 if(!elapsed_timers.empty()){
818 for(std::map<v3s16, NodeTimer>::iterator
819 i = elapsed_timers.begin();
820 i != elapsed_timers.end(); i++){
821 n = block->getNodeNoEx(i->first);
822 v3s16 p = i->first + block->getPosRelative();
823 if(m_script->node_on_timer(p,n,i->second.elapsed))
824 block->setNodeTimer(i->first,NodeTimer(i->second.timeout,0));
828 /* Handle ActiveBlockModifiers */
829 ABMHandler abmhandler(m_abms, dtime_s, this, false);
830 abmhandler.apply(block);
833 void ServerEnvironment::addActiveBlockModifier(ActiveBlockModifier *abm)
835 m_abms.push_back(ABMWithState(abm));
838 bool ServerEnvironment::setNode(v3s16 p, const MapNode &n)
840 INodeDefManager *ndef = m_gamedef->ndef();
841 MapNode n_old = m_map->getNodeNoEx(p);
843 if(ndef->get(n_old).has_on_destruct)
844 m_script->node_on_destruct(p, n_old);
846 bool succeeded = m_map->addNodeWithEvent(p, n);
849 // Call post-destructor
850 if(ndef->get(n_old).has_after_destruct)
851 m_script->node_after_destruct(p, n_old);
853 if(ndef->get(n).has_on_construct)
854 m_script->node_on_construct(p, n);
858 bool ServerEnvironment::removeNode(v3s16 p)
860 INodeDefManager *ndef = m_gamedef->ndef();
861 MapNode n_old = m_map->getNodeNoEx(p);
863 if(ndef->get(n_old).has_on_destruct)
864 m_script->node_on_destruct(p, n_old);
866 // This is slightly optimized compared to addNodeWithEvent(air)
867 bool succeeded = m_map->removeNodeWithEvent(p);
870 // Call post-destructor
871 if(ndef->get(n_old).has_after_destruct)
872 m_script->node_after_destruct(p, n_old);
873 // Air doesn't require constructor
877 std::set<u16> ServerEnvironment::getObjectsInsideRadius(v3f pos, float radius)
879 std::set<u16> objects;
880 for(std::map<u16, ServerActiveObject*>::iterator
881 i = m_active_objects.begin();
882 i != m_active_objects.end(); ++i)
884 ServerActiveObject* obj = i->second;
886 v3f objectpos = obj->getBasePosition();
887 if(objectpos.getDistanceFrom(pos) > radius)
894 void ServerEnvironment::clearAllObjects()
896 infostream<<"ServerEnvironment::clearAllObjects(): "
897 <<"Removing all active objects"<<std::endl;
898 std::list<u16> objects_to_remove;
899 for(std::map<u16, ServerActiveObject*>::iterator
900 i = m_active_objects.begin();
901 i != m_active_objects.end(); ++i)
903 ServerActiveObject* obj = i->second;
904 if(obj->getType() == ACTIVEOBJECT_TYPE_PLAYER)
907 // Delete static object if block is loaded
908 if(obj->m_static_exists){
909 MapBlock *block = m_map->getBlockNoCreateNoEx(obj->m_static_block);
911 block->m_static_objects.remove(id);
912 block->raiseModified(MOD_STATE_WRITE_NEEDED,
914 obj->m_static_exists = false;
917 // If known by some client, don't delete immediately
918 if(obj->m_known_by_count > 0){
919 obj->m_pending_deactivation = true;
920 obj->m_removed = true;
924 // Tell the object about removal
925 obj->removingFromEnvironment();
926 // Deregister in scripting api
927 m_script->removeObjectReference(obj);
929 // Delete active object
930 if(obj->environmentDeletes())
932 // Id to be removed from m_active_objects
933 objects_to_remove.push_back(id);
935 // Remove references from m_active_objects
936 for(std::list<u16>::iterator i = objects_to_remove.begin();
937 i != objects_to_remove.end(); ++i)
939 m_active_objects.erase(*i);
942 // Get list of loaded blocks
943 std::list<v3s16> loaded_blocks;
944 infostream<<"ServerEnvironment::clearAllObjects(): "
945 <<"Listing all loaded blocks"<<std::endl;
946 m_map->listAllLoadedBlocks(loaded_blocks);
947 infostream<<"ServerEnvironment::clearAllObjects(): "
948 <<"Done listing all loaded blocks: "
949 <<loaded_blocks.size()<<std::endl;
951 // Get list of loadable blocks
952 std::list<v3s16> loadable_blocks;
953 infostream<<"ServerEnvironment::clearAllObjects(): "
954 <<"Listing all loadable blocks"<<std::endl;
955 m_map->listAllLoadableBlocks(loadable_blocks);
956 infostream<<"ServerEnvironment::clearAllObjects(): "
957 <<"Done listing all loadable blocks: "
958 <<loadable_blocks.size()
959 <<", now clearing"<<std::endl;
961 // Grab a reference on each loaded block to avoid unloading it
962 for(std::list<v3s16>::iterator i = loaded_blocks.begin();
963 i != loaded_blocks.end(); ++i)
966 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
971 // Remove objects in all loadable blocks
972 u32 unload_interval = g_settings->getS32("max_clearobjects_extra_loaded_blocks");
973 unload_interval = MYMAX(unload_interval, 1);
974 u32 report_interval = loadable_blocks.size() / 10;
975 u32 num_blocks_checked = 0;
976 u32 num_blocks_cleared = 0;
977 u32 num_objs_cleared = 0;
978 for(std::list<v3s16>::iterator i = loadable_blocks.begin();
979 i != loadable_blocks.end(); ++i)
982 MapBlock *block = m_map->emergeBlock(p, false);
984 errorstream<<"ServerEnvironment::clearAllObjects(): "
985 <<"Failed to emerge block "<<PP(p)<<std::endl;
988 u32 num_stored = block->m_static_objects.m_stored.size();
989 u32 num_active = block->m_static_objects.m_active.size();
990 if(num_stored != 0 || num_active != 0){
991 block->m_static_objects.m_stored.clear();
992 block->m_static_objects.m_active.clear();
993 block->raiseModified(MOD_STATE_WRITE_NEEDED,
995 num_objs_cleared += num_stored + num_active;
996 num_blocks_cleared++;
998 num_blocks_checked++;
1000 if(num_blocks_checked % report_interval == 0){
1001 float percent = 100.0 * (float)num_blocks_checked /
1002 loadable_blocks.size();
1003 infostream<<"ServerEnvironment::clearAllObjects(): "
1004 <<"Cleared "<<num_objs_cleared<<" objects"
1005 <<" in "<<num_blocks_cleared<<" blocks ("
1006 <<percent<<"%)"<<std::endl;
1008 if(num_blocks_checked % unload_interval == 0){
1009 m_map->unloadUnreferencedBlocks();
1012 m_map->unloadUnreferencedBlocks();
1014 // Drop references that were added above
1015 for(std::list<v3s16>::iterator i = loaded_blocks.begin();
1016 i != loaded_blocks.end(); ++i)
1019 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1024 infostream<<"ServerEnvironment::clearAllObjects(): "
1025 <<"Finished: Cleared "<<num_objs_cleared<<" objects"
1026 <<" in "<<num_blocks_cleared<<" blocks"<<std::endl;
1029 void ServerEnvironment::step(float dtime)
1031 DSTACK(__FUNCTION_NAME);
1033 //TimeTaker timer("ServerEnv step");
1035 /* Step time of day */
1036 stepTimeOfDay(dtime);
1039 // NOTE: This is kind of funny on a singleplayer game, but doesn't
1040 // really matter that much.
1041 m_recommended_send_interval = g_settings->getFloat("dedicated_server_step");
1047 m_game_time_fraction_counter += dtime;
1048 u32 inc_i = (u32)m_game_time_fraction_counter;
1049 m_game_time += inc_i;
1050 m_game_time_fraction_counter -= (float)inc_i;
1057 ScopeProfiler sp(g_profiler, "SEnv: handle players avg", SPT_AVG);
1058 for(std::list<Player*>::iterator i = m_players.begin();
1059 i != m_players.end(); ++i)
1061 Player *player = *i;
1063 // Ignore disconnected players
1064 if(player->peer_id == 0)
1068 player->move(dtime, *m_map, 100*BS);
1073 Manage active block list
1075 if(m_active_blocks_management_interval.step(dtime, 2.0))
1077 ScopeProfiler sp(g_profiler, "SEnv: manage act. block list avg /2s", SPT_AVG);
1079 Get player block positions
1081 std::list<v3s16> players_blockpos;
1082 for(std::list<Player*>::iterator
1083 i = m_players.begin();
1084 i != m_players.end(); ++i)
1086 Player *player = *i;
1087 // Ignore disconnected players
1088 if(player->peer_id == 0)
1090 v3s16 blockpos = getNodeBlockPos(
1091 floatToInt(player->getPosition(), BS));
1092 players_blockpos.push_back(blockpos);
1096 Update list of active blocks, collecting changes
1098 const s16 active_block_range = g_settings->getS16("active_block_range");
1099 std::set<v3s16> blocks_removed;
1100 std::set<v3s16> blocks_added;
1101 m_active_blocks.update(players_blockpos, active_block_range,
1102 blocks_removed, blocks_added);
1105 Handle removed blocks
1108 // Convert active objects that are no more in active blocks to static
1109 deactivateFarObjects(false);
1111 for(std::set<v3s16>::iterator
1112 i = blocks_removed.begin();
1113 i != blocks_removed.end(); ++i)
1117 /* infostream<<"Server: Block " << PP(p)
1118 << " became inactive"<<std::endl; */
1120 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1124 // Set current time as timestamp (and let it set ChangedFlag)
1125 block->setTimestamp(m_game_time);
1132 for(std::set<v3s16>::iterator
1133 i = blocks_added.begin();
1134 i != blocks_added.end(); ++i)
1138 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1140 // Block needs to be fetched first
1141 m_emerger->enqueueBlockEmerge(
1142 PEER_ID_INEXISTENT, p, false);
1143 m_active_blocks.m_list.erase(p);
1147 activateBlock(block);
1148 /* infostream<<"Server: Block " << PP(p)
1149 << " became active"<<std::endl; */
1154 Mess around in active blocks
1156 if(m_active_blocks_nodemetadata_interval.step(dtime, 1.0))
1158 ScopeProfiler sp(g_profiler, "SEnv: mess in act. blocks avg /1s", SPT_AVG);
1162 for(std::set<v3s16>::iterator
1163 i = m_active_blocks.m_list.begin();
1164 i != m_active_blocks.m_list.end(); ++i)
1168 /*infostream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
1169 <<") being handled"<<std::endl;*/
1171 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1175 // Reset block usage timer
1176 block->resetUsageTimer();
1178 // Set current time as timestamp
1179 block->setTimestampNoChangedFlag(m_game_time);
1180 // If time has changed much from the one on disk,
1181 // set block to be saved when it is unloaded
1182 if(block->getTimestamp() > block->getDiskTimestamp() + 60)
1183 block->raiseModified(MOD_STATE_WRITE_AT_UNLOAD,
1184 "Timestamp older than 60s (step)");
1187 std::map<v3s16, NodeTimer> elapsed_timers =
1188 block->m_node_timers.step((float)dtime);
1189 if(!elapsed_timers.empty()){
1191 for(std::map<v3s16, NodeTimer>::iterator
1192 i = elapsed_timers.begin();
1193 i != elapsed_timers.end(); i++){
1194 n = block->getNodeNoEx(i->first);
1195 p = i->first + block->getPosRelative();
1196 if(m_script->node_on_timer(p,n,i->second.elapsed))
1197 block->setNodeTimer(i->first,NodeTimer(i->second.timeout,0));
1203 const float abm_interval = 1.0;
1204 if(m_active_block_modifier_interval.step(dtime, abm_interval))
1206 if(m_active_block_interval_overload_skip > 0){
1207 ScopeProfiler sp(g_profiler, "SEnv: ABM overload skips");
1208 m_active_block_interval_overload_skip--;
1211 ScopeProfiler sp(g_profiler, "SEnv: modify in blocks avg /1s", SPT_AVG);
1212 TimeTaker timer("modify in active blocks");
1214 // Initialize handling of ActiveBlockModifiers
1215 ABMHandler abmhandler(m_abms, abm_interval, this, true);
1217 for(std::set<v3s16>::iterator
1218 i = m_active_blocks.m_list.begin();
1219 i != m_active_blocks.m_list.end(); ++i)
1223 /*infostream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
1224 <<") being handled"<<std::endl;*/
1226 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1230 // Set current time as timestamp
1231 block->setTimestampNoChangedFlag(m_game_time);
1233 /* Handle ActiveBlockModifiers */
1234 abmhandler.apply(block);
1237 u32 time_ms = timer.stop(true);
1238 u32 max_time_ms = 200;
1239 if(time_ms > max_time_ms){
1240 infostream<<"WARNING: active block modifiers took "
1241 <<time_ms<<"ms (longer than "
1242 <<max_time_ms<<"ms)"<<std::endl;
1243 m_active_block_interval_overload_skip = (time_ms / max_time_ms) + 1;
1248 Step script environment (run global on_step())
1250 m_script->environment_Step(dtime);
1256 ScopeProfiler sp(g_profiler, "SEnv: step act. objs avg", SPT_AVG);
1257 //TimeTaker timer("Step active objects");
1259 g_profiler->avg("SEnv: num of objects", m_active_objects.size());
1261 // This helps the objects to send data at the same time
1262 bool send_recommended = false;
1263 m_send_recommended_timer += dtime;
1264 if(m_send_recommended_timer > getSendRecommendedInterval())
1266 m_send_recommended_timer -= getSendRecommendedInterval();
1267 send_recommended = true;
1270 for(std::map<u16, ServerActiveObject*>::iterator
1271 i = m_active_objects.begin();
1272 i != m_active_objects.end(); ++i)
1274 ServerActiveObject* obj = i->second;
1275 // Remove non-peaceful mobs on peaceful mode
1276 if(g_settings->getBool("only_peaceful_mobs")){
1277 if(!obj->isPeaceful())
1278 obj->m_removed = true;
1280 // Don't step if is to be removed or stored statically
1281 if(obj->m_removed || obj->m_pending_deactivation)
1284 obj->step(dtime, send_recommended);
1285 // Read messages from object
1286 while(!obj->m_messages_out.empty())
1288 m_active_object_messages.push_back(
1289 obj->m_messages_out.pop_front());
1295 Manage active objects
1297 if(m_object_management_interval.step(dtime, 0.5))
1299 ScopeProfiler sp(g_profiler, "SEnv: remove removed objs avg /.5s", SPT_AVG);
1301 Remove objects that satisfy (m_removed && m_known_by_count==0)
1303 removeRemovedObjects();
1307 ServerActiveObject* ServerEnvironment::getActiveObject(u16 id)
1309 std::map<u16, ServerActiveObject*>::iterator n;
1310 n = m_active_objects.find(id);
1311 if(n == m_active_objects.end())
1316 bool isFreeServerActiveObjectId(u16 id,
1317 std::map<u16, ServerActiveObject*> &objects)
1322 return objects.find(id) == objects.end();
1325 u16 getFreeServerActiveObjectId(
1326 std::map<u16, ServerActiveObject*> &objects)
1328 //try to reuse id's as late as possible
1329 static u16 last_used_id = 0;
1330 u16 startid = last_used_id;
1334 if(isFreeServerActiveObjectId(last_used_id, objects))
1335 return last_used_id;
1337 if(last_used_id == startid)
1342 u16 ServerEnvironment::addActiveObject(ServerActiveObject *object)
1345 u16 id = addActiveObjectRaw(object, true, 0);
1350 bool ServerEnvironment::addActiveObjectAsStatic(ServerActiveObject *obj)
1354 v3f objectpos = obj->getBasePosition();
1356 // The block in which the object resides in
1357 v3s16 blockpos_o = getNodeBlockPos(floatToInt(objectpos, BS));
1360 Update the static data
1363 // Create new static object
1364 std::string staticdata = obj->getStaticData();
1365 StaticObject s_obj(obj->getType(), objectpos, staticdata);
1366 // Add to the block where the object is located in
1367 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1368 // Get or generate the block
1369 MapBlock *block = m_map->emergeBlock(blockpos);
1371 bool succeeded = false;
1375 block->m_static_objects.insert(0, s_obj);
1376 block->raiseModified(MOD_STATE_WRITE_AT_UNLOAD,
1377 "addActiveObjectAsStatic");
1381 infostream<<"ServerEnvironment::addActiveObjectAsStatic: "
1382 <<"Could not find or generate "
1383 <<"a block for storing static object"<<std::endl;
1387 if(obj->environmentDeletes())
1395 Finds out what new objects have been added to
1396 inside a radius around a position
1398 void ServerEnvironment::getAddedActiveObjects(v3s16 pos, s16 radius,
1399 std::set<u16> ¤t_objects,
1400 std::set<u16> &added_objects)
1402 v3f pos_f = intToFloat(pos, BS);
1403 f32 radius_f = radius * BS;
1405 Go through the object list,
1406 - discard m_removed objects,
1407 - discard objects that are too far away,
1408 - discard objects that are found in current_objects.
1409 - add remaining objects to added_objects
1411 for(std::map<u16, ServerActiveObject*>::iterator
1412 i = m_active_objects.begin();
1413 i != m_active_objects.end(); ++i)
1417 ServerActiveObject *object = i->second;
1420 // Discard if removed
1421 if(object->m_removed)
1423 if(object->unlimitedTransferDistance() == false){
1424 // Discard if too far
1425 f32 distance_f = object->getBasePosition().getDistanceFrom(pos_f);
1426 if(distance_f > radius_f)
1429 // Discard if already on current_objects
1430 std::set<u16>::iterator n;
1431 n = current_objects.find(id);
1432 if(n != current_objects.end())
1434 // Add to added_objects
1435 added_objects.insert(id);
1440 Finds out what objects have been removed from
1441 inside a radius around a position
1443 void ServerEnvironment::getRemovedActiveObjects(v3s16 pos, s16 radius,
1444 std::set<u16> ¤t_objects,
1445 std::set<u16> &removed_objects)
1447 v3f pos_f = intToFloat(pos, BS);
1448 f32 radius_f = radius * BS;
1450 Go through current_objects; object is removed if:
1451 - object is not found in m_active_objects (this is actually an
1452 error condition; objects should be set m_removed=true and removed
1453 only after all clients have been informed about removal), or
1454 - object has m_removed=true, or
1455 - object is too far away
1457 for(std::set<u16>::iterator
1458 i = current_objects.begin();
1459 i != current_objects.end(); ++i)
1462 ServerActiveObject *object = getActiveObject(id);
1465 infostream<<"ServerEnvironment::getRemovedActiveObjects():"
1466 <<" object in current_objects is NULL"<<std::endl;
1467 removed_objects.insert(id);
1471 if(object->m_removed)
1473 removed_objects.insert(id);
1477 // If transfer distance is unlimited, don't remove
1478 if(object->unlimitedTransferDistance())
1481 f32 distance_f = object->getBasePosition().getDistanceFrom(pos_f);
1483 if(distance_f >= radius_f)
1485 removed_objects.insert(id);
1493 ActiveObjectMessage ServerEnvironment::getActiveObjectMessage()
1495 if(m_active_object_messages.empty())
1496 return ActiveObjectMessage(0);
1498 ActiveObjectMessage message = m_active_object_messages.front();
1499 m_active_object_messages.pop_front();
1504 ************ Private methods *************
1507 u16 ServerEnvironment::addActiveObjectRaw(ServerActiveObject *object,
1508 bool set_changed, u32 dtime_s)
1511 if(object->getId() == 0){
1512 u16 new_id = getFreeServerActiveObjectId(m_active_objects);
1515 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1516 <<"no free ids available"<<std::endl;
1517 if(object->environmentDeletes())
1521 object->setId(new_id);
1524 verbosestream<<"ServerEnvironment::addActiveObjectRaw(): "
1525 <<"supplied with id "<<object->getId()<<std::endl;
1527 if(isFreeServerActiveObjectId(object->getId(), m_active_objects) == false)
1529 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1530 <<"id is not free ("<<object->getId()<<")"<<std::endl;
1531 if(object->environmentDeletes())
1535 /*infostream<<"ServerEnvironment::addActiveObjectRaw(): "
1536 <<"added (id="<<object->getId()<<")"<<std::endl;*/
1538 m_active_objects[object->getId()] = object;
1540 verbosestream<<"ServerEnvironment::addActiveObjectRaw(): "
1541 <<"Added id="<<object->getId()<<"; there are now "
1542 <<m_active_objects.size()<<" active objects."
1545 // Register reference in scripting api (must be done before post-init)
1546 m_script->addObjectReference(object);
1547 // Post-initialize object
1548 object->addedToEnvironment(dtime_s);
1550 // Add static data to block
1551 if(object->isStaticAllowed())
1553 // Add static object to active static list of the block
1554 v3f objectpos = object->getBasePosition();
1555 std::string staticdata = object->getStaticData();
1556 StaticObject s_obj(object->getType(), objectpos, staticdata);
1557 // Add to the block where the object is located in
1558 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1559 MapBlock *block = m_map->getBlockNoCreateNoEx(blockpos);
1562 block->m_static_objects.m_active[object->getId()] = s_obj;
1563 object->m_static_exists = true;
1564 object->m_static_block = blockpos;
1567 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1568 "addActiveObjectRaw");
1571 v3s16 p = floatToInt(objectpos, BS);
1572 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1573 <<"could not find block for storing id="<<object->getId()
1574 <<" statically (pos="<<PP(p)<<")"<<std::endl;
1578 return object->getId();
1582 Remove objects that satisfy (m_removed && m_known_by_count==0)
1584 void ServerEnvironment::removeRemovedObjects()
1586 std::list<u16> objects_to_remove;
1587 for(std::map<u16, ServerActiveObject*>::iterator
1588 i = m_active_objects.begin();
1589 i != m_active_objects.end(); ++i)
1592 ServerActiveObject* obj = i->second;
1593 // This shouldn't happen but check it
1596 infostream<<"NULL object found in ServerEnvironment"
1597 <<" while finding removed objects. id="<<id<<std::endl;
1598 // Id to be removed from m_active_objects
1599 objects_to_remove.push_back(id);
1604 We will delete objects that are marked as removed or thatare
1605 waiting for deletion after deactivation
1607 if(obj->m_removed == false && obj->m_pending_deactivation == false)
1611 Delete static data from block if is marked as removed
1613 if(obj->m_static_exists && obj->m_removed)
1615 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
1617 block->m_static_objects.remove(id);
1618 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1619 "removeRemovedObjects");
1620 obj->m_static_exists = false;
1622 infostream << "failed to emerge block from which "
1623 "an object to be removed was loaded from. id="<<id<<std::endl;
1627 // If m_known_by_count > 0, don't actually remove.
1628 if(obj->m_known_by_count > 0)
1631 // Tell the object about removal
1632 obj->removingFromEnvironment();
1633 // Deregister in scripting api
1634 m_script->removeObjectReference(obj);
1637 if(obj->environmentDeletes())
1639 // Id to be removed from m_active_objects
1640 objects_to_remove.push_back(id);
1642 // Remove references from m_active_objects
1643 for(std::list<u16>::iterator i = objects_to_remove.begin();
1644 i != objects_to_remove.end(); ++i)
1646 m_active_objects.erase(*i);
1650 static void print_hexdump(std::ostream &o, const std::string &data)
1652 const int linelength = 16;
1653 for(int l=0; ; l++){
1654 int i0 = linelength * l;
1655 bool at_end = false;
1656 int thislinelength = linelength;
1657 if(i0 + thislinelength > (int)data.size()){
1658 thislinelength = data.size() - i0;
1661 for(int di=0; di<linelength; di++){
1664 if(di<thislinelength)
1665 snprintf(buf, 4, "%.2x ", data[i]);
1667 snprintf(buf, 4, " ");
1671 for(int di=0; di<thislinelength; di++){
1685 Convert stored objects from blocks near the players to active.
1687 void ServerEnvironment::activateObjects(MapBlock *block, u32 dtime_s)
1691 // Ignore if no stored objects (to not set changed flag)
1692 if(block->m_static_objects.m_stored.size() == 0)
1694 verbosestream<<"ServerEnvironment::activateObjects(): "
1695 <<"activating objects of block "<<PP(block->getPos())
1696 <<" ("<<block->m_static_objects.m_stored.size()
1697 <<" objects)"<<std::endl;
1698 bool large_amount = (block->m_static_objects.m_stored.size() > g_settings->getU16("max_objects_per_block"));
1700 errorstream<<"suspiciously large amount of objects detected: "
1701 <<block->m_static_objects.m_stored.size()<<" in "
1702 <<PP(block->getPos())
1703 <<"; removing all of them."<<std::endl;
1704 // Clear stored list
1705 block->m_static_objects.m_stored.clear();
1706 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1707 "stored list cleared in activateObjects due to "
1708 "large amount of objects");
1711 // A list for objects that couldn't be converted to active for some
1712 // reason. They will be stored back.
1713 std::list<StaticObject> new_stored;
1714 // Loop through stored static objects
1715 for(std::list<StaticObject>::iterator
1716 i = block->m_static_objects.m_stored.begin();
1717 i != block->m_static_objects.m_stored.end(); ++i)
1719 /*infostream<<"Server: Creating an active object from "
1720 <<"static data"<<std::endl;*/
1721 StaticObject &s_obj = *i;
1722 // Create an active object from the data
1723 ServerActiveObject *obj = ServerActiveObject::create
1724 (s_obj.type, this, 0, s_obj.pos, s_obj.data);
1725 // If couldn't create object, store static data back.
1728 errorstream<<"ServerEnvironment::activateObjects(): "
1729 <<"failed to create active object from static object "
1730 <<"in block "<<PP(s_obj.pos/BS)
1731 <<" type="<<(int)s_obj.type<<" data:"<<std::endl;
1732 print_hexdump(verbosestream, s_obj.data);
1734 new_stored.push_back(s_obj);
1737 verbosestream<<"ServerEnvironment::activateObjects(): "
1738 <<"activated static object pos="<<PP(s_obj.pos/BS)
1739 <<" type="<<(int)s_obj.type<<std::endl;
1740 // This will also add the object to the active static list
1741 addActiveObjectRaw(obj, false, dtime_s);
1743 // Clear stored list
1744 block->m_static_objects.m_stored.clear();
1745 // Add leftover failed stuff to stored list
1746 for(std::list<StaticObject>::iterator
1747 i = new_stored.begin();
1748 i != new_stored.end(); ++i)
1750 StaticObject &s_obj = *i;
1751 block->m_static_objects.m_stored.push_back(s_obj);
1754 Note: Block hasn't really been modified here.
1755 The objects have just been activated and moved from the stored
1756 static list to the active static list.
1757 As such, the block is essentially the same.
1758 Thus, do not call block->raiseModified(MOD_STATE_WRITE_NEEDED).
1759 Otherwise there would be a huge amount of unnecessary I/O.
1764 Convert objects that are not standing inside active blocks to static.
1766 If m_known_by_count != 0, active object is not deleted, but static
1767 data is still updated.
1769 If force_delete is set, active object is deleted nevertheless. It
1770 shall only be set so in the destructor of the environment.
1772 If block wasn't generated (not in memory or on disk),
1774 void ServerEnvironment::deactivateFarObjects(bool force_delete)
1776 std::list<u16> objects_to_remove;
1777 for(std::map<u16, ServerActiveObject*>::iterator
1778 i = m_active_objects.begin();
1779 i != m_active_objects.end(); ++i)
1781 ServerActiveObject* obj = i->second;
1784 // Do not deactivate if static data creation not allowed
1785 if(!force_delete && !obj->isStaticAllowed())
1788 // If pending deactivation, let removeRemovedObjects() do it
1789 if(!force_delete && obj->m_pending_deactivation)
1793 v3f objectpos = obj->getBasePosition();
1795 // The block in which the object resides in
1796 v3s16 blockpos_o = getNodeBlockPos(floatToInt(objectpos, BS));
1798 // If object's static data is stored in a deactivated block and object
1799 // is actually located in an active block, re-save to the block in
1800 // which the object is actually located in.
1802 obj->m_static_exists &&
1803 !m_active_blocks.contains(obj->m_static_block) &&
1804 m_active_blocks.contains(blockpos_o))
1806 v3s16 old_static_block = obj->m_static_block;
1808 // Save to block where object is located
1809 MapBlock *block = m_map->emergeBlock(blockpos_o, false);
1811 errorstream<<"ServerEnvironment::deactivateFarObjects(): "
1812 <<"Could not save object id="<<id
1813 <<" to it's current block "<<PP(blockpos_o)
1817 std::string staticdata_new = obj->getStaticData();
1818 StaticObject s_obj(obj->getType(), objectpos, staticdata_new);
1819 block->m_static_objects.insert(id, s_obj);
1820 obj->m_static_block = blockpos_o;
1821 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1822 "deactivateFarObjects: Static data moved in");
1824 // Delete from block where object was located
1825 block = m_map->emergeBlock(old_static_block, false);
1827 errorstream<<"ServerEnvironment::deactivateFarObjects(): "
1828 <<"Could not delete object id="<<id
1829 <<" from it's previous block "<<PP(old_static_block)
1833 block->m_static_objects.remove(id);
1834 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1835 "deactivateFarObjects: Static data moved out");
1839 // If block is active, don't remove
1840 if(!force_delete && m_active_blocks.contains(blockpos_o))
1843 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
1844 <<"deactivating object id="<<id<<" on inactive block "
1845 <<PP(blockpos_o)<<std::endl;
1847 // If known by some client, don't immediately delete.
1848 bool pending_delete = (obj->m_known_by_count > 0 && !force_delete);
1851 Update the static data
1854 if(obj->isStaticAllowed())
1856 // Create new static object
1857 std::string staticdata_new = obj->getStaticData();
1858 StaticObject s_obj(obj->getType(), objectpos, staticdata_new);
1860 bool stays_in_same_block = false;
1861 bool data_changed = true;
1863 if(obj->m_static_exists){
1864 if(obj->m_static_block == blockpos_o)
1865 stays_in_same_block = true;
1867 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
1869 std::map<u16, StaticObject>::iterator n =
1870 block->m_static_objects.m_active.find(id);
1871 if(n != block->m_static_objects.m_active.end()){
1872 StaticObject static_old = n->second;
1874 float save_movem = obj->getMinimumSavedMovement();
1876 if(static_old.data == staticdata_new &&
1877 (static_old.pos - objectpos).getLength() < save_movem)
1878 data_changed = false;
1880 errorstream<<"ServerEnvironment::deactivateFarObjects(): "
1881 <<"id="<<id<<" m_static_exists=true but "
1882 <<"static data doesn't actually exist in "
1883 <<PP(obj->m_static_block)<<std::endl;
1887 bool shall_be_written = (!stays_in_same_block || data_changed);
1889 // Delete old static object
1890 if(obj->m_static_exists)
1892 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
1895 block->m_static_objects.remove(id);
1896 obj->m_static_exists = false;
1897 // Only mark block as modified if data changed considerably
1898 if(shall_be_written)
1899 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1900 "deactivateFarObjects: Static data "
1901 "changed considerably");
1905 // Add to the block where the object is located in
1906 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1907 // Get or generate the block
1908 MapBlock *block = NULL;
1910 block = m_map->emergeBlock(blockpos);
1911 } catch(InvalidPositionException &e){
1912 // Handled via NULL pointer
1917 if(block->m_static_objects.m_stored.size() >= g_settings->getU16("max_objects_per_block")){
1918 errorstream<<"ServerEnv: Trying to store id="<<obj->getId()
1919 <<" statically but block "<<PP(blockpos)
1920 <<" already contains "
1921 <<block->m_static_objects.m_stored.size()
1923 <<" Forcing delete."<<std::endl;
1924 force_delete = true;
1926 // If static counterpart already exists, remove it first.
1927 // This shouldn't happen, but happens rarely for some
1928 // unknown reason. Unsuccessful attempts have been made to
1929 // find said reason.
1930 if(id && block->m_static_objects.m_active.find(id) != block->m_static_objects.m_active.end()){
1931 infostream<<"ServerEnv: WARNING: Performing hack #83274"
1933 block->m_static_objects.remove(id);
1936 block->m_static_objects.insert(0, s_obj);
1938 // Only mark block as modified if data changed considerably
1939 if(shall_be_written)
1940 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1941 "deactivateFarObjects: Static data "
1942 "changed considerably");
1944 obj->m_static_exists = true;
1945 obj->m_static_block = block->getPos();
1950 v3s16 p = floatToInt(objectpos, BS);
1951 errorstream<<"ServerEnv: Could not find or generate "
1952 <<"a block for storing id="<<obj->getId()
1953 <<" statically (pos="<<PP(p)<<")"<<std::endl;
1960 If known by some client, set pending deactivation.
1961 Otherwise delete it immediately.
1964 if(pending_delete && !force_delete)
1966 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
1967 <<"object id="<<id<<" is known by clients"
1968 <<"; not deleting yet"<<std::endl;
1970 obj->m_pending_deactivation = true;
1974 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
1975 <<"object id="<<id<<" is not known by clients"
1976 <<"; deleting"<<std::endl;
1978 // Tell the object about removal
1979 obj->removingFromEnvironment();
1980 // Deregister in scripting api
1981 m_script->removeObjectReference(obj);
1983 // Delete active object
1984 if(obj->environmentDeletes())
1986 // Id to be removed from m_active_objects
1987 objects_to_remove.push_back(id);
1990 // Remove references from m_active_objects
1991 for(std::list<u16>::iterator i = objects_to_remove.begin();
1992 i != objects_to_remove.end(); ++i)
1994 m_active_objects.erase(*i);
2001 #include "clientsimpleobject.h"
2007 ClientEnvironment::ClientEnvironment(ClientMap *map, scene::ISceneManager *smgr,
2008 ITextureSource *texturesource, IGameDef *gamedef,
2009 IrrlichtDevice *irr):
2012 m_texturesource(texturesource),
2018 ClientEnvironment::~ClientEnvironment()
2020 // delete active objects
2021 for(std::map<u16, ClientActiveObject*>::iterator
2022 i = m_active_objects.begin();
2023 i != m_active_objects.end(); ++i)
2028 for(std::list<ClientSimpleObject*>::iterator
2029 i = m_simple_objects.begin(); i != m_simple_objects.end(); ++i)
2038 Map & ClientEnvironment::getMap()
2043 ClientMap & ClientEnvironment::getClientMap()
2048 void ClientEnvironment::addPlayer(Player *player)
2050 DSTACK(__FUNCTION_NAME);
2052 It is a failure if player is local and there already is a local
2055 assert(!(player->isLocal() == true && getLocalPlayer() != NULL));
2057 Environment::addPlayer(player);
2060 LocalPlayer * ClientEnvironment::getLocalPlayer()
2062 for(std::list<Player*>::iterator i = m_players.begin();
2063 i != m_players.end(); ++i)
2065 Player *player = *i;
2066 if(player->isLocal())
2067 return (LocalPlayer*)player;
2072 void ClientEnvironment::step(float dtime)
2074 DSTACK(__FUNCTION_NAME);
2076 /* Step time of day */
2077 stepTimeOfDay(dtime);
2079 // Get some settings
2080 bool fly_allowed = m_gamedef->checkLocalPrivilege("fly");
2081 bool free_move = fly_allowed && g_settings->getBool("free_move");
2084 LocalPlayer *lplayer = getLocalPlayer();
2086 // collision info queue
2087 std::list<CollisionInfo> player_collisions;
2090 Get the speed the player is going
2092 bool is_climbing = lplayer->is_climbing;
2094 f32 player_speed = lplayer->getSpeed().getLength();
2097 Maximum position increment
2099 //f32 position_max_increment = 0.05*BS;
2100 f32 position_max_increment = 0.1*BS;
2102 // Maximum time increment (for collision detection etc)
2103 // time = distance / speed
2104 f32 dtime_max_increment = 1;
2105 if(player_speed > 0.001)
2106 dtime_max_increment = position_max_increment / player_speed;
2108 // Maximum time increment is 10ms or lower
2109 if(dtime_max_increment > 0.01)
2110 dtime_max_increment = 0.01;
2112 // Don't allow overly huge dtime
2116 f32 dtime_downcount = dtime;
2119 Stuff that has a maximum time increment
2128 if(dtime_downcount > dtime_max_increment)
2130 dtime_part = dtime_max_increment;
2131 dtime_downcount -= dtime_part;
2135 dtime_part = dtime_downcount;
2137 Setting this to 0 (no -=dtime_part) disables an infinite loop
2138 when dtime_part is so small that dtime_downcount -= dtime_part
2141 dtime_downcount = 0;
2150 if(free_move == false && is_climbing == false)
2153 v3f speed = lplayer->getSpeed();
2154 if(lplayer->in_liquid == false)
2155 speed.Y -= lplayer->movement_gravity * lplayer->physics_override_gravity * dtime_part * 2;
2157 // Liquid floating / sinking
2158 if(lplayer->in_liquid && !lplayer->swimming_vertical)
2159 speed.Y -= lplayer->movement_liquid_sink * dtime_part * 2;
2161 // Liquid resistance
2162 if(lplayer->in_liquid_stable || lplayer->in_liquid)
2164 // How much the node's viscosity blocks movement, ranges between 0 and 1
2165 // Should match the scale at which viscosity increase affects other liquid attributes
2166 const f32 viscosity_factor = 0.3;
2168 v3f d_wanted = -speed / lplayer->movement_liquid_fluidity;
2169 f32 dl = d_wanted.getLength();
2170 if(dl > lplayer->movement_liquid_fluidity_smooth)
2171 dl = lplayer->movement_liquid_fluidity_smooth;
2172 dl *= (lplayer->liquid_viscosity * viscosity_factor) + (1 - viscosity_factor);
2174 v3f d = d_wanted.normalize() * dl;
2178 if(speed.X > lplayer->movement_liquid_fluidity + lplayer->movement_liquid_fluidity_smooth) speed.X -= lplayer->movement_liquid_fluidity_smooth;
2179 if(speed.X < -lplayer->movement_liquid_fluidity - lplayer->movement_liquid_fluidity_smooth) speed.X += lplayer->movement_liquid_fluidity_smooth;
2180 if(speed.Y > lplayer->movement_liquid_fluidity + lplayer->movement_liquid_fluidity_smooth) speed.Y -= lplayer->movement_liquid_fluidity_smooth;
2181 if(speed.Y < -lplayer->movement_liquid_fluidity - lplayer->movement_liquid_fluidity_smooth) speed.Y += lplayer->movement_liquid_fluidity_smooth;
2182 if(speed.Z > lplayer->movement_liquid_fluidity + lplayer->movement_liquid_fluidity_smooth) speed.Z -= lplayer->movement_liquid_fluidity_smooth;
2183 if(speed.Z < -lplayer->movement_liquid_fluidity - lplayer->movement_liquid_fluidity_smooth) speed.Z += lplayer->movement_liquid_fluidity_smooth;
2187 lplayer->setSpeed(speed);
2192 This also does collision detection.
2194 lplayer->move(dtime_part, this, position_max_increment,
2195 &player_collisions);
2198 while(dtime_downcount > 0.001);
2200 //std::cout<<"Looped "<<loopcount<<" times."<<std::endl;
2202 for(std::list<CollisionInfo>::iterator
2203 i = player_collisions.begin();
2204 i != player_collisions.end(); ++i)
2206 CollisionInfo &info = *i;
2207 v3f speed_diff = info.new_speed - info.old_speed;;
2208 // Handle only fall damage
2209 // (because otherwise walking against something in fast_move kills you)
2210 if(speed_diff.Y < 0 || info.old_speed.Y >= 0)
2212 // Get rid of other components
2215 f32 pre_factor = 1; // 1 hp per node/s
2216 f32 tolerance = BS*14; // 5 without damage
2217 f32 post_factor = 1; // 1 hp per node/s
2218 if(info.type == COLLISION_NODE)
2220 const ContentFeatures &f = m_gamedef->ndef()->
2221 get(m_map->getNodeNoEx(info.node_p));
2222 // Determine fall damage multiplier
2223 int addp = itemgroup_get(f.groups, "fall_damage_add_percent");
2224 pre_factor = 1.0 + (float)addp/100.0;
2226 float speed = pre_factor * speed_diff.getLength();
2227 if(speed > tolerance)
2229 f32 damage_f = (speed - tolerance)/BS * post_factor;
2230 u16 damage = (u16)(damage_f+0.5);
2232 damageLocalPlayer(damage, true);
2233 MtEvent *e = new SimpleTriggerEvent("PlayerFallingDamage");
2234 m_gamedef->event()->put(e);
2240 A quick draft of lava damage
2242 if(m_lava_hurt_interval.step(dtime, 1.0))
2244 v3f pf = lplayer->getPosition();
2246 // Feet, middle and head
2247 v3s16 p1 = floatToInt(pf + v3f(0, BS*0.1, 0), BS);
2248 MapNode n1 = m_map->getNodeNoEx(p1);
2249 v3s16 p2 = floatToInt(pf + v3f(0, BS*0.8, 0), BS);
2250 MapNode n2 = m_map->getNodeNoEx(p2);
2251 v3s16 p3 = floatToInt(pf + v3f(0, BS*1.6, 0), BS);
2252 MapNode n3 = m_map->getNodeNoEx(p3);
2254 u32 damage_per_second = 0;
2255 damage_per_second = MYMAX(damage_per_second,
2256 m_gamedef->ndef()->get(n1).damage_per_second);
2257 damage_per_second = MYMAX(damage_per_second,
2258 m_gamedef->ndef()->get(n2).damage_per_second);
2259 damage_per_second = MYMAX(damage_per_second,
2260 m_gamedef->ndef()->get(n3).damage_per_second);
2262 if(damage_per_second != 0)
2264 damageLocalPlayer(damage_per_second, true);
2271 if(m_drowning_interval.step(dtime, 2.0))
2273 v3f pf = lplayer->getPosition();
2276 v3s16 p = floatToInt(pf + v3f(0, BS*1.6, 0), BS);
2277 MapNode n = m_map->getNodeNoEx(p);
2278 ContentFeatures c = m_gamedef->ndef()->get(n);
2279 u8 drowning_damage = c.drowning;
2280 if(drowning_damage > 0 && lplayer->hp > 0){
2281 u16 breath = lplayer->getBreath();
2288 lplayer->setBreath(breath);
2289 updateLocalPlayerBreath(breath);
2292 if(lplayer->getBreath() == 0 && drowning_damage > 0){
2293 damageLocalPlayer(drowning_damage, true);
2296 if(m_breathing_interval.step(dtime, 0.5))
2298 v3f pf = lplayer->getPosition();
2301 v3s16 p = floatToInt(pf + v3f(0, BS*1.6, 0), BS);
2302 MapNode n = m_map->getNodeNoEx(p);
2303 ContentFeatures c = m_gamedef->ndef()->get(n);
2305 lplayer->setBreath(11);
2307 else if(c.drowning == 0){
2308 u16 breath = lplayer->getBreath();
2311 lplayer->setBreath(breath);
2312 updateLocalPlayerBreath(breath);
2318 Stuff that can be done in an arbitarily large dtime
2320 for(std::list<Player*>::iterator i = m_players.begin();
2321 i != m_players.end(); ++i)
2323 Player *player = *i;
2326 Handle non-local players
2328 if(player->isLocal() == false)
2331 player->move(dtime, *m_map, 100*BS);
2335 // Update lighting on all players on client
2339 v3s16 p = player->getLightPosition();
2340 MapNode n = m_map->getNode(p);
2341 light = n.getLightBlendF1((float)getDayNightRatio()/1000, m_gamedef->ndef());
2343 catch(InvalidPositionException &e){
2344 light = blend_light_f1((float)getDayNightRatio()/1000, LIGHT_SUN, 0);
2346 player->light = light;
2350 Step active objects and update lighting of them
2353 g_profiler->avg("CEnv: num of objects", m_active_objects.size());
2354 bool update_lighting = m_active_object_light_update_interval.step(dtime, 0.21);
2355 for(std::map<u16, ClientActiveObject*>::iterator
2356 i = m_active_objects.begin();
2357 i != m_active_objects.end(); ++i)
2359 ClientActiveObject* obj = i->second;
2361 obj->step(dtime, this);
2369 v3s16 p = obj->getLightPosition();
2370 MapNode n = m_map->getNode(p);
2371 light = n.getLightBlend(getDayNightRatio(), m_gamedef->ndef());
2373 catch(InvalidPositionException &e){
2374 light = blend_light(getDayNightRatio(), LIGHT_SUN, 0);
2376 obj->updateLight(light);
2381 Step and handle simple objects
2383 g_profiler->avg("CEnv: num of simple objects", m_simple_objects.size());
2384 for(std::list<ClientSimpleObject*>::iterator
2385 i = m_simple_objects.begin(); i != m_simple_objects.end();)
2387 ClientSimpleObject *simple = *i;
2388 std::list<ClientSimpleObject*>::iterator cur = i;
2390 simple->step(dtime);
2391 if(simple->m_to_be_removed){
2393 m_simple_objects.erase(cur);
2398 void ClientEnvironment::addSimpleObject(ClientSimpleObject *simple)
2400 m_simple_objects.push_back(simple);
2403 ClientActiveObject* ClientEnvironment::getActiveObject(u16 id)
2405 std::map<u16, ClientActiveObject*>::iterator n;
2406 n = m_active_objects.find(id);
2407 if(n == m_active_objects.end())
2412 bool isFreeClientActiveObjectId(u16 id,
2413 std::map<u16, ClientActiveObject*> &objects)
2418 return objects.find(id) == objects.end();
2421 u16 getFreeClientActiveObjectId(
2422 std::map<u16, ClientActiveObject*> &objects)
2424 //try to reuse id's as late as possible
2425 static u16 last_used_id = 0;
2426 u16 startid = last_used_id;
2430 if(isFreeClientActiveObjectId(last_used_id, objects))
2431 return last_used_id;
2433 if(last_used_id == startid)
2438 u16 ClientEnvironment::addActiveObject(ClientActiveObject *object)
2441 if(object->getId() == 0)
2443 u16 new_id = getFreeClientActiveObjectId(m_active_objects);
2446 infostream<<"ClientEnvironment::addActiveObject(): "
2447 <<"no free ids available"<<std::endl;
2451 object->setId(new_id);
2453 if(isFreeClientActiveObjectId(object->getId(), m_active_objects) == false)
2455 infostream<<"ClientEnvironment::addActiveObject(): "
2456 <<"id is not free ("<<object->getId()<<")"<<std::endl;
2460 infostream<<"ClientEnvironment::addActiveObject(): "
2461 <<"added (id="<<object->getId()<<")"<<std::endl;
2462 m_active_objects[object->getId()] = object;
2463 object->addToScene(m_smgr, m_texturesource, m_irr);
2464 { // Update lighting immediately
2468 v3s16 p = object->getLightPosition();
2469 MapNode n = m_map->getNode(p);
2470 light = n.getLightBlend(getDayNightRatio(), m_gamedef->ndef());
2472 catch(InvalidPositionException &e){
2473 light = blend_light(getDayNightRatio(), LIGHT_SUN, 0);
2475 object->updateLight(light);
2477 return object->getId();
2480 void ClientEnvironment::addActiveObject(u16 id, u8 type,
2481 const std::string &init_data)
2483 ClientActiveObject* obj =
2484 ClientActiveObject::create(type, m_gamedef, this);
2487 infostream<<"ClientEnvironment::addActiveObject(): "
2488 <<"id="<<id<<" type="<<type<<": Couldn't create object"
2497 obj->initialize(init_data);
2499 catch(SerializationError &e)
2501 errorstream<<"ClientEnvironment::addActiveObject():"
2502 <<" id="<<id<<" type="<<type
2503 <<": SerializationError in initialize(): "
2505 <<": init_data="<<serializeJsonString(init_data)
2509 addActiveObject(obj);
2512 void ClientEnvironment::removeActiveObject(u16 id)
2514 verbosestream<<"ClientEnvironment::removeActiveObject(): "
2515 <<"id="<<id<<std::endl;
2516 ClientActiveObject* obj = getActiveObject(id);
2519 infostream<<"ClientEnvironment::removeActiveObject(): "
2520 <<"id="<<id<<" not found"<<std::endl;
2523 obj->removeFromScene(true);
2525 m_active_objects.erase(id);
2528 void ClientEnvironment::processActiveObjectMessage(u16 id,
2529 const std::string &data)
2531 ClientActiveObject* obj = getActiveObject(id);
2534 infostream<<"ClientEnvironment::processActiveObjectMessage():"
2535 <<" got message for id="<<id<<", which doesn't exist."
2541 obj->processMessage(data);
2543 catch(SerializationError &e)
2545 errorstream<<"ClientEnvironment::processActiveObjectMessage():"
2546 <<" id="<<id<<" type="<<obj->getType()
2547 <<" SerializationError in processMessage(),"
2548 <<" message="<<serializeJsonString(data)
2554 Callbacks for activeobjects
2557 void ClientEnvironment::damageLocalPlayer(u8 damage, bool handle_hp)
2559 LocalPlayer *lplayer = getLocalPlayer();
2563 if(lplayer->hp > damage)
2564 lplayer->hp -= damage;
2569 ClientEnvEvent event;
2570 event.type = CEE_PLAYER_DAMAGE;
2571 event.player_damage.amount = damage;
2572 event.player_damage.send_to_server = handle_hp;
2573 m_client_event_queue.push_back(event);
2576 void ClientEnvironment::updateLocalPlayerBreath(u16 breath)
2578 ClientEnvEvent event;
2579 event.type = CEE_PLAYER_BREATH;
2580 event.player_breath.amount = breath;
2581 m_client_event_queue.push_back(event);
2585 Client likes to call these
2588 void ClientEnvironment::getActiveObjects(v3f origin, f32 max_d,
2589 std::vector<DistanceSortedActiveObject> &dest)
2591 for(std::map<u16, ClientActiveObject*>::iterator
2592 i = m_active_objects.begin();
2593 i != m_active_objects.end(); ++i)
2595 ClientActiveObject* obj = i->second;
2597 f32 d = (obj->getPosition() - origin).getLength();
2602 DistanceSortedActiveObject dso(obj, d);
2604 dest.push_back(dso);
2608 ClientEnvEvent ClientEnvironment::getClientEvent()
2610 ClientEnvEvent event;
2611 if(m_client_event_queue.empty())
2612 event.type = CEE_NONE;
2614 event = m_client_event_queue.front();
2615 m_client_event_queue.pop_front();
2620 #endif // #ifndef SERVER