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),
53 m_enable_day_night_ratio_override(false),
54 m_day_night_ratio_override(0.0f)
58 Environment::~Environment()
61 for(std::list<Player*>::iterator i = m_players.begin();
62 i != m_players.end(); ++i)
68 void Environment::addPlayer(Player *player)
70 DSTACK(__FUNCTION_NAME);
72 Check that peer_ids are unique.
73 Also check that names are unique.
74 Exception: there can be multiple players with peer_id=0
76 // If peer id is non-zero, it has to be unique.
77 if(player->peer_id != 0)
78 assert(getPlayer(player->peer_id) == NULL);
79 // Name has to be unique.
80 assert(getPlayer(player->getName()) == NULL);
82 m_players.push_back(player);
85 void Environment::removePlayer(u16 peer_id)
87 DSTACK(__FUNCTION_NAME);
89 for(std::list<Player*>::iterator i = m_players.begin();
90 i != m_players.end(); ++i)
93 if(player->peer_id != peer_id)
98 // See if there is an another one
99 // (shouldn't be, but just to be sure)
104 Player * Environment::getPlayer(u16 peer_id)
106 for(std::list<Player*>::iterator i = m_players.begin();
107 i != m_players.end(); ++i)
110 if(player->peer_id == peer_id)
116 Player * Environment::getPlayer(const char *name)
118 for(std::list<Player*>::iterator i = m_players.begin();
119 i != m_players.end(); ++i)
122 if(strcmp(player->getName(), name) == 0)
128 Player * Environment::getRandomConnectedPlayer()
130 std::list<Player*> connected_players = getPlayers(true);
131 u32 chosen_one = myrand() % connected_players.size();
133 for(std::list<Player*>::iterator
134 i = connected_players.begin();
135 i != connected_players.end(); ++i)
147 Player * Environment::getNearestConnectedPlayer(v3f pos)
149 std::list<Player*> connected_players = getPlayers(true);
151 Player *nearest_player = NULL;
152 for(std::list<Player*>::iterator
153 i = connected_players.begin();
154 i != connected_players.end(); ++i)
157 f32 d = player->getPosition().getDistanceFrom(pos);
158 if(d < nearest_d || nearest_player == NULL)
161 nearest_player = player;
164 return nearest_player;
167 std::list<Player*> Environment::getPlayers()
172 std::list<Player*> Environment::getPlayers(bool ignore_disconnected)
174 std::list<Player*> newlist;
175 for(std::list<Player*>::iterator
176 i = m_players.begin();
177 i != m_players.end(); ++i)
181 if(ignore_disconnected)
183 // Ignore disconnected players
184 if(player->peer_id == 0)
188 newlist.push_back(player);
193 u32 Environment::getDayNightRatio()
195 if(m_enable_day_night_ratio_override)
196 return m_day_night_ratio_override;
197 bool smooth = g_settings->getBool("enable_shaders");
198 return time_to_daynight_ratio(m_time_of_day_f*24000, smooth);
201 void Environment::stepTimeOfDay(float dtime)
203 m_time_counter += dtime;
204 f32 speed = m_time_of_day_speed * 24000./(24.*3600);
205 u32 units = (u32)(m_time_counter*speed);
206 m_time_counter -= (f32)units / speed;
210 if(m_time_of_day + units >= 24000)
212 m_time_of_day = (m_time_of_day + units) % 24000;
214 m_time_of_day_f = (float)m_time_of_day / 24000.0;
217 m_time_of_day_f += m_time_of_day_speed/24/3600*dtime;
218 if(m_time_of_day_f > 1.0)
219 m_time_of_day_f -= 1.0;
220 if(m_time_of_day_f < 0.0)
221 m_time_of_day_f += 1.0;
229 ABMWithState::ABMWithState(ActiveBlockModifier *abm_):
233 // Initialize timer to random value to spread processing
234 float itv = abm->getTriggerInterval();
235 itv = MYMAX(0.001, itv); // No less than 1ms
236 int minval = MYMAX(-0.51*itv, -60); // Clamp to
237 int maxval = MYMIN(0.51*itv, 60); // +-60 seconds
238 timer = myrand_range(minval, maxval);
245 void fillRadiusBlock(v3s16 p0, s16 r, std::set<v3s16> &list)
248 for(p.X=p0.X-r; p.X<=p0.X+r; p.X++)
249 for(p.Y=p0.Y-r; p.Y<=p0.Y+r; p.Y++)
250 for(p.Z=p0.Z-r; p.Z<=p0.Z+r; p.Z++)
257 void ActiveBlockList::update(std::list<v3s16> &active_positions,
259 std::set<v3s16> &blocks_removed,
260 std::set<v3s16> &blocks_added)
265 std::set<v3s16> newlist = m_forceloaded_list;
266 for(std::list<v3s16>::iterator i = active_positions.begin();
267 i != active_positions.end(); ++i)
269 fillRadiusBlock(*i, radius, newlist);
273 Find out which blocks on the old list are not on the new list
275 // Go through old list
276 for(std::set<v3s16>::iterator i = m_list.begin();
277 i != m_list.end(); ++i)
280 // If not on new list, it's been removed
281 if(newlist.find(p) == newlist.end())
282 blocks_removed.insert(p);
286 Find out which blocks on the new list are not on the old list
288 // Go through new list
289 for(std::set<v3s16>::iterator i = newlist.begin();
290 i != newlist.end(); ++i)
293 // If not on old list, it's been added
294 if(m_list.find(p) == m_list.end())
295 blocks_added.insert(p);
302 for(std::set<v3s16>::iterator i = newlist.begin();
303 i != newlist.end(); ++i)
314 ServerEnvironment::ServerEnvironment(ServerMap *map,
315 GameScripting *scriptIface, IGameDef *gamedef):
317 m_script(scriptIface),
319 m_random_spawn_timer(3),
320 m_send_recommended_timer(0),
321 m_active_block_interval_overload_skip(0),
323 m_game_time_fraction_counter(0),
324 m_recommended_send_interval(0.1),
325 m_max_lag_estimate(0.1)
327 m_use_weather = g_settings->getBool("weather");
330 ServerEnvironment::~ServerEnvironment()
332 // Clear active block list.
333 // This makes the next one delete all active objects.
334 m_active_blocks.clear();
336 // Convert all objects to static and delete the active objects
337 deactivateFarObjects(true);
342 // Delete ActiveBlockModifiers
343 for(std::list<ABMWithState>::iterator
344 i = m_abms.begin(); i != m_abms.end(); ++i){
349 Map & ServerEnvironment::getMap()
354 ServerMap & ServerEnvironment::getServerMap()
359 bool ServerEnvironment::line_of_sight(v3f pos1, v3f pos2, float stepsize, v3s16 *p)
361 float distance = pos1.getDistanceFrom(pos2);
363 //calculate normalized direction vector
364 v3f normalized_vector = v3f((pos2.X - pos1.X)/distance,
365 (pos2.Y - pos1.Y)/distance,
366 (pos2.Z - pos1.Z)/distance);
368 //find out if there's a node on path between pos1 and pos2
369 for (float i = 1; i < distance; i += stepsize) {
370 v3s16 pos = floatToInt(v3f(normalized_vector.X * i,
371 normalized_vector.Y * i,
372 normalized_vector.Z * i) +pos1,BS);
374 MapNode n = getMap().getNodeNoEx(pos);
376 if(n.param0 != CONTENT_AIR) {
386 void ServerEnvironment::serializePlayers(const std::string &savedir)
388 std::string players_path = savedir + "/players";
389 fs::CreateDir(players_path);
391 std::set<Player*> saved_players;
393 std::vector<fs::DirListNode> player_files = fs::GetDirListing(players_path);
394 for(u32 i=0; i<player_files.size(); i++)
396 if(player_files[i].dir || player_files[i].name[0] == '.')
399 // Full path to this file
400 std::string path = players_path + "/" + player_files[i].name;
402 //infostream<<"Checking player file "<<path<<std::endl;
404 // Load player to see what is its name
405 RemotePlayer testplayer(m_gamedef);
407 // Open file and deserialize
408 std::ifstream is(path.c_str(), std::ios_base::binary);
409 if(is.good() == false)
411 infostream<<"Failed to read "<<path<<std::endl;
414 testplayer.deSerialize(is, player_files[i].name);
417 //infostream<<"Loaded test player with name "<<testplayer.getName()<<std::endl;
419 // Search for the player
420 std::string playername = testplayer.getName();
421 Player *player = getPlayer(playername.c_str());
424 infostream<<"Didn't find matching player, ignoring file "<<path<<std::endl;
428 //infostream<<"Found matching player, overwriting."<<std::endl;
430 // OK, found. Save player there.
431 if(player->checkModified())
433 // Open file and serialize
434 std::ostringstream ss(std::ios_base::binary);
435 player->serialize(ss);
436 if(!fs::safeWriteToFile(path, ss.str()))
438 infostream<<"Failed to write "<<path<<std::endl;
441 saved_players.insert(player);
443 saved_players.insert(player);
447 for(std::list<Player*>::iterator i = m_players.begin();
448 i != m_players.end(); ++i)
451 if(saved_players.find(player) != saved_players.end())
453 /*infostream<<"Player "<<player->getName()
454 <<" was already saved."<<std::endl;*/
457 std::string playername = player->getName();
458 // Don't save unnamed player
461 //infostream<<"Not saving unnamed player."<<std::endl;
467 if(string_allowed(playername, PLAYERNAME_ALLOWED_CHARS) == false)
468 playername = "player";
469 std::string path = players_path + "/" + playername;
471 for(u32 i=0; i<1000; i++)
473 if(fs::PathExists(path) == false)
478 path = players_path + "/" + playername + itos(i);
482 infostream<<"Didn't find free file for player"<<std::endl;
487 /*infostream<<"Saving player "<<player->getName()<<" to "
489 // Open file and serialize
490 std::ostringstream ss(std::ios_base::binary);
491 player->serialize(ss);
492 if(!fs::safeWriteToFile(path, ss.str()))
494 infostream<<"Failed to write "<<path<<std::endl;
497 saved_players.insert(player);
501 //infostream<<"Saved "<<saved_players.size()<<" players."<<std::endl;
504 void ServerEnvironment::deSerializePlayers(const std::string &savedir)
506 std::string players_path = savedir + "/players";
508 std::vector<fs::DirListNode> player_files = fs::GetDirListing(players_path);
509 for(u32 i=0; i<player_files.size(); i++)
511 if(player_files[i].dir)
514 // Full path to this file
515 std::string path = players_path + "/" + player_files[i].name;
517 //infostream<<"Checking player file "<<path<<std::endl;
519 // Load player to see what is its name
520 RemotePlayer testplayer(m_gamedef);
522 // Open file and deserialize
523 std::ifstream is(path.c_str(), std::ios_base::binary);
524 if(is.good() == false)
526 infostream<<"Failed to read "<<path<<std::endl;
529 testplayer.deSerialize(is, player_files[i].name);
532 if(!string_allowed(testplayer.getName(), PLAYERNAME_ALLOWED_CHARS))
534 infostream<<"Not loading player with invalid name: "
535 <<testplayer.getName()<<std::endl;
538 /*infostream<<"Loaded test player with name "<<testplayer.getName()
541 // Search for the player
542 std::string playername = testplayer.getName();
543 Player *player = getPlayer(playername.c_str());
544 bool newplayer = false;
547 //infostream<<"Is a new player"<<std::endl;
548 player = new RemotePlayer(m_gamedef);
554 verbosestream<<"Reading player "<<testplayer.getName()<<" from "
556 // Open file and deserialize
557 std::ifstream is(path.c_str(), std::ios_base::binary);
558 if(is.good() == false)
560 infostream<<"Failed to read "<<path<<std::endl;
563 player->deSerialize(is, player_files[i].name);
573 void ServerEnvironment::saveMeta(const std::string &savedir)
575 std::string path = savedir + "/env_meta.txt";
577 // Open file and serialize
578 std::ostringstream ss(std::ios_base::binary);
581 args.setU64("game_time", m_game_time);
582 args.setU64("time_of_day", getTimeOfDay());
586 if(!fs::safeWriteToFile(path, ss.str()))
588 infostream<<"ServerEnvironment::saveMeta(): Failed to write "
590 throw SerializationError("Couldn't save env meta");
594 void ServerEnvironment::loadMeta(const std::string &savedir)
596 std::string path = savedir + "/env_meta.txt";
598 // Open file and deserialize
599 std::ifstream is(path.c_str(), std::ios_base::binary);
600 if(is.good() == false)
602 infostream<<"ServerEnvironment::loadMeta(): Failed to open "
604 throw SerializationError("Couldn't load env meta");
612 throw SerializationError
613 ("ServerEnvironment::loadMeta(): EnvArgsEnd not found");
615 std::getline(is, line);
616 std::string trimmedline = trim(line);
617 if(trimmedline == "EnvArgsEnd")
619 args.parseConfigLine(line);
623 m_game_time = args.getU64("game_time");
624 }catch(SettingNotFoundException &e){
625 // Getting this is crucial, otherwise timestamps are useless
626 throw SerializationError("Couldn't load env meta game_time");
630 m_time_of_day = args.getU64("time_of_day");
631 }catch(SettingNotFoundException &e){
632 // This is not as important
633 m_time_of_day = 9000;
639 ActiveBlockModifier *abm;
641 std::set<content_t> required_neighbors;
647 ServerEnvironment *m_env;
648 std::map<content_t, std::list<ActiveABM> > m_aabms;
650 ABMHandler(std::list<ABMWithState> &abms,
651 float dtime_s, ServerEnvironment *env,
657 INodeDefManager *ndef = env->getGameDef()->ndef();
658 for(std::list<ABMWithState>::iterator
659 i = abms.begin(); i != abms.end(); ++i){
660 ActiveBlockModifier *abm = i->abm;
661 float trigger_interval = abm->getTriggerInterval();
662 if(trigger_interval < 0.001)
663 trigger_interval = 0.001;
664 float actual_interval = dtime_s;
667 if(i->timer < trigger_interval)
669 i->timer -= trigger_interval;
670 actual_interval = trigger_interval;
672 float intervals = actual_interval / trigger_interval;
675 float chance = abm->getTriggerChance();
680 aabm.chance = chance / intervals;
684 std::set<std::string> required_neighbors_s
685 = abm->getRequiredNeighbors();
686 for(std::set<std::string>::iterator
687 i = required_neighbors_s.begin();
688 i != required_neighbors_s.end(); i++)
690 ndef->getIds(*i, aabm.required_neighbors);
693 std::set<std::string> contents_s = abm->getTriggerContents();
694 for(std::set<std::string>::iterator
695 i = contents_s.begin(); i != contents_s.end(); i++)
697 std::set<content_t> ids;
698 ndef->getIds(*i, ids);
699 for(std::set<content_t>::const_iterator k = ids.begin();
703 std::map<content_t, std::list<ActiveABM> >::iterator j;
705 if(j == m_aabms.end()){
706 std::list<ActiveABM> aabmlist;
707 m_aabms[c] = aabmlist;
710 j->second.push_back(aabm);
715 void apply(MapBlock *block)
720 ServerMap *map = &m_env->getServerMap();
723 for(p0.X=0; p0.X<MAP_BLOCKSIZE; p0.X++)
724 for(p0.Y=0; p0.Y<MAP_BLOCKSIZE; p0.Y++)
725 for(p0.Z=0; p0.Z<MAP_BLOCKSIZE; p0.Z++)
727 MapNode n = block->getNodeNoEx(p0);
728 content_t c = n.getContent();
729 v3s16 p = p0 + block->getPosRelative();
731 std::map<content_t, std::list<ActiveABM> >::iterator j;
733 if(j == m_aabms.end())
736 for(std::list<ActiveABM>::iterator
737 i = j->second.begin(); i != j->second.end(); i++)
739 if(myrand() % i->chance != 0)
743 if(!i->required_neighbors.empty())
746 for(p1.X = p.X-1; p1.X <= p.X+1; p1.X++)
747 for(p1.Y = p.Y-1; p1.Y <= p.Y+1; p1.Y++)
748 for(p1.Z = p.Z-1; p1.Z <= p.Z+1; p1.Z++)
752 MapNode n = map->getNodeNoEx(p1);
753 content_t c = n.getContent();
754 std::set<content_t>::const_iterator k;
755 k = i->required_neighbors.find(c);
756 if(k != i->required_neighbors.end()){
760 // No required neighbor found
765 // Find out how many objects the block contains
766 u32 active_object_count = block->m_static_objects.m_active.size();
767 // Find out how many objects this and all the neighbors contain
768 u32 active_object_count_wider = 0;
769 u32 wider_unknown_count = 0;
770 for(s16 x=-1; x<=1; x++)
771 for(s16 y=-1; y<=1; y++)
772 for(s16 z=-1; z<=1; z++)
774 MapBlock *block2 = map->getBlockNoCreateNoEx(
775 block->getPos() + v3s16(x,y,z));
777 wider_unknown_count = 0;
780 active_object_count_wider +=
781 block2->m_static_objects.m_active.size()
782 + block2->m_static_objects.m_stored.size();
785 u32 wider_known_count = 3*3*3 - wider_unknown_count;
786 active_object_count_wider += wider_unknown_count * active_object_count_wider / wider_known_count;
788 // Call all the trigger variations
789 i->abm->trigger(m_env, p, n);
790 i->abm->trigger(m_env, p, n,
791 active_object_count, active_object_count_wider);
797 void ServerEnvironment::activateBlock(MapBlock *block, u32 additional_dtime)
799 // Get time difference
801 u32 stamp = block->getTimestamp();
802 if(m_game_time > stamp && stamp != BLOCK_TIMESTAMP_UNDEFINED)
803 dtime_s = m_game_time - block->getTimestamp();
804 dtime_s += additional_dtime;
806 /*infostream<<"ServerEnvironment::activateBlock(): block timestamp: "
807 <<stamp<<", game time: "<<m_game_time<<std::endl;*/
809 // Set current time as timestamp
810 block->setTimestampNoChangedFlag(m_game_time);
812 /*infostream<<"ServerEnvironment::activateBlock(): block is "
813 <<dtime_s<<" seconds old."<<std::endl;*/
815 // Activate stored objects
816 activateObjects(block, dtime_s);
819 std::map<v3s16, NodeTimer> elapsed_timers =
820 block->m_node_timers.step((float)dtime_s);
821 if(!elapsed_timers.empty()){
823 for(std::map<v3s16, NodeTimer>::iterator
824 i = elapsed_timers.begin();
825 i != elapsed_timers.end(); i++){
826 n = block->getNodeNoEx(i->first);
827 v3s16 p = i->first + block->getPosRelative();
828 if(m_script->node_on_timer(p,n,i->second.elapsed))
829 block->setNodeTimer(i->first,NodeTimer(i->second.timeout,0));
833 /* Handle ActiveBlockModifiers */
834 ABMHandler abmhandler(m_abms, dtime_s, this, false);
835 abmhandler.apply(block);
838 void ServerEnvironment::addActiveBlockModifier(ActiveBlockModifier *abm)
840 m_abms.push_back(ABMWithState(abm));
843 bool ServerEnvironment::setNode(v3s16 p, const MapNode &n)
845 INodeDefManager *ndef = m_gamedef->ndef();
846 MapNode n_old = m_map->getNodeNoEx(p);
848 if(ndef->get(n_old).has_on_destruct)
849 m_script->node_on_destruct(p, n_old);
851 bool succeeded = m_map->addNodeWithEvent(p, n);
854 // Call post-destructor
855 if(ndef->get(n_old).has_after_destruct)
856 m_script->node_after_destruct(p, n_old);
858 if(ndef->get(n).has_on_construct)
859 m_script->node_on_construct(p, n);
863 bool ServerEnvironment::removeNode(v3s16 p)
865 INodeDefManager *ndef = m_gamedef->ndef();
866 MapNode n_old = m_map->getNodeNoEx(p);
868 if(ndef->get(n_old).has_on_destruct)
869 m_script->node_on_destruct(p, n_old);
871 // This is slightly optimized compared to addNodeWithEvent(air)
872 bool succeeded = m_map->removeNodeWithEvent(p);
875 // Call post-destructor
876 if(ndef->get(n_old).has_after_destruct)
877 m_script->node_after_destruct(p, n_old);
878 // Air doesn't require constructor
882 bool ServerEnvironment::swapNode(v3s16 p, const MapNode &n)
884 return m_map->addNodeWithEvent(p, n, false);
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(report_interval != 0 &&
1011 num_blocks_checked % report_interval == 0){
1012 float percent = 100.0 * (float)num_blocks_checked /
1013 loadable_blocks.size();
1014 infostream<<"ServerEnvironment::clearAllObjects(): "
1015 <<"Cleared "<<num_objs_cleared<<" objects"
1016 <<" in "<<num_blocks_cleared<<" blocks ("
1017 <<percent<<"%)"<<std::endl;
1019 if(num_blocks_checked % unload_interval == 0){
1020 m_map->unloadUnreferencedBlocks();
1023 m_map->unloadUnreferencedBlocks();
1025 // Drop references that were added above
1026 for(std::list<v3s16>::iterator i = loaded_blocks.begin();
1027 i != loaded_blocks.end(); ++i)
1030 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1035 infostream<<"ServerEnvironment::clearAllObjects(): "
1036 <<"Finished: Cleared "<<num_objs_cleared<<" objects"
1037 <<" in "<<num_blocks_cleared<<" blocks"<<std::endl;
1040 void ServerEnvironment::step(float dtime)
1042 DSTACK(__FUNCTION_NAME);
1044 //TimeTaker timer("ServerEnv step");
1046 /* Step time of day */
1047 stepTimeOfDay(dtime);
1050 // NOTE: This is kind of funny on a singleplayer game, but doesn't
1051 // really matter that much.
1052 m_recommended_send_interval = g_settings->getFloat("dedicated_server_step");
1058 m_game_time_fraction_counter += dtime;
1059 u32 inc_i = (u32)m_game_time_fraction_counter;
1060 m_game_time += inc_i;
1061 m_game_time_fraction_counter -= (float)inc_i;
1068 ScopeProfiler sp(g_profiler, "SEnv: handle players avg", SPT_AVG);
1069 for(std::list<Player*>::iterator i = m_players.begin();
1070 i != m_players.end(); ++i)
1072 Player *player = *i;
1074 // Ignore disconnected players
1075 if(player->peer_id == 0)
1079 player->move(dtime, *m_map, 100*BS);
1084 Manage active block list
1086 if(m_active_blocks_management_interval.step(dtime, 2.0))
1088 ScopeProfiler sp(g_profiler, "SEnv: manage act. block list avg /2s", SPT_AVG);
1090 Get player block positions
1092 std::list<v3s16> players_blockpos;
1093 for(std::list<Player*>::iterator
1094 i = m_players.begin();
1095 i != m_players.end(); ++i)
1097 Player *player = *i;
1098 // Ignore disconnected players
1099 if(player->peer_id == 0)
1101 v3s16 blockpos = getNodeBlockPos(
1102 floatToInt(player->getPosition(), BS));
1103 players_blockpos.push_back(blockpos);
1107 Update list of active blocks, collecting changes
1109 const s16 active_block_range = g_settings->getS16("active_block_range");
1110 std::set<v3s16> blocks_removed;
1111 std::set<v3s16> blocks_added;
1112 m_active_blocks.update(players_blockpos, active_block_range,
1113 blocks_removed, blocks_added);
1116 Handle removed blocks
1119 // Convert active objects that are no more in active blocks to static
1120 deactivateFarObjects(false);
1122 for(std::set<v3s16>::iterator
1123 i = blocks_removed.begin();
1124 i != blocks_removed.end(); ++i)
1128 /* infostream<<"Server: Block " << PP(p)
1129 << " became inactive"<<std::endl; */
1131 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1135 // Set current time as timestamp (and let it set ChangedFlag)
1136 block->setTimestamp(m_game_time);
1143 for(std::set<v3s16>::iterator
1144 i = blocks_added.begin();
1145 i != blocks_added.end(); ++i)
1149 MapBlock *block = m_map->getBlockOrEmerge(p);
1151 m_active_blocks.m_list.erase(p);
1155 activateBlock(block);
1156 /* infostream<<"Server: Block " << PP(p)
1157 << " became active"<<std::endl; */
1162 Mess around in active blocks
1164 if(m_active_blocks_nodemetadata_interval.step(dtime, 1.0))
1166 ScopeProfiler sp(g_profiler, "SEnv: mess in act. blocks avg /1s", SPT_AVG);
1170 for(std::set<v3s16>::iterator
1171 i = m_active_blocks.m_list.begin();
1172 i != m_active_blocks.m_list.end(); ++i)
1176 /*infostream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
1177 <<") being handled"<<std::endl;*/
1179 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1183 // Reset block usage timer
1184 block->resetUsageTimer();
1186 // Set current time as timestamp
1187 block->setTimestampNoChangedFlag(m_game_time);
1188 // If time has changed much from the one on disk,
1189 // set block to be saved when it is unloaded
1190 if(block->getTimestamp() > block->getDiskTimestamp() + 60)
1191 block->raiseModified(MOD_STATE_WRITE_AT_UNLOAD,
1192 "Timestamp older than 60s (step)");
1195 std::map<v3s16, NodeTimer> elapsed_timers =
1196 block->m_node_timers.step((float)dtime);
1197 if(!elapsed_timers.empty()){
1199 for(std::map<v3s16, NodeTimer>::iterator
1200 i = elapsed_timers.begin();
1201 i != elapsed_timers.end(); i++){
1202 n = block->getNodeNoEx(i->first);
1203 p = i->first + block->getPosRelative();
1204 if(m_script->node_on_timer(p,n,i->second.elapsed))
1205 block->setNodeTimer(i->first,NodeTimer(i->second.timeout,0));
1211 const float abm_interval = 1.0;
1212 if(m_active_block_modifier_interval.step(dtime, abm_interval))
1214 if(m_active_block_interval_overload_skip > 0){
1215 ScopeProfiler sp(g_profiler, "SEnv: ABM overload skips");
1216 m_active_block_interval_overload_skip--;
1219 ScopeProfiler sp(g_profiler, "SEnv: modify in blocks avg /1s", SPT_AVG);
1220 TimeTaker timer("modify in active blocks");
1222 // Initialize handling of ActiveBlockModifiers
1223 ABMHandler abmhandler(m_abms, abm_interval, this, true);
1225 for(std::set<v3s16>::iterator
1226 i = m_active_blocks.m_list.begin();
1227 i != m_active_blocks.m_list.end(); ++i)
1231 /*infostream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
1232 <<") being handled"<<std::endl;*/
1234 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1238 // Set current time as timestamp
1239 block->setTimestampNoChangedFlag(m_game_time);
1241 /* Handle ActiveBlockModifiers */
1242 abmhandler.apply(block);
1245 u32 time_ms = timer.stop(true);
1246 u32 max_time_ms = 200;
1247 if(time_ms > max_time_ms){
1248 infostream<<"WARNING: active block modifiers took "
1249 <<time_ms<<"ms (longer than "
1250 <<max_time_ms<<"ms)"<<std::endl;
1251 m_active_block_interval_overload_skip = (time_ms / max_time_ms) + 1;
1256 Step script environment (run global on_step())
1258 m_script->environment_Step(dtime);
1264 ScopeProfiler sp(g_profiler, "SEnv: step act. objs avg", SPT_AVG);
1265 //TimeTaker timer("Step active objects");
1267 g_profiler->avg("SEnv: num of objects", m_active_objects.size());
1269 // This helps the objects to send data at the same time
1270 bool send_recommended = false;
1271 m_send_recommended_timer += dtime;
1272 if(m_send_recommended_timer > getSendRecommendedInterval())
1274 m_send_recommended_timer -= getSendRecommendedInterval();
1275 send_recommended = true;
1278 for(std::map<u16, ServerActiveObject*>::iterator
1279 i = m_active_objects.begin();
1280 i != m_active_objects.end(); ++i)
1282 ServerActiveObject* obj = i->second;
1283 // Remove non-peaceful mobs on peaceful mode
1284 if(g_settings->getBool("only_peaceful_mobs")){
1285 if(!obj->isPeaceful())
1286 obj->m_removed = true;
1288 // Don't step if is to be removed or stored statically
1289 if(obj->m_removed || obj->m_pending_deactivation)
1292 obj->step(dtime, send_recommended);
1293 // Read messages from object
1294 while(!obj->m_messages_out.empty())
1296 m_active_object_messages.push_back(
1297 obj->m_messages_out.pop_front());
1303 Manage active objects
1305 if(m_object_management_interval.step(dtime, 0.5))
1307 ScopeProfiler sp(g_profiler, "SEnv: remove removed objs avg /.5s", SPT_AVG);
1309 Remove objects that satisfy (m_removed && m_known_by_count==0)
1311 removeRemovedObjects();
1315 ServerActiveObject* ServerEnvironment::getActiveObject(u16 id)
1317 std::map<u16, ServerActiveObject*>::iterator n;
1318 n = m_active_objects.find(id);
1319 if(n == m_active_objects.end())
1324 bool isFreeServerActiveObjectId(u16 id,
1325 std::map<u16, ServerActiveObject*> &objects)
1330 return objects.find(id) == objects.end();
1333 u16 getFreeServerActiveObjectId(
1334 std::map<u16, ServerActiveObject*> &objects)
1336 //try to reuse id's as late as possible
1337 static u16 last_used_id = 0;
1338 u16 startid = last_used_id;
1342 if(isFreeServerActiveObjectId(last_used_id, objects))
1343 return last_used_id;
1345 if(last_used_id == startid)
1350 u16 ServerEnvironment::addActiveObject(ServerActiveObject *object)
1353 u16 id = addActiveObjectRaw(object, true, 0);
1358 bool ServerEnvironment::addActiveObjectAsStatic(ServerActiveObject *obj)
1362 v3f objectpos = obj->getBasePosition();
1364 // The block in which the object resides in
1365 v3s16 blockpos_o = getNodeBlockPos(floatToInt(objectpos, BS));
1368 Update the static data
1371 // Create new static object
1372 std::string staticdata = obj->getStaticData();
1373 StaticObject s_obj(obj->getType(), objectpos, staticdata);
1374 // Add to the block where the object is located in
1375 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1376 // Get or generate the block
1377 MapBlock *block = m_map->emergeBlock(blockpos);
1379 bool succeeded = false;
1383 block->m_static_objects.insert(0, s_obj);
1384 block->raiseModified(MOD_STATE_WRITE_AT_UNLOAD,
1385 "addActiveObjectAsStatic");
1389 infostream<<"ServerEnvironment::addActiveObjectAsStatic: "
1390 <<"Could not find or generate "
1391 <<"a block for storing static object"<<std::endl;
1395 if(obj->environmentDeletes())
1403 Finds out what new objects have been added to
1404 inside a radius around a position
1406 void ServerEnvironment::getAddedActiveObjects(v3s16 pos, s16 radius,
1407 std::set<u16> ¤t_objects,
1408 std::set<u16> &added_objects)
1410 v3f pos_f = intToFloat(pos, BS);
1411 f32 radius_f = radius * BS;
1413 Go through the object list,
1414 - discard m_removed objects,
1415 - discard objects that are too far away,
1416 - discard objects that are found in current_objects.
1417 - add remaining objects to added_objects
1419 for(std::map<u16, ServerActiveObject*>::iterator
1420 i = m_active_objects.begin();
1421 i != m_active_objects.end(); ++i)
1425 ServerActiveObject *object = i->second;
1428 // Discard if removed or deactivating
1429 if(object->m_removed || object->m_pending_deactivation)
1431 if(object->unlimitedTransferDistance() == false){
1432 // Discard if too far
1433 f32 distance_f = object->getBasePosition().getDistanceFrom(pos_f);
1434 if(distance_f > radius_f)
1437 // Discard if already on current_objects
1438 std::set<u16>::iterator n;
1439 n = current_objects.find(id);
1440 if(n != current_objects.end())
1442 // Add to added_objects
1443 added_objects.insert(id);
1448 Finds out what objects have been removed from
1449 inside a radius around a position
1451 void ServerEnvironment::getRemovedActiveObjects(v3s16 pos, s16 radius,
1452 std::set<u16> ¤t_objects,
1453 std::set<u16> &removed_objects)
1455 v3f pos_f = intToFloat(pos, BS);
1456 f32 radius_f = radius * BS;
1458 Go through current_objects; object is removed if:
1459 - object is not found in m_active_objects (this is actually an
1460 error condition; objects should be set m_removed=true and removed
1461 only after all clients have been informed about removal), or
1462 - object has m_removed=true, or
1463 - object is too far away
1465 for(std::set<u16>::iterator
1466 i = current_objects.begin();
1467 i != current_objects.end(); ++i)
1470 ServerActiveObject *object = getActiveObject(id);
1473 infostream<<"ServerEnvironment::getRemovedActiveObjects():"
1474 <<" object in current_objects is NULL"<<std::endl;
1475 removed_objects.insert(id);
1479 if(object->m_removed || object->m_pending_deactivation)
1481 removed_objects.insert(id);
1485 // If transfer distance is unlimited, don't remove
1486 if(object->unlimitedTransferDistance())
1489 f32 distance_f = object->getBasePosition().getDistanceFrom(pos_f);
1491 if(distance_f >= radius_f)
1493 removed_objects.insert(id);
1501 ActiveObjectMessage ServerEnvironment::getActiveObjectMessage()
1503 if(m_active_object_messages.empty())
1504 return ActiveObjectMessage(0);
1506 ActiveObjectMessage message = m_active_object_messages.front();
1507 m_active_object_messages.pop_front();
1512 ************ Private methods *************
1515 u16 ServerEnvironment::addActiveObjectRaw(ServerActiveObject *object,
1516 bool set_changed, u32 dtime_s)
1519 if(object->getId() == 0){
1520 u16 new_id = getFreeServerActiveObjectId(m_active_objects);
1523 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1524 <<"no free ids available"<<std::endl;
1525 if(object->environmentDeletes())
1529 object->setId(new_id);
1532 verbosestream<<"ServerEnvironment::addActiveObjectRaw(): "
1533 <<"supplied with id "<<object->getId()<<std::endl;
1535 if(isFreeServerActiveObjectId(object->getId(), m_active_objects) == false)
1537 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1538 <<"id is not free ("<<object->getId()<<")"<<std::endl;
1539 if(object->environmentDeletes())
1543 /*infostream<<"ServerEnvironment::addActiveObjectRaw(): "
1544 <<"added (id="<<object->getId()<<")"<<std::endl;*/
1546 m_active_objects[object->getId()] = object;
1548 verbosestream<<"ServerEnvironment::addActiveObjectRaw(): "
1549 <<"Added id="<<object->getId()<<"; there are now "
1550 <<m_active_objects.size()<<" active objects."
1553 // Register reference in scripting api (must be done before post-init)
1554 m_script->addObjectReference(object);
1555 // Post-initialize object
1556 object->addedToEnvironment(dtime_s);
1558 // Add static data to block
1559 if(object->isStaticAllowed())
1561 // Add static object to active static list of the block
1562 v3f objectpos = object->getBasePosition();
1563 std::string staticdata = object->getStaticData();
1564 StaticObject s_obj(object->getType(), objectpos, staticdata);
1565 // Add to the block where the object is located in
1566 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1567 MapBlock *block = m_map->emergeBlock(blockpos);
1569 block->m_static_objects.m_active[object->getId()] = s_obj;
1570 object->m_static_exists = true;
1571 object->m_static_block = blockpos;
1574 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1575 "addActiveObjectRaw");
1577 v3s16 p = floatToInt(objectpos, BS);
1578 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1579 <<"could not emerge block for storing id="<<object->getId()
1580 <<" statically (pos="<<PP(p)<<")"<<std::endl;
1584 return object->getId();
1588 Remove objects that satisfy (m_removed && m_known_by_count==0)
1590 void ServerEnvironment::removeRemovedObjects()
1592 std::list<u16> objects_to_remove;
1593 for(std::map<u16, ServerActiveObject*>::iterator
1594 i = m_active_objects.begin();
1595 i != m_active_objects.end(); ++i)
1598 ServerActiveObject* obj = i->second;
1599 // This shouldn't happen but check it
1602 infostream<<"NULL object found in ServerEnvironment"
1603 <<" while finding removed objects. id="<<id<<std::endl;
1604 // Id to be removed from m_active_objects
1605 objects_to_remove.push_back(id);
1610 We will delete objects that are marked as removed or thatare
1611 waiting for deletion after deactivation
1613 if(obj->m_removed == false && obj->m_pending_deactivation == false)
1617 Delete static data from block if is marked as removed
1619 if(obj->m_static_exists && obj->m_removed)
1621 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
1623 block->m_static_objects.remove(id);
1624 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1625 "removeRemovedObjects/remove");
1626 obj->m_static_exists = false;
1628 infostream<<"Failed to emerge block from which an object to "
1629 <<"be removed was loaded from. id="<<id<<std::endl;
1633 // If m_known_by_count > 0, don't actually remove. On some future
1634 // invocation this will be 0, which is when removal will continue.
1635 if(obj->m_known_by_count > 0)
1639 Move static data from active to stored if not marked as removed
1641 if(obj->m_static_exists && !obj->m_removed){
1642 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
1644 std::map<u16, StaticObject>::iterator i =
1645 block->m_static_objects.m_active.find(id);
1646 if(i != block->m_static_objects.m_active.end()){
1647 block->m_static_objects.m_stored.push_back(i->second);
1648 block->m_static_objects.m_active.erase(id);
1649 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1650 "removeRemovedObjects/deactivate");
1653 infostream<<"Failed to emerge block from which an object to "
1654 <<"be deactivated was loaded from. id="<<id<<std::endl;
1658 // Tell the object about removal
1659 obj->removingFromEnvironment();
1660 // Deregister in scripting api
1661 m_script->removeObjectReference(obj);
1664 if(obj->environmentDeletes())
1666 // Id to be removed from m_active_objects
1667 objects_to_remove.push_back(id);
1669 // Remove references from m_active_objects
1670 for(std::list<u16>::iterator i = objects_to_remove.begin();
1671 i != objects_to_remove.end(); ++i)
1673 m_active_objects.erase(*i);
1677 static void print_hexdump(std::ostream &o, const std::string &data)
1679 const int linelength = 16;
1680 for(int l=0; ; l++){
1681 int i0 = linelength * l;
1682 bool at_end = false;
1683 int thislinelength = linelength;
1684 if(i0 + thislinelength > (int)data.size()){
1685 thislinelength = data.size() - i0;
1688 for(int di=0; di<linelength; di++){
1691 if(di<thislinelength)
1692 snprintf(buf, 4, "%.2x ", data[i]);
1694 snprintf(buf, 4, " ");
1698 for(int di=0; di<thislinelength; di++){
1712 Convert stored objects from blocks near the players to active.
1714 void ServerEnvironment::activateObjects(MapBlock *block, u32 dtime_s)
1718 // Ignore if no stored objects (to not set changed flag)
1719 if(block->m_static_objects.m_stored.size() == 0)
1721 verbosestream<<"ServerEnvironment::activateObjects(): "
1722 <<"activating objects of block "<<PP(block->getPos())
1723 <<" ("<<block->m_static_objects.m_stored.size()
1724 <<" objects)"<<std::endl;
1725 bool large_amount = (block->m_static_objects.m_stored.size() > g_settings->getU16("max_objects_per_block"));
1727 errorstream<<"suspiciously large amount of objects detected: "
1728 <<block->m_static_objects.m_stored.size()<<" in "
1729 <<PP(block->getPos())
1730 <<"; removing all of them."<<std::endl;
1731 // Clear stored list
1732 block->m_static_objects.m_stored.clear();
1733 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1734 "stored list cleared in activateObjects due to "
1735 "large amount of objects");
1739 // Activate stored objects
1740 std::list<StaticObject> new_stored;
1741 for(std::list<StaticObject>::iterator
1742 i = block->m_static_objects.m_stored.begin();
1743 i != block->m_static_objects.m_stored.end(); ++i)
1745 /*infostream<<"Server: Creating an active object from "
1746 <<"static data"<<std::endl;*/
1747 StaticObject &s_obj = *i;
1748 // Create an active object from the data
1749 ServerActiveObject *obj = ServerActiveObject::create
1750 (s_obj.type, this, 0, s_obj.pos, s_obj.data);
1751 // If couldn't create object, store static data back.
1754 errorstream<<"ServerEnvironment::activateObjects(): "
1755 <<"failed to create active object from static object "
1756 <<"in block "<<PP(s_obj.pos/BS)
1757 <<" type="<<(int)s_obj.type<<" data:"<<std::endl;
1758 print_hexdump(verbosestream, s_obj.data);
1760 new_stored.push_back(s_obj);
1763 verbosestream<<"ServerEnvironment::activateObjects(): "
1764 <<"activated static object pos="<<PP(s_obj.pos/BS)
1765 <<" type="<<(int)s_obj.type<<std::endl;
1766 // This will also add the object to the active static list
1767 addActiveObjectRaw(obj, false, dtime_s);
1769 // Clear stored list
1770 block->m_static_objects.m_stored.clear();
1771 // Add leftover failed stuff to stored list
1772 for(std::list<StaticObject>::iterator
1773 i = new_stored.begin();
1774 i != new_stored.end(); ++i)
1776 StaticObject &s_obj = *i;
1777 block->m_static_objects.m_stored.push_back(s_obj);
1780 // Turn the active counterparts of activated objects not pending for
1782 for(std::map<u16, StaticObject>::iterator
1783 i = block->m_static_objects.m_active.begin();
1784 i != block->m_static_objects.m_active.end(); ++i)
1787 ServerActiveObject *object = getActiveObject(id);
1789 object->m_pending_deactivation = false;
1793 Note: Block hasn't really been modified here.
1794 The objects have just been activated and moved from the stored
1795 static list to the active static list.
1796 As such, the block is essentially the same.
1797 Thus, do not call block->raiseModified(MOD_STATE_WRITE_NEEDED).
1798 Otherwise there would be a huge amount of unnecessary I/O.
1803 Convert objects that are not standing inside active blocks to static.
1805 If m_known_by_count != 0, active object is not deleted, but static
1806 data is still updated.
1808 If force_delete is set, active object is deleted nevertheless. It
1809 shall only be set so in the destructor of the environment.
1811 If block wasn't generated (not in memory or on disk),
1813 void ServerEnvironment::deactivateFarObjects(bool force_delete)
1815 std::list<u16> objects_to_remove;
1816 for(std::map<u16, ServerActiveObject*>::iterator
1817 i = m_active_objects.begin();
1818 i != m_active_objects.end(); ++i)
1820 ServerActiveObject* obj = i->second;
1823 // Do not deactivate if static data creation not allowed
1824 if(!force_delete && !obj->isStaticAllowed())
1827 // If pending deactivation, let removeRemovedObjects() do it
1828 if(!force_delete && obj->m_pending_deactivation)
1832 v3f objectpos = obj->getBasePosition();
1834 // The block in which the object resides in
1835 v3s16 blockpos_o = getNodeBlockPos(floatToInt(objectpos, BS));
1837 // If object's static data is stored in a deactivated block and object
1838 // is actually located in an active block, re-save to the block in
1839 // which the object is actually located in.
1841 obj->m_static_exists &&
1842 !m_active_blocks.contains(obj->m_static_block) &&
1843 m_active_blocks.contains(blockpos_o))
1845 v3s16 old_static_block = obj->m_static_block;
1847 // Save to block where object is located
1848 MapBlock *block = m_map->emergeBlock(blockpos_o, false);
1850 errorstream<<"ServerEnvironment::deactivateFarObjects(): "
1851 <<"Could not save object id="<<id
1852 <<" to it's current block "<<PP(blockpos_o)
1856 std::string staticdata_new = obj->getStaticData();
1857 StaticObject s_obj(obj->getType(), objectpos, staticdata_new);
1858 block->m_static_objects.insert(id, s_obj);
1859 obj->m_static_block = blockpos_o;
1860 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1861 "deactivateFarObjects: Static data moved in");
1863 // Delete from block where object was located
1864 block = m_map->emergeBlock(old_static_block, false);
1866 errorstream<<"ServerEnvironment::deactivateFarObjects(): "
1867 <<"Could not delete object id="<<id
1868 <<" from it's previous block "<<PP(old_static_block)
1872 block->m_static_objects.remove(id);
1873 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1874 "deactivateFarObjects: Static data moved out");
1878 // If block is active, don't remove
1879 if(!force_delete && m_active_blocks.contains(blockpos_o))
1882 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
1883 <<"deactivating object id="<<id<<" on inactive block "
1884 <<PP(blockpos_o)<<std::endl;
1886 // If known by some client, don't immediately delete.
1887 bool pending_delete = (obj->m_known_by_count > 0 && !force_delete);
1890 Update the static data
1893 if(obj->isStaticAllowed())
1895 // Create new static object
1896 std::string staticdata_new = obj->getStaticData();
1897 StaticObject s_obj(obj->getType(), objectpos, staticdata_new);
1899 bool stays_in_same_block = false;
1900 bool data_changed = true;
1902 if(obj->m_static_exists){
1903 if(obj->m_static_block == blockpos_o)
1904 stays_in_same_block = true;
1906 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
1908 std::map<u16, StaticObject>::iterator n =
1909 block->m_static_objects.m_active.find(id);
1910 if(n != block->m_static_objects.m_active.end()){
1911 StaticObject static_old = n->second;
1913 float save_movem = obj->getMinimumSavedMovement();
1915 if(static_old.data == staticdata_new &&
1916 (static_old.pos - objectpos).getLength() < save_movem)
1917 data_changed = false;
1919 errorstream<<"ServerEnvironment::deactivateFarObjects(): "
1920 <<"id="<<id<<" m_static_exists=true but "
1921 <<"static data doesn't actually exist in "
1922 <<PP(obj->m_static_block)<<std::endl;
1926 bool shall_be_written = (!stays_in_same_block || data_changed);
1928 // Delete old static object
1929 if(obj->m_static_exists)
1931 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
1934 block->m_static_objects.remove(id);
1935 obj->m_static_exists = false;
1936 // Only mark block as modified if data changed considerably
1937 if(shall_be_written)
1938 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1939 "deactivateFarObjects: Static data "
1940 "changed considerably");
1944 // Add to the block where the object is located in
1945 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1946 // Get or generate the block
1947 MapBlock *block = NULL;
1949 block = m_map->emergeBlock(blockpos);
1950 } catch(InvalidPositionException &e){
1951 // Handled via NULL pointer
1952 // NOTE: emergeBlock's failure is usually determined by it
1953 // actually returning NULL
1958 if(block->m_static_objects.m_stored.size() >= g_settings->getU16("max_objects_per_block")){
1959 errorstream<<"ServerEnv: Trying to store id="<<obj->getId()
1960 <<" statically but block "<<PP(blockpos)
1961 <<" already contains "
1962 <<block->m_static_objects.m_stored.size()
1964 <<" Forcing delete."<<std::endl;
1965 force_delete = true;
1967 // If static counterpart already exists in target block,
1969 // This shouldn't happen because the object is removed from
1970 // the previous block before this according to
1971 // obj->m_static_block, but happens rarely for some unknown
1972 // reason. Unsuccessful attempts have been made to find
1974 if(id && block->m_static_objects.m_active.find(id) != block->m_static_objects.m_active.end()){
1975 infostream<<"ServerEnv: WARNING: Performing hack #83274"
1977 block->m_static_objects.remove(id);
1979 // Store static data
1980 u16 store_id = pending_delete ? id : 0;
1981 block->m_static_objects.insert(store_id, s_obj);
1983 // Only mark block as modified if data changed considerably
1984 if(shall_be_written)
1985 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1986 "deactivateFarObjects: Static data "
1987 "changed considerably");
1989 obj->m_static_exists = true;
1990 obj->m_static_block = block->getPos();
1995 v3s16 p = floatToInt(objectpos, BS);
1996 errorstream<<"ServerEnv: Could not find or generate "
1997 <<"a block for storing id="<<obj->getId()
1998 <<" statically (pos="<<PP(p)<<")"<<std::endl;
2005 If known by some client, set pending deactivation.
2006 Otherwise delete it immediately.
2009 if(pending_delete && !force_delete)
2011 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
2012 <<"object id="<<id<<" is known by clients"
2013 <<"; not deleting yet"<<std::endl;
2015 obj->m_pending_deactivation = true;
2019 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
2020 <<"object id="<<id<<" is not known by clients"
2021 <<"; deleting"<<std::endl;
2023 // Tell the object about removal
2024 obj->removingFromEnvironment();
2025 // Deregister in scripting api
2026 m_script->removeObjectReference(obj);
2028 // Delete active object
2029 if(obj->environmentDeletes())
2031 // Id to be removed from m_active_objects
2032 objects_to_remove.push_back(id);
2035 // Remove references from m_active_objects
2036 for(std::list<u16>::iterator i = objects_to_remove.begin();
2037 i != objects_to_remove.end(); ++i)
2039 m_active_objects.erase(*i);
2046 #include "clientsimpleobject.h"
2052 ClientEnvironment::ClientEnvironment(ClientMap *map, scene::ISceneManager *smgr,
2053 ITextureSource *texturesource, IGameDef *gamedef,
2054 IrrlichtDevice *irr):
2057 m_texturesource(texturesource),
2063 ClientEnvironment::~ClientEnvironment()
2065 // delete active objects
2066 for(std::map<u16, ClientActiveObject*>::iterator
2067 i = m_active_objects.begin();
2068 i != m_active_objects.end(); ++i)
2073 for(std::list<ClientSimpleObject*>::iterator
2074 i = m_simple_objects.begin(); i != m_simple_objects.end(); ++i)
2083 Map & ClientEnvironment::getMap()
2088 ClientMap & ClientEnvironment::getClientMap()
2093 void ClientEnvironment::addPlayer(Player *player)
2095 DSTACK(__FUNCTION_NAME);
2097 It is a failure if player is local and there already is a local
2100 assert(!(player->isLocal() == true && getLocalPlayer() != NULL));
2102 Environment::addPlayer(player);
2105 LocalPlayer * ClientEnvironment::getLocalPlayer()
2107 for(std::list<Player*>::iterator i = m_players.begin();
2108 i != m_players.end(); ++i)
2110 Player *player = *i;
2111 if(player->isLocal())
2112 return (LocalPlayer*)player;
2117 void ClientEnvironment::step(float dtime)
2119 DSTACK(__FUNCTION_NAME);
2121 /* Step time of day */
2122 stepTimeOfDay(dtime);
2124 // Get some settings
2125 bool fly_allowed = m_gamedef->checkLocalPrivilege("fly");
2126 bool free_move = fly_allowed && g_settings->getBool("free_move");
2129 LocalPlayer *lplayer = getLocalPlayer();
2131 // collision info queue
2132 std::list<CollisionInfo> player_collisions;
2135 Get the speed the player is going
2137 bool is_climbing = lplayer->is_climbing;
2139 f32 player_speed = lplayer->getSpeed().getLength();
2142 Maximum position increment
2144 //f32 position_max_increment = 0.05*BS;
2145 f32 position_max_increment = 0.1*BS;
2147 // Maximum time increment (for collision detection etc)
2148 // time = distance / speed
2149 f32 dtime_max_increment = 1;
2150 if(player_speed > 0.001)
2151 dtime_max_increment = position_max_increment / player_speed;
2153 // Maximum time increment is 10ms or lower
2154 if(dtime_max_increment > 0.01)
2155 dtime_max_increment = 0.01;
2157 // Don't allow overly huge dtime
2161 f32 dtime_downcount = dtime;
2164 Stuff that has a maximum time increment
2173 if(dtime_downcount > dtime_max_increment)
2175 dtime_part = dtime_max_increment;
2176 dtime_downcount -= dtime_part;
2180 dtime_part = dtime_downcount;
2182 Setting this to 0 (no -=dtime_part) disables an infinite loop
2183 when dtime_part is so small that dtime_downcount -= dtime_part
2186 dtime_downcount = 0;
2195 if(free_move == false && is_climbing == false)
2198 v3f speed = lplayer->getSpeed();
2199 if(lplayer->in_liquid == false)
2200 speed.Y -= lplayer->movement_gravity * lplayer->physics_override_gravity * dtime_part * 2;
2202 // Liquid floating / sinking
2203 if(lplayer->in_liquid && !lplayer->swimming_vertical)
2204 speed.Y -= lplayer->movement_liquid_sink * dtime_part * 2;
2206 // Liquid resistance
2207 if(lplayer->in_liquid_stable || lplayer->in_liquid)
2209 // How much the node's viscosity blocks movement, ranges between 0 and 1
2210 // Should match the scale at which viscosity increase affects other liquid attributes
2211 const f32 viscosity_factor = 0.3;
2213 v3f d_wanted = -speed / lplayer->movement_liquid_fluidity;
2214 f32 dl = d_wanted.getLength();
2215 if(dl > lplayer->movement_liquid_fluidity_smooth)
2216 dl = lplayer->movement_liquid_fluidity_smooth;
2217 dl *= (lplayer->liquid_viscosity * viscosity_factor) + (1 - viscosity_factor);
2219 v3f d = d_wanted.normalize() * dl;
2223 if(speed.X > lplayer->movement_liquid_fluidity + lplayer->movement_liquid_fluidity_smooth) speed.X -= lplayer->movement_liquid_fluidity_smooth;
2224 if(speed.X < -lplayer->movement_liquid_fluidity - lplayer->movement_liquid_fluidity_smooth) speed.X += lplayer->movement_liquid_fluidity_smooth;
2225 if(speed.Y > lplayer->movement_liquid_fluidity + lplayer->movement_liquid_fluidity_smooth) speed.Y -= lplayer->movement_liquid_fluidity_smooth;
2226 if(speed.Y < -lplayer->movement_liquid_fluidity - lplayer->movement_liquid_fluidity_smooth) speed.Y += lplayer->movement_liquid_fluidity_smooth;
2227 if(speed.Z > lplayer->movement_liquid_fluidity + lplayer->movement_liquid_fluidity_smooth) speed.Z -= lplayer->movement_liquid_fluidity_smooth;
2228 if(speed.Z < -lplayer->movement_liquid_fluidity - lplayer->movement_liquid_fluidity_smooth) speed.Z += lplayer->movement_liquid_fluidity_smooth;
2232 lplayer->setSpeed(speed);
2237 This also does collision detection.
2239 lplayer->move(dtime_part, this, position_max_increment,
2240 &player_collisions);
2243 while(dtime_downcount > 0.001);
2245 //std::cout<<"Looped "<<loopcount<<" times."<<std::endl;
2247 for(std::list<CollisionInfo>::iterator
2248 i = player_collisions.begin();
2249 i != player_collisions.end(); ++i)
2251 CollisionInfo &info = *i;
2252 v3f speed_diff = info.new_speed - info.old_speed;;
2253 // Handle only fall damage
2254 // (because otherwise walking against something in fast_move kills you)
2255 if(speed_diff.Y < 0 || info.old_speed.Y >= 0)
2257 // Get rid of other components
2260 f32 pre_factor = 1; // 1 hp per node/s
2261 f32 tolerance = BS*14; // 5 without damage
2262 f32 post_factor = 1; // 1 hp per node/s
2263 if(info.type == COLLISION_NODE)
2265 const ContentFeatures &f = m_gamedef->ndef()->
2266 get(m_map->getNodeNoEx(info.node_p));
2267 // Determine fall damage multiplier
2268 int addp = itemgroup_get(f.groups, "fall_damage_add_percent");
2269 pre_factor = 1.0 + (float)addp/100.0;
2271 float speed = pre_factor * speed_diff.getLength();
2272 if(speed > tolerance)
2274 f32 damage_f = (speed - tolerance)/BS * post_factor;
2275 u16 damage = (u16)(damage_f+0.5);
2277 damageLocalPlayer(damage, true);
2278 MtEvent *e = new SimpleTriggerEvent("PlayerFallingDamage");
2279 m_gamedef->event()->put(e);
2285 A quick draft of lava damage
2287 if(m_lava_hurt_interval.step(dtime, 1.0))
2289 v3f pf = lplayer->getPosition();
2291 // Feet, middle and head
2292 v3s16 p1 = floatToInt(pf + v3f(0, BS*0.1, 0), BS);
2293 MapNode n1 = m_map->getNodeNoEx(p1);
2294 v3s16 p2 = floatToInt(pf + v3f(0, BS*0.8, 0), BS);
2295 MapNode n2 = m_map->getNodeNoEx(p2);
2296 v3s16 p3 = floatToInt(pf + v3f(0, BS*1.6, 0), BS);
2297 MapNode n3 = m_map->getNodeNoEx(p3);
2299 u32 damage_per_second = 0;
2300 damage_per_second = MYMAX(damage_per_second,
2301 m_gamedef->ndef()->get(n1).damage_per_second);
2302 damage_per_second = MYMAX(damage_per_second,
2303 m_gamedef->ndef()->get(n2).damage_per_second);
2304 damage_per_second = MYMAX(damage_per_second,
2305 m_gamedef->ndef()->get(n3).damage_per_second);
2307 if(damage_per_second != 0)
2309 damageLocalPlayer(damage_per_second, true);
2316 if(m_drowning_interval.step(dtime, 2.0))
2318 v3f pf = lplayer->getPosition();
2321 v3s16 p = floatToInt(pf + v3f(0, BS*1.6, 0), BS);
2322 MapNode n = m_map->getNodeNoEx(p);
2323 ContentFeatures c = m_gamedef->ndef()->get(n);
2324 u8 drowning_damage = c.drowning;
2325 if(drowning_damage > 0 && lplayer->hp > 0){
2326 u16 breath = lplayer->getBreath();
2333 lplayer->setBreath(breath);
2334 updateLocalPlayerBreath(breath);
2337 if(lplayer->getBreath() == 0 && drowning_damage > 0){
2338 damageLocalPlayer(drowning_damage, true);
2341 if(m_breathing_interval.step(dtime, 0.5))
2343 v3f pf = lplayer->getPosition();
2346 v3s16 p = floatToInt(pf + v3f(0, BS*1.6, 0), BS);
2347 MapNode n = m_map->getNodeNoEx(p);
2348 ContentFeatures c = m_gamedef->ndef()->get(n);
2350 lplayer->setBreath(11);
2352 else if(c.drowning == 0){
2353 u16 breath = lplayer->getBreath();
2356 lplayer->setBreath(breath);
2357 updateLocalPlayerBreath(breath);
2363 Stuff that can be done in an arbitarily large dtime
2365 for(std::list<Player*>::iterator i = m_players.begin();
2366 i != m_players.end(); ++i)
2368 Player *player = *i;
2371 Handle non-local players
2373 if(player->isLocal() == false)
2376 player->move(dtime, *m_map, 100*BS);
2380 // Update lighting on all players on client
2384 v3s16 p = player->getLightPosition();
2385 MapNode n = m_map->getNode(p);
2386 light = n.getLightBlendF1((float)getDayNightRatio()/1000, m_gamedef->ndef());
2388 catch(InvalidPositionException &e){
2389 light = blend_light_f1((float)getDayNightRatio()/1000, LIGHT_SUN, 0);
2391 player->light = light;
2395 Step active objects and update lighting of them
2398 g_profiler->avg("CEnv: num of objects", m_active_objects.size());
2399 bool update_lighting = m_active_object_light_update_interval.step(dtime, 0.21);
2400 for(std::map<u16, ClientActiveObject*>::iterator
2401 i = m_active_objects.begin();
2402 i != m_active_objects.end(); ++i)
2404 ClientActiveObject* obj = i->second;
2406 obj->step(dtime, this);
2414 v3s16 p = obj->getLightPosition();
2415 MapNode n = m_map->getNode(p);
2416 light = n.getLightBlend(getDayNightRatio(), m_gamedef->ndef());
2418 catch(InvalidPositionException &e){
2419 light = blend_light(getDayNightRatio(), LIGHT_SUN, 0);
2421 obj->updateLight(light);
2426 Step and handle simple objects
2428 g_profiler->avg("CEnv: num of simple objects", m_simple_objects.size());
2429 for(std::list<ClientSimpleObject*>::iterator
2430 i = m_simple_objects.begin(); i != m_simple_objects.end();)
2432 ClientSimpleObject *simple = *i;
2433 std::list<ClientSimpleObject*>::iterator cur = i;
2435 simple->step(dtime);
2436 if(simple->m_to_be_removed){
2438 m_simple_objects.erase(cur);
2443 void ClientEnvironment::addSimpleObject(ClientSimpleObject *simple)
2445 m_simple_objects.push_back(simple);
2448 ClientActiveObject* ClientEnvironment::getActiveObject(u16 id)
2450 std::map<u16, ClientActiveObject*>::iterator n;
2451 n = m_active_objects.find(id);
2452 if(n == m_active_objects.end())
2457 bool isFreeClientActiveObjectId(u16 id,
2458 std::map<u16, ClientActiveObject*> &objects)
2463 return objects.find(id) == objects.end();
2466 u16 getFreeClientActiveObjectId(
2467 std::map<u16, ClientActiveObject*> &objects)
2469 //try to reuse id's as late as possible
2470 static u16 last_used_id = 0;
2471 u16 startid = last_used_id;
2475 if(isFreeClientActiveObjectId(last_used_id, objects))
2476 return last_used_id;
2478 if(last_used_id == startid)
2483 u16 ClientEnvironment::addActiveObject(ClientActiveObject *object)
2486 if(object->getId() == 0)
2488 u16 new_id = getFreeClientActiveObjectId(m_active_objects);
2491 infostream<<"ClientEnvironment::addActiveObject(): "
2492 <<"no free ids available"<<std::endl;
2496 object->setId(new_id);
2498 if(isFreeClientActiveObjectId(object->getId(), m_active_objects) == false)
2500 infostream<<"ClientEnvironment::addActiveObject(): "
2501 <<"id is not free ("<<object->getId()<<")"<<std::endl;
2505 infostream<<"ClientEnvironment::addActiveObject(): "
2506 <<"added (id="<<object->getId()<<")"<<std::endl;
2507 m_active_objects[object->getId()] = object;
2508 object->addToScene(m_smgr, m_texturesource, m_irr);
2509 { // Update lighting immediately
2513 v3s16 p = object->getLightPosition();
2514 MapNode n = m_map->getNode(p);
2515 light = n.getLightBlend(getDayNightRatio(), m_gamedef->ndef());
2517 catch(InvalidPositionException &e){
2518 light = blend_light(getDayNightRatio(), LIGHT_SUN, 0);
2520 object->updateLight(light);
2522 return object->getId();
2525 void ClientEnvironment::addActiveObject(u16 id, u8 type,
2526 const std::string &init_data)
2528 ClientActiveObject* obj =
2529 ClientActiveObject::create(type, m_gamedef, this);
2532 infostream<<"ClientEnvironment::addActiveObject(): "
2533 <<"id="<<id<<" type="<<type<<": Couldn't create object"
2542 obj->initialize(init_data);
2544 catch(SerializationError &e)
2546 errorstream<<"ClientEnvironment::addActiveObject():"
2547 <<" id="<<id<<" type="<<type
2548 <<": SerializationError in initialize(): "
2550 <<": init_data="<<serializeJsonString(init_data)
2554 addActiveObject(obj);
2557 void ClientEnvironment::removeActiveObject(u16 id)
2559 verbosestream<<"ClientEnvironment::removeActiveObject(): "
2560 <<"id="<<id<<std::endl;
2561 ClientActiveObject* obj = getActiveObject(id);
2564 infostream<<"ClientEnvironment::removeActiveObject(): "
2565 <<"id="<<id<<" not found"<<std::endl;
2568 obj->removeFromScene(true);
2570 m_active_objects.erase(id);
2573 void ClientEnvironment::processActiveObjectMessage(u16 id,
2574 const std::string &data)
2576 ClientActiveObject* obj = getActiveObject(id);
2579 infostream<<"ClientEnvironment::processActiveObjectMessage():"
2580 <<" got message for id="<<id<<", which doesn't exist."
2586 obj->processMessage(data);
2588 catch(SerializationError &e)
2590 errorstream<<"ClientEnvironment::processActiveObjectMessage():"
2591 <<" id="<<id<<" type="<<obj->getType()
2592 <<" SerializationError in processMessage(),"
2593 <<" message="<<serializeJsonString(data)
2599 Callbacks for activeobjects
2602 void ClientEnvironment::damageLocalPlayer(u8 damage, bool handle_hp)
2604 LocalPlayer *lplayer = getLocalPlayer();
2608 if(lplayer->hp > damage)
2609 lplayer->hp -= damage;
2614 ClientEnvEvent event;
2615 event.type = CEE_PLAYER_DAMAGE;
2616 event.player_damage.amount = damage;
2617 event.player_damage.send_to_server = handle_hp;
2618 m_client_event_queue.push_back(event);
2621 void ClientEnvironment::updateLocalPlayerBreath(u16 breath)
2623 ClientEnvEvent event;
2624 event.type = CEE_PLAYER_BREATH;
2625 event.player_breath.amount = breath;
2626 m_client_event_queue.push_back(event);
2630 Client likes to call these
2633 void ClientEnvironment::getActiveObjects(v3f origin, f32 max_d,
2634 std::vector<DistanceSortedActiveObject> &dest)
2636 for(std::map<u16, ClientActiveObject*>::iterator
2637 i = m_active_objects.begin();
2638 i != m_active_objects.end(); ++i)
2640 ClientActiveObject* obj = i->second;
2642 f32 d = (obj->getPosition() - origin).getLength();
2647 DistanceSortedActiveObject dso(obj, d);
2649 dest.push_back(dso);
2653 ClientEnvEvent ClientEnvironment::getClientEvent()
2655 ClientEnvEvent event;
2656 if(m_client_event_queue.empty())
2657 event.type = CEE_NONE;
2659 event = m_client_event_queue.front();
2660 m_client_event_queue.pop_front();
2665 #endif // #ifndef SERVER