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);
813 // Calculate weather conditions
815 m_map->updateBlockHeat(this, block->getPos() * MAP_BLOCKSIZE, block);
816 m_map->updateBlockHumidity(this, block->getPos() * MAP_BLOCKSIZE, block);
818 block->heat = HEAT_UNDEFINED;
819 block->humidity = HUMIDITY_UNDEFINED;
820 block->weather_update_time = 0;
824 std::map<v3s16, NodeTimer> elapsed_timers =
825 block->m_node_timers.step((float)dtime_s);
826 if(!elapsed_timers.empty()){
828 for(std::map<v3s16, NodeTimer>::iterator
829 i = elapsed_timers.begin();
830 i != elapsed_timers.end(); i++){
831 n = block->getNodeNoEx(i->first);
832 v3s16 p = i->first + block->getPosRelative();
833 if(m_script->node_on_timer(p,n,i->second.elapsed))
834 block->setNodeTimer(i->first,NodeTimer(i->second.timeout,0));
838 /* Handle ActiveBlockModifiers */
839 ABMHandler abmhandler(m_abms, dtime_s, this, false);
840 abmhandler.apply(block);
843 void ServerEnvironment::addActiveBlockModifier(ActiveBlockModifier *abm)
845 m_abms.push_back(ABMWithState(abm));
848 bool ServerEnvironment::setNode(v3s16 p, const MapNode &n)
850 INodeDefManager *ndef = m_gamedef->ndef();
851 MapNode n_old = m_map->getNodeNoEx(p);
853 if(ndef->get(n_old).has_on_destruct)
854 m_script->node_on_destruct(p, n_old);
856 bool succeeded = m_map->addNodeWithEvent(p, n);
859 // Call post-destructor
860 if(ndef->get(n_old).has_after_destruct)
861 m_script->node_after_destruct(p, n_old);
863 if(ndef->get(n).has_on_construct)
864 m_script->node_on_construct(p, n);
868 bool ServerEnvironment::removeNode(v3s16 p)
870 INodeDefManager *ndef = m_gamedef->ndef();
871 MapNode n_old = m_map->getNodeNoEx(p);
873 if(ndef->get(n_old).has_on_destruct)
874 m_script->node_on_destruct(p, n_old);
876 // This is slightly optimized compared to addNodeWithEvent(air)
877 bool succeeded = m_map->removeNodeWithEvent(p);
880 // Call post-destructor
881 if(ndef->get(n_old).has_after_destruct)
882 m_script->node_after_destruct(p, n_old);
883 // Air doesn't require constructor
887 std::set<u16> ServerEnvironment::getObjectsInsideRadius(v3f pos, float radius)
889 std::set<u16> objects;
890 for(std::map<u16, ServerActiveObject*>::iterator
891 i = m_active_objects.begin();
892 i != m_active_objects.end(); ++i)
894 ServerActiveObject* obj = i->second;
896 v3f objectpos = obj->getBasePosition();
897 if(objectpos.getDistanceFrom(pos) > radius)
904 void ServerEnvironment::clearAllObjects()
906 infostream<<"ServerEnvironment::clearAllObjects(): "
907 <<"Removing all active objects"<<std::endl;
908 std::list<u16> objects_to_remove;
909 for(std::map<u16, ServerActiveObject*>::iterator
910 i = m_active_objects.begin();
911 i != m_active_objects.end(); ++i)
913 ServerActiveObject* obj = i->second;
914 if(obj->getType() == ACTIVEOBJECT_TYPE_PLAYER)
917 // Delete static object if block is loaded
918 if(obj->m_static_exists){
919 MapBlock *block = m_map->getBlockNoCreateNoEx(obj->m_static_block);
921 block->m_static_objects.remove(id);
922 block->raiseModified(MOD_STATE_WRITE_NEEDED,
924 obj->m_static_exists = false;
927 // If known by some client, don't delete immediately
928 if(obj->m_known_by_count > 0){
929 obj->m_pending_deactivation = true;
930 obj->m_removed = true;
934 // Tell the object about removal
935 obj->removingFromEnvironment();
936 // Deregister in scripting api
937 m_script->removeObjectReference(obj);
939 // Delete active object
940 if(obj->environmentDeletes())
942 // Id to be removed from m_active_objects
943 objects_to_remove.push_back(id);
945 // Remove references from m_active_objects
946 for(std::list<u16>::iterator i = objects_to_remove.begin();
947 i != objects_to_remove.end(); ++i)
949 m_active_objects.erase(*i);
952 // Get list of loaded blocks
953 std::list<v3s16> loaded_blocks;
954 infostream<<"ServerEnvironment::clearAllObjects(): "
955 <<"Listing all loaded blocks"<<std::endl;
956 m_map->listAllLoadedBlocks(loaded_blocks);
957 infostream<<"ServerEnvironment::clearAllObjects(): "
958 <<"Done listing all loaded blocks: "
959 <<loaded_blocks.size()<<std::endl;
961 // Get list of loadable blocks
962 std::list<v3s16> loadable_blocks;
963 infostream<<"ServerEnvironment::clearAllObjects(): "
964 <<"Listing all loadable blocks"<<std::endl;
965 m_map->listAllLoadableBlocks(loadable_blocks);
966 infostream<<"ServerEnvironment::clearAllObjects(): "
967 <<"Done listing all loadable blocks: "
968 <<loadable_blocks.size()
969 <<", now clearing"<<std::endl;
971 // Grab a reference on each loaded block to avoid unloading it
972 for(std::list<v3s16>::iterator i = loaded_blocks.begin();
973 i != loaded_blocks.end(); ++i)
976 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
981 // Remove objects in all loadable blocks
982 u32 unload_interval = g_settings->getS32("max_clearobjects_extra_loaded_blocks");
983 unload_interval = MYMAX(unload_interval, 1);
984 u32 report_interval = loadable_blocks.size() / 10;
985 u32 num_blocks_checked = 0;
986 u32 num_blocks_cleared = 0;
987 u32 num_objs_cleared = 0;
988 for(std::list<v3s16>::iterator i = loadable_blocks.begin();
989 i != loadable_blocks.end(); ++i)
992 MapBlock *block = m_map->emergeBlock(p, false);
994 errorstream<<"ServerEnvironment::clearAllObjects(): "
995 <<"Failed to emerge block "<<PP(p)<<std::endl;
998 u32 num_stored = block->m_static_objects.m_stored.size();
999 u32 num_active = block->m_static_objects.m_active.size();
1000 if(num_stored != 0 || num_active != 0){
1001 block->m_static_objects.m_stored.clear();
1002 block->m_static_objects.m_active.clear();
1003 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1005 num_objs_cleared += num_stored + num_active;
1006 num_blocks_cleared++;
1008 num_blocks_checked++;
1010 if(num_blocks_checked % report_interval == 0){
1011 float percent = 100.0 * (float)num_blocks_checked /
1012 loadable_blocks.size();
1013 infostream<<"ServerEnvironment::clearAllObjects(): "
1014 <<"Cleared "<<num_objs_cleared<<" objects"
1015 <<" in "<<num_blocks_cleared<<" blocks ("
1016 <<percent<<"%)"<<std::endl;
1018 if(num_blocks_checked % unload_interval == 0){
1019 m_map->unloadUnreferencedBlocks();
1022 m_map->unloadUnreferencedBlocks();
1024 // Drop references that were added above
1025 for(std::list<v3s16>::iterator i = loaded_blocks.begin();
1026 i != loaded_blocks.end(); ++i)
1029 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1034 infostream<<"ServerEnvironment::clearAllObjects(): "
1035 <<"Finished: Cleared "<<num_objs_cleared<<" objects"
1036 <<" in "<<num_blocks_cleared<<" blocks"<<std::endl;
1039 void ServerEnvironment::step(float dtime)
1041 DSTACK(__FUNCTION_NAME);
1043 //TimeTaker timer("ServerEnv step");
1045 /* Step time of day */
1046 stepTimeOfDay(dtime);
1049 // NOTE: This is kind of funny on a singleplayer game, but doesn't
1050 // really matter that much.
1051 m_recommended_send_interval = g_settings->getFloat("dedicated_server_step");
1057 m_game_time_fraction_counter += dtime;
1058 u32 inc_i = (u32)m_game_time_fraction_counter;
1059 m_game_time += inc_i;
1060 m_game_time_fraction_counter -= (float)inc_i;
1067 ScopeProfiler sp(g_profiler, "SEnv: handle players avg", SPT_AVG);
1068 for(std::list<Player*>::iterator i = m_players.begin();
1069 i != m_players.end(); ++i)
1071 Player *player = *i;
1073 // Ignore disconnected players
1074 if(player->peer_id == 0)
1078 player->move(dtime, *m_map, 100*BS);
1083 Manage active block list
1085 if(m_active_blocks_management_interval.step(dtime, 2.0))
1087 ScopeProfiler sp(g_profiler, "SEnv: manage act. block list avg /2s", SPT_AVG);
1089 Get player block positions
1091 std::list<v3s16> players_blockpos;
1092 for(std::list<Player*>::iterator
1093 i = m_players.begin();
1094 i != m_players.end(); ++i)
1096 Player *player = *i;
1097 // Ignore disconnected players
1098 if(player->peer_id == 0)
1100 v3s16 blockpos = getNodeBlockPos(
1101 floatToInt(player->getPosition(), BS));
1102 players_blockpos.push_back(blockpos);
1106 Update list of active blocks, collecting changes
1108 const s16 active_block_range = g_settings->getS16("active_block_range");
1109 std::set<v3s16> blocks_removed;
1110 std::set<v3s16> blocks_added;
1111 m_active_blocks.update(players_blockpos, active_block_range,
1112 blocks_removed, blocks_added);
1115 Handle removed blocks
1118 // Convert active objects that are no more in active blocks to static
1119 deactivateFarObjects(false);
1121 for(std::set<v3s16>::iterator
1122 i = blocks_removed.begin();
1123 i != blocks_removed.end(); ++i)
1127 /* infostream<<"Server: Block " << PP(p)
1128 << " became inactive"<<std::endl; */
1130 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1134 // Set current time as timestamp (and let it set ChangedFlag)
1135 block->setTimestamp(m_game_time);
1142 for(std::set<v3s16>::iterator
1143 i = blocks_added.begin();
1144 i != blocks_added.end(); ++i)
1148 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1150 // Block needs to be fetched first
1151 m_emerger->enqueueBlockEmerge(
1152 PEER_ID_INEXISTENT, p, false);
1153 m_active_blocks.m_list.erase(p);
1157 activateBlock(block);
1158 /* infostream<<"Server: Block " << PP(p)
1159 << " became active"<<std::endl; */
1164 Mess around in active blocks
1166 if(m_active_blocks_nodemetadata_interval.step(dtime, 1.0))
1168 ScopeProfiler sp(g_profiler, "SEnv: mess in act. blocks avg /1s", SPT_AVG);
1172 for(std::set<v3s16>::iterator
1173 i = m_active_blocks.m_list.begin();
1174 i != m_active_blocks.m_list.end(); ++i)
1178 /*infostream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
1179 <<") being handled"<<std::endl;*/
1181 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1185 // Reset block usage timer
1186 block->resetUsageTimer();
1188 // Set current time as timestamp
1189 block->setTimestampNoChangedFlag(m_game_time);
1190 // If time has changed much from the one on disk,
1191 // set block to be saved when it is unloaded
1192 if(block->getTimestamp() > block->getDiskTimestamp() + 60)
1193 block->raiseModified(MOD_STATE_WRITE_AT_UNLOAD,
1194 "Timestamp older than 60s (step)");
1197 std::map<v3s16, NodeTimer> elapsed_timers =
1198 block->m_node_timers.step((float)dtime);
1199 if(!elapsed_timers.empty()){
1201 for(std::map<v3s16, NodeTimer>::iterator
1202 i = elapsed_timers.begin();
1203 i != elapsed_timers.end(); i++){
1204 n = block->getNodeNoEx(i->first);
1205 p = i->first + block->getPosRelative();
1206 if(m_script->node_on_timer(p,n,i->second.elapsed))
1207 block->setNodeTimer(i->first,NodeTimer(i->second.timeout,0));
1213 const float abm_interval = 1.0;
1214 if(m_active_block_modifier_interval.step(dtime, abm_interval))
1216 if(m_active_block_interval_overload_skip > 0){
1217 ScopeProfiler sp(g_profiler, "SEnv: ABM overload skips");
1218 m_active_block_interval_overload_skip--;
1221 ScopeProfiler sp(g_profiler, "SEnv: modify in blocks avg /1s", SPT_AVG);
1222 TimeTaker timer("modify in active blocks");
1224 // Initialize handling of ActiveBlockModifiers
1225 ABMHandler abmhandler(m_abms, abm_interval, this, true);
1227 for(std::set<v3s16>::iterator
1228 i = m_active_blocks.m_list.begin();
1229 i != m_active_blocks.m_list.end(); ++i)
1233 /*infostream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
1234 <<") being handled"<<std::endl;*/
1236 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1240 // Set current time as timestamp
1241 block->setTimestampNoChangedFlag(m_game_time);
1243 /* Handle ActiveBlockModifiers */
1244 abmhandler.apply(block);
1247 u32 time_ms = timer.stop(true);
1248 u32 max_time_ms = 200;
1249 if(time_ms > max_time_ms){
1250 infostream<<"WARNING: active block modifiers took "
1251 <<time_ms<<"ms (longer than "
1252 <<max_time_ms<<"ms)"<<std::endl;
1253 m_active_block_interval_overload_skip = (time_ms / max_time_ms) + 1;
1258 Step script environment (run global on_step())
1260 m_script->environment_Step(dtime);
1266 ScopeProfiler sp(g_profiler, "SEnv: step act. objs avg", SPT_AVG);
1267 //TimeTaker timer("Step active objects");
1269 g_profiler->avg("SEnv: num of objects", m_active_objects.size());
1271 // This helps the objects to send data at the same time
1272 bool send_recommended = false;
1273 m_send_recommended_timer += dtime;
1274 if(m_send_recommended_timer > getSendRecommendedInterval())
1276 m_send_recommended_timer -= getSendRecommendedInterval();
1277 send_recommended = true;
1280 for(std::map<u16, ServerActiveObject*>::iterator
1281 i = m_active_objects.begin();
1282 i != m_active_objects.end(); ++i)
1284 ServerActiveObject* obj = i->second;
1285 // Remove non-peaceful mobs on peaceful mode
1286 if(g_settings->getBool("only_peaceful_mobs")){
1287 if(!obj->isPeaceful())
1288 obj->m_removed = true;
1290 // Don't step if is to be removed or stored statically
1291 if(obj->m_removed || obj->m_pending_deactivation)
1294 obj->step(dtime, send_recommended);
1295 // Read messages from object
1296 while(!obj->m_messages_out.empty())
1298 m_active_object_messages.push_back(
1299 obj->m_messages_out.pop_front());
1305 Manage active objects
1307 if(m_object_management_interval.step(dtime, 0.5))
1309 ScopeProfiler sp(g_profiler, "SEnv: remove removed objs avg /.5s", SPT_AVG);
1311 Remove objects that satisfy (m_removed && m_known_by_count==0)
1313 removeRemovedObjects();
1317 ServerActiveObject* ServerEnvironment::getActiveObject(u16 id)
1319 std::map<u16, ServerActiveObject*>::iterator n;
1320 n = m_active_objects.find(id);
1321 if(n == m_active_objects.end())
1326 bool isFreeServerActiveObjectId(u16 id,
1327 std::map<u16, ServerActiveObject*> &objects)
1332 return objects.find(id) == objects.end();
1335 u16 getFreeServerActiveObjectId(
1336 std::map<u16, ServerActiveObject*> &objects)
1338 //try to reuse id's as late as possible
1339 static u16 last_used_id = 0;
1340 u16 startid = last_used_id;
1344 if(isFreeServerActiveObjectId(last_used_id, objects))
1345 return last_used_id;
1347 if(last_used_id == startid)
1352 u16 ServerEnvironment::addActiveObject(ServerActiveObject *object)
1355 u16 id = addActiveObjectRaw(object, true, 0);
1360 bool ServerEnvironment::addActiveObjectAsStatic(ServerActiveObject *obj)
1364 v3f objectpos = obj->getBasePosition();
1366 // The block in which the object resides in
1367 v3s16 blockpos_o = getNodeBlockPos(floatToInt(objectpos, BS));
1370 Update the static data
1373 // Create new static object
1374 std::string staticdata = obj->getStaticData();
1375 StaticObject s_obj(obj->getType(), objectpos, staticdata);
1376 // Add to the block where the object is located in
1377 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1378 // Get or generate the block
1379 MapBlock *block = m_map->emergeBlock(blockpos);
1381 bool succeeded = false;
1385 block->m_static_objects.insert(0, s_obj);
1386 block->raiseModified(MOD_STATE_WRITE_AT_UNLOAD,
1387 "addActiveObjectAsStatic");
1391 infostream<<"ServerEnvironment::addActiveObjectAsStatic: "
1392 <<"Could not find or generate "
1393 <<"a block for storing static object"<<std::endl;
1397 if(obj->environmentDeletes())
1405 Finds out what new objects have been added to
1406 inside a radius around a position
1408 void ServerEnvironment::getAddedActiveObjects(v3s16 pos, s16 radius,
1409 std::set<u16> ¤t_objects,
1410 std::set<u16> &added_objects)
1412 v3f pos_f = intToFloat(pos, BS);
1413 f32 radius_f = radius * BS;
1415 Go through the object list,
1416 - discard m_removed objects,
1417 - discard objects that are too far away,
1418 - discard objects that are found in current_objects.
1419 - add remaining objects to added_objects
1421 for(std::map<u16, ServerActiveObject*>::iterator
1422 i = m_active_objects.begin();
1423 i != m_active_objects.end(); ++i)
1427 ServerActiveObject *object = i->second;
1430 // Discard if removed
1431 if(object->m_removed)
1433 if(object->unlimitedTransferDistance() == false){
1434 // Discard if too far
1435 f32 distance_f = object->getBasePosition().getDistanceFrom(pos_f);
1436 if(distance_f > radius_f)
1439 // Discard if already on current_objects
1440 std::set<u16>::iterator n;
1441 n = current_objects.find(id);
1442 if(n != current_objects.end())
1444 // Add to added_objects
1445 added_objects.insert(id);
1450 Finds out what objects have been removed from
1451 inside a radius around a position
1453 void ServerEnvironment::getRemovedActiveObjects(v3s16 pos, s16 radius,
1454 std::set<u16> ¤t_objects,
1455 std::set<u16> &removed_objects)
1457 v3f pos_f = intToFloat(pos, BS);
1458 f32 radius_f = radius * BS;
1460 Go through current_objects; object is removed if:
1461 - object is not found in m_active_objects (this is actually an
1462 error condition; objects should be set m_removed=true and removed
1463 only after all clients have been informed about removal), or
1464 - object has m_removed=true, or
1465 - object is too far away
1467 for(std::set<u16>::iterator
1468 i = current_objects.begin();
1469 i != current_objects.end(); ++i)
1472 ServerActiveObject *object = getActiveObject(id);
1475 infostream<<"ServerEnvironment::getRemovedActiveObjects():"
1476 <<" object in current_objects is NULL"<<std::endl;
1477 removed_objects.insert(id);
1481 if(object->m_removed)
1483 removed_objects.insert(id);
1487 // If transfer distance is unlimited, don't remove
1488 if(object->unlimitedTransferDistance())
1491 f32 distance_f = object->getBasePosition().getDistanceFrom(pos_f);
1493 if(distance_f >= radius_f)
1495 removed_objects.insert(id);
1503 ActiveObjectMessage ServerEnvironment::getActiveObjectMessage()
1505 if(m_active_object_messages.empty())
1506 return ActiveObjectMessage(0);
1508 ActiveObjectMessage message = m_active_object_messages.front();
1509 m_active_object_messages.pop_front();
1514 ************ Private methods *************
1517 u16 ServerEnvironment::addActiveObjectRaw(ServerActiveObject *object,
1518 bool set_changed, u32 dtime_s)
1521 if(object->getId() == 0){
1522 u16 new_id = getFreeServerActiveObjectId(m_active_objects);
1525 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1526 <<"no free ids available"<<std::endl;
1527 if(object->environmentDeletes())
1531 object->setId(new_id);
1534 verbosestream<<"ServerEnvironment::addActiveObjectRaw(): "
1535 <<"supplied with id "<<object->getId()<<std::endl;
1537 if(isFreeServerActiveObjectId(object->getId(), m_active_objects) == false)
1539 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1540 <<"id is not free ("<<object->getId()<<")"<<std::endl;
1541 if(object->environmentDeletes())
1545 /*infostream<<"ServerEnvironment::addActiveObjectRaw(): "
1546 <<"added (id="<<object->getId()<<")"<<std::endl;*/
1548 m_active_objects[object->getId()] = object;
1550 verbosestream<<"ServerEnvironment::addActiveObjectRaw(): "
1551 <<"Added id="<<object->getId()<<"; there are now "
1552 <<m_active_objects.size()<<" active objects."
1555 // Register reference in scripting api (must be done before post-init)
1556 m_script->addObjectReference(object);
1557 // Post-initialize object
1558 object->addedToEnvironment(dtime_s);
1560 // Add static data to block
1561 if(object->isStaticAllowed())
1563 // Add static object to active static list of the block
1564 v3f objectpos = object->getBasePosition();
1565 std::string staticdata = object->getStaticData();
1566 StaticObject s_obj(object->getType(), objectpos, staticdata);
1567 // Add to the block where the object is located in
1568 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1569 MapBlock *block = m_map->getBlockNoCreateNoEx(blockpos);
1572 block->m_static_objects.m_active[object->getId()] = s_obj;
1573 object->m_static_exists = true;
1574 object->m_static_block = blockpos;
1577 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1578 "addActiveObjectRaw");
1581 v3s16 p = floatToInt(objectpos, BS);
1582 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1583 <<"could not find block for storing id="<<object->getId()
1584 <<" statically (pos="<<PP(p)<<")"<<std::endl;
1588 return object->getId();
1592 Remove objects that satisfy (m_removed && m_known_by_count==0)
1594 void ServerEnvironment::removeRemovedObjects()
1596 std::list<u16> objects_to_remove;
1597 for(std::map<u16, ServerActiveObject*>::iterator
1598 i = m_active_objects.begin();
1599 i != m_active_objects.end(); ++i)
1602 ServerActiveObject* obj = i->second;
1603 // This shouldn't happen but check it
1606 infostream<<"NULL object found in ServerEnvironment"
1607 <<" while finding removed objects. id="<<id<<std::endl;
1608 // Id to be removed from m_active_objects
1609 objects_to_remove.push_back(id);
1614 We will delete objects that are marked as removed or thatare
1615 waiting for deletion after deactivation
1617 if(obj->m_removed == false && obj->m_pending_deactivation == false)
1621 Delete static data from block if is marked as removed
1623 if(obj->m_static_exists && obj->m_removed)
1625 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
1627 block->m_static_objects.remove(id);
1628 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1629 "removeRemovedObjects");
1630 obj->m_static_exists = false;
1632 infostream << "failed to emerge block from which "
1633 "an object to be removed was loaded from. id="<<id<<std::endl;
1637 // If m_known_by_count > 0, don't actually remove.
1638 if(obj->m_known_by_count > 0)
1641 // Tell the object about removal
1642 obj->removingFromEnvironment();
1643 // Deregister in scripting api
1644 m_script->removeObjectReference(obj);
1647 if(obj->environmentDeletes())
1649 // Id to be removed from m_active_objects
1650 objects_to_remove.push_back(id);
1652 // Remove references from m_active_objects
1653 for(std::list<u16>::iterator i = objects_to_remove.begin();
1654 i != objects_to_remove.end(); ++i)
1656 m_active_objects.erase(*i);
1660 static void print_hexdump(std::ostream &o, const std::string &data)
1662 const int linelength = 16;
1663 for(int l=0; ; l++){
1664 int i0 = linelength * l;
1665 bool at_end = false;
1666 int thislinelength = linelength;
1667 if(i0 + thislinelength > (int)data.size()){
1668 thislinelength = data.size() - i0;
1671 for(int di=0; di<linelength; di++){
1674 if(di<thislinelength)
1675 snprintf(buf, 4, "%.2x ", data[i]);
1677 snprintf(buf, 4, " ");
1681 for(int di=0; di<thislinelength; di++){
1695 Convert stored objects from blocks near the players to active.
1697 void ServerEnvironment::activateObjects(MapBlock *block, u32 dtime_s)
1701 // Ignore if no stored objects (to not set changed flag)
1702 if(block->m_static_objects.m_stored.size() == 0)
1704 verbosestream<<"ServerEnvironment::activateObjects(): "
1705 <<"activating objects of block "<<PP(block->getPos())
1706 <<" ("<<block->m_static_objects.m_stored.size()
1707 <<" objects)"<<std::endl;
1708 bool large_amount = (block->m_static_objects.m_stored.size() > g_settings->getU16("max_objects_per_block"));
1710 errorstream<<"suspiciously large amount of objects detected: "
1711 <<block->m_static_objects.m_stored.size()<<" in "
1712 <<PP(block->getPos())
1713 <<"; removing all of them."<<std::endl;
1714 // Clear stored list
1715 block->m_static_objects.m_stored.clear();
1716 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1717 "stored list cleared in activateObjects due to "
1718 "large amount of objects");
1721 // A list for objects that couldn't be converted to active for some
1722 // reason. They will be stored back.
1723 std::list<StaticObject> new_stored;
1724 // Loop through stored static objects
1725 for(std::list<StaticObject>::iterator
1726 i = block->m_static_objects.m_stored.begin();
1727 i != block->m_static_objects.m_stored.end(); ++i)
1729 /*infostream<<"Server: Creating an active object from "
1730 <<"static data"<<std::endl;*/
1731 StaticObject &s_obj = *i;
1732 // Create an active object from the data
1733 ServerActiveObject *obj = ServerActiveObject::create
1734 (s_obj.type, this, 0, s_obj.pos, s_obj.data);
1735 // If couldn't create object, store static data back.
1738 errorstream<<"ServerEnvironment::activateObjects(): "
1739 <<"failed to create active object from static object "
1740 <<"in block "<<PP(s_obj.pos/BS)
1741 <<" type="<<(int)s_obj.type<<" data:"<<std::endl;
1742 print_hexdump(verbosestream, s_obj.data);
1744 new_stored.push_back(s_obj);
1747 verbosestream<<"ServerEnvironment::activateObjects(): "
1748 <<"activated static object pos="<<PP(s_obj.pos/BS)
1749 <<" type="<<(int)s_obj.type<<std::endl;
1750 // This will also add the object to the active static list
1751 addActiveObjectRaw(obj, false, dtime_s);
1753 // Clear stored list
1754 block->m_static_objects.m_stored.clear();
1755 // Add leftover failed stuff to stored list
1756 for(std::list<StaticObject>::iterator
1757 i = new_stored.begin();
1758 i != new_stored.end(); ++i)
1760 StaticObject &s_obj = *i;
1761 block->m_static_objects.m_stored.push_back(s_obj);
1764 Note: Block hasn't really been modified here.
1765 The objects have just been activated and moved from the stored
1766 static list to the active static list.
1767 As such, the block is essentially the same.
1768 Thus, do not call block->raiseModified(MOD_STATE_WRITE_NEEDED).
1769 Otherwise there would be a huge amount of unnecessary I/O.
1774 Convert objects that are not standing inside active blocks to static.
1776 If m_known_by_count != 0, active object is not deleted, but static
1777 data is still updated.
1779 If force_delete is set, active object is deleted nevertheless. It
1780 shall only be set so in the destructor of the environment.
1782 If block wasn't generated (not in memory or on disk),
1784 void ServerEnvironment::deactivateFarObjects(bool force_delete)
1786 std::list<u16> objects_to_remove;
1787 for(std::map<u16, ServerActiveObject*>::iterator
1788 i = m_active_objects.begin();
1789 i != m_active_objects.end(); ++i)
1791 ServerActiveObject* obj = i->second;
1794 // Do not deactivate if static data creation not allowed
1795 if(!force_delete && !obj->isStaticAllowed())
1798 // If pending deactivation, let removeRemovedObjects() do it
1799 if(!force_delete && obj->m_pending_deactivation)
1803 v3f objectpos = obj->getBasePosition();
1805 // The block in which the object resides in
1806 v3s16 blockpos_o = getNodeBlockPos(floatToInt(objectpos, BS));
1808 // If block is active, don't remove
1809 if(!force_delete && m_active_blocks.contains(blockpos_o))
1812 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
1813 <<"deactivating object id="<<id<<" on inactive block "
1814 <<PP(blockpos_o)<<std::endl;
1816 // If known by some client, don't immediately delete.
1817 bool pending_delete = (obj->m_known_by_count > 0 && !force_delete);
1820 Update the static data
1823 if(obj->isStaticAllowed())
1825 // Create new static object
1826 std::string staticdata_new = obj->getStaticData();
1827 StaticObject s_obj(obj->getType(), objectpos, staticdata_new);
1829 bool stays_in_same_block = false;
1830 bool data_changed = true;
1832 if(obj->m_static_exists){
1833 if(obj->m_static_block == blockpos_o)
1834 stays_in_same_block = true;
1836 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
1838 std::map<u16, StaticObject>::iterator n =
1839 block->m_static_objects.m_active.find(id);
1840 if(n != block->m_static_objects.m_active.end()){
1841 StaticObject static_old = n->second;
1843 float save_movem = obj->getMinimumSavedMovement();
1845 if(static_old.data == staticdata_new &&
1846 (static_old.pos - objectpos).getLength() < save_movem)
1847 data_changed = false;
1849 errorstream<<"ServerEnvironment::deactivateFarObjects(): "
1850 <<"id="<<id<<" m_static_exists=true but "
1851 <<"static data doesn't actually exist in "
1852 <<PP(obj->m_static_block)<<std::endl;
1856 bool shall_be_written = (!stays_in_same_block || data_changed);
1858 // Delete old static object
1859 if(obj->m_static_exists)
1861 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
1864 block->m_static_objects.remove(id);
1865 obj->m_static_exists = false;
1866 // Only mark block as modified if data changed considerably
1867 if(shall_be_written)
1868 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1869 "deactivateFarObjects: Static data "
1870 "changed considerably");
1874 // Add to the block where the object is located in
1875 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1876 // Get or generate the block
1877 MapBlock *block = NULL;
1879 block = m_map->emergeBlock(blockpos);
1880 } catch(InvalidPositionException &e){
1881 // Handled via NULL pointer
1886 if(block->m_static_objects.m_stored.size() >= g_settings->getU16("max_objects_per_block")){
1887 errorstream<<"ServerEnv: Trying to store id="<<obj->getId()
1888 <<" statically but block "<<PP(blockpos)
1889 <<" already contains "
1890 <<block->m_static_objects.m_stored.size()
1892 <<" Forcing delete."<<std::endl;
1893 force_delete = true;
1895 // If static counterpart already exists, remove it first.
1896 // This shouldn't happen, but happens rarely for some
1897 // unknown reason. Unsuccessful attempts have been made to
1898 // find said reason.
1899 if(id && block->m_static_objects.m_active.find(id) != block->m_static_objects.m_active.end()){
1900 infostream<<"ServerEnv: WARNING: Performing hack #83274"
1902 block->m_static_objects.remove(id);
1905 block->m_static_objects.insert(0, s_obj);
1907 // Only mark block as modified if data changed considerably
1908 if(shall_be_written)
1909 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1910 "deactivateFarObjects: Static data "
1911 "changed considerably");
1913 obj->m_static_exists = true;
1914 obj->m_static_block = block->getPos();
1919 v3s16 p = floatToInt(objectpos, BS);
1920 errorstream<<"ServerEnv: Could not find or generate "
1921 <<"a block for storing id="<<obj->getId()
1922 <<" statically (pos="<<PP(p)<<")"<<std::endl;
1929 If known by some client, set pending deactivation.
1930 Otherwise delete it immediately.
1933 if(pending_delete && !force_delete)
1935 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
1936 <<"object id="<<id<<" is known by clients"
1937 <<"; not deleting yet"<<std::endl;
1939 obj->m_pending_deactivation = true;
1943 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
1944 <<"object id="<<id<<" is not known by clients"
1945 <<"; deleting"<<std::endl;
1947 // Tell the object about removal
1948 obj->removingFromEnvironment();
1949 // Deregister in scripting api
1950 m_script->removeObjectReference(obj);
1952 // Delete active object
1953 if(obj->environmentDeletes())
1955 // Id to be removed from m_active_objects
1956 objects_to_remove.push_back(id);
1959 // Remove references from m_active_objects
1960 for(std::list<u16>::iterator i = objects_to_remove.begin();
1961 i != objects_to_remove.end(); ++i)
1963 m_active_objects.erase(*i);
1970 #include "clientsimpleobject.h"
1976 ClientEnvironment::ClientEnvironment(ClientMap *map, scene::ISceneManager *smgr,
1977 ITextureSource *texturesource, IGameDef *gamedef,
1978 IrrlichtDevice *irr):
1981 m_texturesource(texturesource),
1987 ClientEnvironment::~ClientEnvironment()
1989 // delete active objects
1990 for(std::map<u16, ClientActiveObject*>::iterator
1991 i = m_active_objects.begin();
1992 i != m_active_objects.end(); ++i)
1997 for(std::list<ClientSimpleObject*>::iterator
1998 i = m_simple_objects.begin(); i != m_simple_objects.end(); ++i)
2007 Map & ClientEnvironment::getMap()
2012 ClientMap & ClientEnvironment::getClientMap()
2017 void ClientEnvironment::addPlayer(Player *player)
2019 DSTACK(__FUNCTION_NAME);
2021 It is a failure if player is local and there already is a local
2024 assert(!(player->isLocal() == true && getLocalPlayer() != NULL));
2026 Environment::addPlayer(player);
2029 LocalPlayer * ClientEnvironment::getLocalPlayer()
2031 for(std::list<Player*>::iterator i = m_players.begin();
2032 i != m_players.end(); ++i)
2034 Player *player = *i;
2035 if(player->isLocal())
2036 return (LocalPlayer*)player;
2041 void ClientEnvironment::step(float dtime)
2043 DSTACK(__FUNCTION_NAME);
2045 /* Step time of day */
2046 stepTimeOfDay(dtime);
2048 // Get some settings
2049 bool fly_allowed = m_gamedef->checkLocalPrivilege("fly");
2050 bool free_move = fly_allowed && g_settings->getBool("free_move");
2053 LocalPlayer *lplayer = getLocalPlayer();
2055 // collision info queue
2056 std::list<CollisionInfo> player_collisions;
2059 Get the speed the player is going
2061 bool is_climbing = lplayer->is_climbing;
2063 f32 player_speed = lplayer->getSpeed().getLength();
2066 Maximum position increment
2068 //f32 position_max_increment = 0.05*BS;
2069 f32 position_max_increment = 0.1*BS;
2071 // Maximum time increment (for collision detection etc)
2072 // time = distance / speed
2073 f32 dtime_max_increment = 1;
2074 if(player_speed > 0.001)
2075 dtime_max_increment = position_max_increment / player_speed;
2077 // Maximum time increment is 10ms or lower
2078 if(dtime_max_increment > 0.01)
2079 dtime_max_increment = 0.01;
2081 // Don't allow overly huge dtime
2085 f32 dtime_downcount = dtime;
2088 Stuff that has a maximum time increment
2097 if(dtime_downcount > dtime_max_increment)
2099 dtime_part = dtime_max_increment;
2100 dtime_downcount -= dtime_part;
2104 dtime_part = dtime_downcount;
2106 Setting this to 0 (no -=dtime_part) disables an infinite loop
2107 when dtime_part is so small that dtime_downcount -= dtime_part
2110 dtime_downcount = 0;
2119 if(free_move == false && is_climbing == false)
2122 v3f speed = lplayer->getSpeed();
2123 if(lplayer->in_liquid == false)
2124 speed.Y -= lplayer->movement_gravity * lplayer->physics_override_gravity * dtime_part * 2;
2126 // Liquid floating / sinking
2127 if(lplayer->in_liquid && !lplayer->swimming_vertical)
2128 speed.Y -= lplayer->movement_liquid_sink * dtime_part * 2;
2130 // Liquid resistance
2131 if(lplayer->in_liquid_stable || lplayer->in_liquid)
2133 // How much the node's viscosity blocks movement, ranges between 0 and 1
2134 // Should match the scale at which viscosity increase affects other liquid attributes
2135 const f32 viscosity_factor = 0.3;
2137 v3f d_wanted = -speed / lplayer->movement_liquid_fluidity;
2138 f32 dl = d_wanted.getLength();
2139 if(dl > lplayer->movement_liquid_fluidity_smooth)
2140 dl = lplayer->movement_liquid_fluidity_smooth;
2141 dl *= (lplayer->liquid_viscosity * viscosity_factor) + (1 - viscosity_factor);
2143 v3f d = d_wanted.normalize() * dl;
2147 if(speed.X > lplayer->movement_liquid_fluidity + lplayer->movement_liquid_fluidity_smooth) speed.X -= lplayer->movement_liquid_fluidity_smooth;
2148 if(speed.X < -lplayer->movement_liquid_fluidity - lplayer->movement_liquid_fluidity_smooth) speed.X += lplayer->movement_liquid_fluidity_smooth;
2149 if(speed.Y > lplayer->movement_liquid_fluidity + lplayer->movement_liquid_fluidity_smooth) speed.Y -= lplayer->movement_liquid_fluidity_smooth;
2150 if(speed.Y < -lplayer->movement_liquid_fluidity - lplayer->movement_liquid_fluidity_smooth) speed.Y += lplayer->movement_liquid_fluidity_smooth;
2151 if(speed.Z > lplayer->movement_liquid_fluidity + lplayer->movement_liquid_fluidity_smooth) speed.Z -= lplayer->movement_liquid_fluidity_smooth;
2152 if(speed.Z < -lplayer->movement_liquid_fluidity - lplayer->movement_liquid_fluidity_smooth) speed.Z += lplayer->movement_liquid_fluidity_smooth;
2156 lplayer->setSpeed(speed);
2161 This also does collision detection.
2163 lplayer->move(dtime_part, this, position_max_increment,
2164 &player_collisions);
2167 while(dtime_downcount > 0.001);
2169 //std::cout<<"Looped "<<loopcount<<" times."<<std::endl;
2171 for(std::list<CollisionInfo>::iterator
2172 i = player_collisions.begin();
2173 i != player_collisions.end(); ++i)
2175 CollisionInfo &info = *i;
2176 v3f speed_diff = info.new_speed - info.old_speed;;
2177 // Handle only fall damage
2178 // (because otherwise walking against something in fast_move kills you)
2179 if(speed_diff.Y < 0 || info.old_speed.Y >= 0)
2181 // Get rid of other components
2184 f32 pre_factor = 1; // 1 hp per node/s
2185 f32 tolerance = BS*14; // 5 without damage
2186 f32 post_factor = 1; // 1 hp per node/s
2187 if(info.type == COLLISION_NODE)
2189 const ContentFeatures &f = m_gamedef->ndef()->
2190 get(m_map->getNodeNoEx(info.node_p));
2191 // Determine fall damage multiplier
2192 int addp = itemgroup_get(f.groups, "fall_damage_add_percent");
2193 pre_factor = 1.0 + (float)addp/100.0;
2195 float speed = pre_factor * speed_diff.getLength();
2196 if(speed > tolerance)
2198 f32 damage_f = (speed - tolerance)/BS * post_factor;
2199 u16 damage = (u16)(damage_f+0.5);
2201 damageLocalPlayer(damage, true);
2202 MtEvent *e = new SimpleTriggerEvent("PlayerFallingDamage");
2203 m_gamedef->event()->put(e);
2209 A quick draft of lava damage
2211 if(m_lava_hurt_interval.step(dtime, 1.0))
2213 v3f pf = lplayer->getPosition();
2215 // Feet, middle and head
2216 v3s16 p1 = floatToInt(pf + v3f(0, BS*0.1, 0), BS);
2217 MapNode n1 = m_map->getNodeNoEx(p1);
2218 v3s16 p2 = floatToInt(pf + v3f(0, BS*0.8, 0), BS);
2219 MapNode n2 = m_map->getNodeNoEx(p2);
2220 v3s16 p3 = floatToInt(pf + v3f(0, BS*1.6, 0), BS);
2221 MapNode n3 = m_map->getNodeNoEx(p3);
2223 u32 damage_per_second = 0;
2224 damage_per_second = MYMAX(damage_per_second,
2225 m_gamedef->ndef()->get(n1).damage_per_second);
2226 damage_per_second = MYMAX(damage_per_second,
2227 m_gamedef->ndef()->get(n2).damage_per_second);
2228 damage_per_second = MYMAX(damage_per_second,
2229 m_gamedef->ndef()->get(n3).damage_per_second);
2231 if(damage_per_second != 0)
2233 damageLocalPlayer(damage_per_second, true);
2240 if(m_drowning_interval.step(dtime, 2.0))
2242 v3f pf = lplayer->getPosition();
2245 v3s16 p = floatToInt(pf + v3f(0, BS*1.6, 0), BS);
2246 MapNode n = m_map->getNodeNoEx(p);
2247 ContentFeatures c = m_gamedef->ndef()->get(n);
2248 u8 drowning_damage = c.drowning;
2249 if(drowning_damage > 0 && lplayer->hp > 0){
2250 u16 breath = lplayer->getBreath();
2257 lplayer->setBreath(breath);
2258 updateLocalPlayerBreath(breath);
2261 if(lplayer->getBreath() == 0 && drowning_damage > 0){
2262 damageLocalPlayer(drowning_damage, true);
2265 if(m_breathing_interval.step(dtime, 0.5))
2267 v3f pf = lplayer->getPosition();
2270 v3s16 p = floatToInt(pf + v3f(0, BS*1.6, 0), BS);
2271 MapNode n = m_map->getNodeNoEx(p);
2272 ContentFeatures c = m_gamedef->ndef()->get(n);
2274 lplayer->setBreath(11);
2276 else if(c.drowning == 0){
2277 u16 breath = lplayer->getBreath();
2280 lplayer->setBreath(breath);
2281 updateLocalPlayerBreath(breath);
2287 Stuff that can be done in an arbitarily large dtime
2289 for(std::list<Player*>::iterator i = m_players.begin();
2290 i != m_players.end(); ++i)
2292 Player *player = *i;
2295 Handle non-local players
2297 if(player->isLocal() == false)
2300 player->move(dtime, *m_map, 100*BS);
2304 // Update lighting on all players on client
2308 v3s16 p = player->getLightPosition();
2309 MapNode n = m_map->getNode(p);
2310 light = n.getLightBlendF1((float)getDayNightRatio()/1000, m_gamedef->ndef());
2312 catch(InvalidPositionException &e){
2313 light = blend_light_f1((float)getDayNightRatio()/1000, LIGHT_SUN, 0);
2315 player->light = light;
2319 Step active objects and update lighting of them
2322 g_profiler->avg("CEnv: num of objects", m_active_objects.size());
2323 bool update_lighting = m_active_object_light_update_interval.step(dtime, 0.21);
2324 for(std::map<u16, ClientActiveObject*>::iterator
2325 i = m_active_objects.begin();
2326 i != m_active_objects.end(); ++i)
2328 ClientActiveObject* obj = i->second;
2330 obj->step(dtime, this);
2338 v3s16 p = obj->getLightPosition();
2339 MapNode n = m_map->getNode(p);
2340 light = n.getLightBlend(getDayNightRatio(), m_gamedef->ndef());
2342 catch(InvalidPositionException &e){
2343 light = blend_light(getDayNightRatio(), LIGHT_SUN, 0);
2345 obj->updateLight(light);
2350 Step and handle simple objects
2352 g_profiler->avg("CEnv: num of simple objects", m_simple_objects.size());
2353 for(std::list<ClientSimpleObject*>::iterator
2354 i = m_simple_objects.begin(); i != m_simple_objects.end();)
2356 ClientSimpleObject *simple = *i;
2357 std::list<ClientSimpleObject*>::iterator cur = i;
2359 simple->step(dtime);
2360 if(simple->m_to_be_removed){
2362 m_simple_objects.erase(cur);
2367 void ClientEnvironment::addSimpleObject(ClientSimpleObject *simple)
2369 m_simple_objects.push_back(simple);
2372 ClientActiveObject* ClientEnvironment::getActiveObject(u16 id)
2374 std::map<u16, ClientActiveObject*>::iterator n;
2375 n = m_active_objects.find(id);
2376 if(n == m_active_objects.end())
2381 bool isFreeClientActiveObjectId(u16 id,
2382 std::map<u16, ClientActiveObject*> &objects)
2387 return objects.find(id) == objects.end();
2390 u16 getFreeClientActiveObjectId(
2391 std::map<u16, ClientActiveObject*> &objects)
2393 //try to reuse id's as late as possible
2394 static u16 last_used_id = 0;
2395 u16 startid = last_used_id;
2399 if(isFreeClientActiveObjectId(last_used_id, objects))
2400 return last_used_id;
2402 if(last_used_id == startid)
2407 u16 ClientEnvironment::addActiveObject(ClientActiveObject *object)
2410 if(object->getId() == 0)
2412 u16 new_id = getFreeClientActiveObjectId(m_active_objects);
2415 infostream<<"ClientEnvironment::addActiveObject(): "
2416 <<"no free ids available"<<std::endl;
2420 object->setId(new_id);
2422 if(isFreeClientActiveObjectId(object->getId(), m_active_objects) == false)
2424 infostream<<"ClientEnvironment::addActiveObject(): "
2425 <<"id is not free ("<<object->getId()<<")"<<std::endl;
2429 infostream<<"ClientEnvironment::addActiveObject(): "
2430 <<"added (id="<<object->getId()<<")"<<std::endl;
2431 m_active_objects[object->getId()] = object;
2432 object->addToScene(m_smgr, m_texturesource, m_irr);
2433 { // Update lighting immediately
2437 v3s16 p = object->getLightPosition();
2438 MapNode n = m_map->getNode(p);
2439 light = n.getLightBlend(getDayNightRatio(), m_gamedef->ndef());
2441 catch(InvalidPositionException &e){
2442 light = blend_light(getDayNightRatio(), LIGHT_SUN, 0);
2444 object->updateLight(light);
2446 return object->getId();
2449 void ClientEnvironment::addActiveObject(u16 id, u8 type,
2450 const std::string &init_data)
2452 ClientActiveObject* obj =
2453 ClientActiveObject::create(type, m_gamedef, this);
2456 infostream<<"ClientEnvironment::addActiveObject(): "
2457 <<"id="<<id<<" type="<<type<<": Couldn't create object"
2466 obj->initialize(init_data);
2468 catch(SerializationError &e)
2470 errorstream<<"ClientEnvironment::addActiveObject():"
2471 <<" id="<<id<<" type="<<type
2472 <<": SerializationError in initialize(): "
2474 <<": init_data="<<serializeJsonString(init_data)
2478 addActiveObject(obj);
2481 void ClientEnvironment::removeActiveObject(u16 id)
2483 verbosestream<<"ClientEnvironment::removeActiveObject(): "
2484 <<"id="<<id<<std::endl;
2485 ClientActiveObject* obj = getActiveObject(id);
2488 infostream<<"ClientEnvironment::removeActiveObject(): "
2489 <<"id="<<id<<" not found"<<std::endl;
2492 obj->removeFromScene(true);
2494 m_active_objects.erase(id);
2497 void ClientEnvironment::processActiveObjectMessage(u16 id,
2498 const std::string &data)
2500 ClientActiveObject* obj = getActiveObject(id);
2503 infostream<<"ClientEnvironment::processActiveObjectMessage():"
2504 <<" got message for id="<<id<<", which doesn't exist."
2510 obj->processMessage(data);
2512 catch(SerializationError &e)
2514 errorstream<<"ClientEnvironment::processActiveObjectMessage():"
2515 <<" id="<<id<<" type="<<obj->getType()
2516 <<" SerializationError in processMessage(),"
2517 <<" message="<<serializeJsonString(data)
2523 Callbacks for activeobjects
2526 void ClientEnvironment::damageLocalPlayer(u8 damage, bool handle_hp)
2528 LocalPlayer *lplayer = getLocalPlayer();
2532 if(lplayer->hp > damage)
2533 lplayer->hp -= damage;
2538 ClientEnvEvent event;
2539 event.type = CEE_PLAYER_DAMAGE;
2540 event.player_damage.amount = damage;
2541 event.player_damage.send_to_server = handle_hp;
2542 m_client_event_queue.push_back(event);
2545 void ClientEnvironment::updateLocalPlayerBreath(u16 breath)
2547 ClientEnvEvent event;
2548 event.type = CEE_PLAYER_BREATH;
2549 event.player_breath.amount = breath;
2550 m_client_event_queue.push_back(event);
2554 Client likes to call these
2557 void ClientEnvironment::getActiveObjects(v3f origin, f32 max_d,
2558 std::vector<DistanceSortedActiveObject> &dest)
2560 for(std::map<u16, ClientActiveObject*>::iterator
2561 i = m_active_objects.begin();
2562 i != m_active_objects.end(); ++i)
2564 ClientActiveObject* obj = i->second;
2566 f32 d = (obj->getPosition() - origin).getLength();
2571 DistanceSortedActiveObject dso(obj, d);
2573 dest.push_back(dso);
2577 ClientEnvEvent ClientEnvironment::getClientEvent()
2579 ClientEnvEvent event;
2580 if(m_client_event_queue.empty())
2581 event.type = CEE_NONE;
2583 event = m_client_event_queue.front();
2584 m_client_event_queue.pop_front();
2589 #endif // #ifndef SERVER