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();
722 // Find out how many objects the block contains
723 u32 active_object_count = block->m_static_objects.m_active.size();
724 // Find out how many objects this and all the neighbors contain
725 u32 active_object_count_wider = 0;
726 u32 wider_unknown_count = 0;
727 for(s16 x=-1; x<=1; x++)
728 for(s16 y=-1; y<=1; y++)
729 for(s16 z=-1; z<=1; z++)
731 MapBlock *block2 = map->getBlockNoCreateNoEx(
732 block->getPos() + v3s16(x,y,z));
734 wider_unknown_count = 0;
737 active_object_count_wider +=
738 block2->m_static_objects.m_active.size()
739 + block2->m_static_objects.m_stored.size();
742 u32 wider_known_count = 3*3*3 - wider_unknown_count;
743 active_object_count_wider += wider_unknown_count * active_object_count_wider / wider_known_count;
746 for(p0.X=0; p0.X<MAP_BLOCKSIZE; p0.X++)
747 for(p0.Y=0; p0.Y<MAP_BLOCKSIZE; p0.Y++)
748 for(p0.Z=0; p0.Z<MAP_BLOCKSIZE; p0.Z++)
750 MapNode n = block->getNodeNoEx(p0);
751 content_t c = n.getContent();
752 v3s16 p = p0 + block->getPosRelative();
754 std::map<content_t, std::list<ActiveABM> >::iterator j;
756 if(j == m_aabms.end())
759 for(std::list<ActiveABM>::iterator
760 i = j->second.begin(); i != j->second.end(); i++)
762 if(myrand() % i->chance != 0)
766 if(!i->required_neighbors.empty())
769 for(p1.X = p.X-1; p1.X <= p.X+1; p1.X++)
770 for(p1.Y = p.Y-1; p1.Y <= p.Y+1; p1.Y++)
771 for(p1.Z = p.Z-1; p1.Z <= p.Z+1; p1.Z++)
775 MapNode n = map->getNodeNoEx(p1);
776 content_t c = n.getContent();
777 std::set<content_t>::const_iterator k;
778 k = i->required_neighbors.find(c);
779 if(k != i->required_neighbors.end()){
783 // No required neighbor found
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 // Reset usage timer immediately, otherwise a block that becomes active
800 // again at around the same time as it would normally be unloaded will
801 // get unloaded incorrectly. (I think this still leaves a small possibility
802 // of a race condition between this and server::AsyncRunStep, which only
803 // some kind of synchronisation will fix, but it at least reduces the window
804 // of opportunity for it to break from seconds to nanoseconds)
805 block->resetUsageTimer();
807 // Get time difference
809 u32 stamp = block->getTimestamp();
810 if(m_game_time > stamp && stamp != BLOCK_TIMESTAMP_UNDEFINED)
811 dtime_s = m_game_time - block->getTimestamp();
812 dtime_s += additional_dtime;
814 /*infostream<<"ServerEnvironment::activateBlock(): block timestamp: "
815 <<stamp<<", game time: "<<m_game_time<<std::endl;*/
817 // Set current time as timestamp
818 block->setTimestampNoChangedFlag(m_game_time);
820 /*infostream<<"ServerEnvironment::activateBlock(): block is "
821 <<dtime_s<<" seconds old."<<std::endl;*/
823 // Activate stored objects
824 activateObjects(block, dtime_s);
827 std::map<v3s16, NodeTimer> elapsed_timers =
828 block->m_node_timers.step((float)dtime_s);
829 if(!elapsed_timers.empty()){
831 for(std::map<v3s16, NodeTimer>::iterator
832 i = elapsed_timers.begin();
833 i != elapsed_timers.end(); i++){
834 n = block->getNodeNoEx(i->first);
835 v3s16 p = i->first + block->getPosRelative();
836 if(m_script->node_on_timer(p,n,i->second.elapsed))
837 block->setNodeTimer(i->first,NodeTimer(i->second.timeout,0));
841 /* Handle ActiveBlockModifiers */
842 ABMHandler abmhandler(m_abms, dtime_s, this, false);
843 abmhandler.apply(block);
846 void ServerEnvironment::addActiveBlockModifier(ActiveBlockModifier *abm)
848 m_abms.push_back(ABMWithState(abm));
851 bool ServerEnvironment::setNode(v3s16 p, const MapNode &n)
853 INodeDefManager *ndef = m_gamedef->ndef();
854 MapNode n_old = m_map->getNodeNoEx(p);
856 if(ndef->get(n_old).has_on_destruct)
857 m_script->node_on_destruct(p, n_old);
859 bool succeeded = m_map->addNodeWithEvent(p, n);
862 // Call post-destructor
863 if(ndef->get(n_old).has_after_destruct)
864 m_script->node_after_destruct(p, n_old);
866 if(ndef->get(n).has_on_construct)
867 m_script->node_on_construct(p, n);
871 bool ServerEnvironment::removeNode(v3s16 p)
873 INodeDefManager *ndef = m_gamedef->ndef();
874 MapNode n_old = m_map->getNodeNoEx(p);
876 if(ndef->get(n_old).has_on_destruct)
877 m_script->node_on_destruct(p, n_old);
879 // This is slightly optimized compared to addNodeWithEvent(air)
880 bool succeeded = m_map->removeNodeWithEvent(p);
883 // Call post-destructor
884 if(ndef->get(n_old).has_after_destruct)
885 m_script->node_after_destruct(p, n_old);
886 // Air doesn't require constructor
890 bool ServerEnvironment::swapNode(v3s16 p, const MapNode &n)
892 return m_map->addNodeWithEvent(p, n, false);
895 std::set<u16> ServerEnvironment::getObjectsInsideRadius(v3f pos, float radius)
897 std::set<u16> objects;
898 for(std::map<u16, ServerActiveObject*>::iterator
899 i = m_active_objects.begin();
900 i != m_active_objects.end(); ++i)
902 ServerActiveObject* obj = i->second;
904 v3f objectpos = obj->getBasePosition();
905 if(objectpos.getDistanceFrom(pos) > radius)
912 void ServerEnvironment::clearAllObjects()
914 infostream<<"ServerEnvironment::clearAllObjects(): "
915 <<"Removing all active objects"<<std::endl;
916 std::list<u16> objects_to_remove;
917 for(std::map<u16, ServerActiveObject*>::iterator
918 i = m_active_objects.begin();
919 i != m_active_objects.end(); ++i)
921 ServerActiveObject* obj = i->second;
922 if(obj->getType() == ACTIVEOBJECT_TYPE_PLAYER)
925 // Delete static object if block is loaded
926 if(obj->m_static_exists){
927 MapBlock *block = m_map->getBlockNoCreateNoEx(obj->m_static_block);
929 block->m_static_objects.remove(id);
930 block->raiseModified(MOD_STATE_WRITE_NEEDED,
932 obj->m_static_exists = false;
935 // If known by some client, don't delete immediately
936 if(obj->m_known_by_count > 0){
937 obj->m_pending_deactivation = true;
938 obj->m_removed = true;
942 // Tell the object about removal
943 obj->removingFromEnvironment();
944 // Deregister in scripting api
945 m_script->removeObjectReference(obj);
947 // Delete active object
948 if(obj->environmentDeletes())
950 // Id to be removed from m_active_objects
951 objects_to_remove.push_back(id);
953 // Remove references from m_active_objects
954 for(std::list<u16>::iterator i = objects_to_remove.begin();
955 i != objects_to_remove.end(); ++i)
957 m_active_objects.erase(*i);
960 // Get list of loaded blocks
961 std::list<v3s16> loaded_blocks;
962 infostream<<"ServerEnvironment::clearAllObjects(): "
963 <<"Listing all loaded blocks"<<std::endl;
964 m_map->listAllLoadedBlocks(loaded_blocks);
965 infostream<<"ServerEnvironment::clearAllObjects(): "
966 <<"Done listing all loaded blocks: "
967 <<loaded_blocks.size()<<std::endl;
969 // Get list of loadable blocks
970 std::list<v3s16> loadable_blocks;
971 infostream<<"ServerEnvironment::clearAllObjects(): "
972 <<"Listing all loadable blocks"<<std::endl;
973 m_map->listAllLoadableBlocks(loadable_blocks);
974 infostream<<"ServerEnvironment::clearAllObjects(): "
975 <<"Done listing all loadable blocks: "
976 <<loadable_blocks.size()
977 <<", now clearing"<<std::endl;
979 // Grab a reference on each loaded block to avoid unloading it
980 for(std::list<v3s16>::iterator i = loaded_blocks.begin();
981 i != loaded_blocks.end(); ++i)
984 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
989 // Remove objects in all loadable blocks
990 u32 unload_interval = g_settings->getS32("max_clearobjects_extra_loaded_blocks");
991 unload_interval = MYMAX(unload_interval, 1);
992 u32 report_interval = loadable_blocks.size() / 10;
993 u32 num_blocks_checked = 0;
994 u32 num_blocks_cleared = 0;
995 u32 num_objs_cleared = 0;
996 for(std::list<v3s16>::iterator i = loadable_blocks.begin();
997 i != loadable_blocks.end(); ++i)
1000 MapBlock *block = m_map->emergeBlock(p, false);
1002 errorstream<<"ServerEnvironment::clearAllObjects(): "
1003 <<"Failed to emerge block "<<PP(p)<<std::endl;
1006 u32 num_stored = block->m_static_objects.m_stored.size();
1007 u32 num_active = block->m_static_objects.m_active.size();
1008 if(num_stored != 0 || num_active != 0){
1009 block->m_static_objects.m_stored.clear();
1010 block->m_static_objects.m_active.clear();
1011 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1013 num_objs_cleared += num_stored + num_active;
1014 num_blocks_cleared++;
1016 num_blocks_checked++;
1018 if(report_interval != 0 &&
1019 num_blocks_checked % report_interval == 0){
1020 float percent = 100.0 * (float)num_blocks_checked /
1021 loadable_blocks.size();
1022 infostream<<"ServerEnvironment::clearAllObjects(): "
1023 <<"Cleared "<<num_objs_cleared<<" objects"
1024 <<" in "<<num_blocks_cleared<<" blocks ("
1025 <<percent<<"%)"<<std::endl;
1027 if(num_blocks_checked % unload_interval == 0){
1028 m_map->unloadUnreferencedBlocks();
1031 m_map->unloadUnreferencedBlocks();
1033 // Drop references that were added above
1034 for(std::list<v3s16>::iterator i = loaded_blocks.begin();
1035 i != loaded_blocks.end(); ++i)
1038 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1043 infostream<<"ServerEnvironment::clearAllObjects(): "
1044 <<"Finished: Cleared "<<num_objs_cleared<<" objects"
1045 <<" in "<<num_blocks_cleared<<" blocks"<<std::endl;
1048 void ServerEnvironment::step(float dtime)
1050 DSTACK(__FUNCTION_NAME);
1052 //TimeTaker timer("ServerEnv step");
1054 /* Step time of day */
1055 stepTimeOfDay(dtime);
1058 // NOTE: This is kind of funny on a singleplayer game, but doesn't
1059 // really matter that much.
1060 m_recommended_send_interval = g_settings->getFloat("dedicated_server_step");
1066 m_game_time_fraction_counter += dtime;
1067 u32 inc_i = (u32)m_game_time_fraction_counter;
1068 m_game_time += inc_i;
1069 m_game_time_fraction_counter -= (float)inc_i;
1076 ScopeProfiler sp(g_profiler, "SEnv: handle players avg", SPT_AVG);
1077 for(std::list<Player*>::iterator i = m_players.begin();
1078 i != m_players.end(); ++i)
1080 Player *player = *i;
1082 // Ignore disconnected players
1083 if(player->peer_id == 0)
1087 player->move(dtime, *m_map, 100*BS);
1092 Manage active block list
1094 if(m_active_blocks_management_interval.step(dtime, 2.0))
1096 ScopeProfiler sp(g_profiler, "SEnv: manage act. block list avg /2s", SPT_AVG);
1098 Get player block positions
1100 std::list<v3s16> players_blockpos;
1101 for(std::list<Player*>::iterator
1102 i = m_players.begin();
1103 i != m_players.end(); ++i)
1105 Player *player = *i;
1106 // Ignore disconnected players
1107 if(player->peer_id == 0)
1109 v3s16 blockpos = getNodeBlockPos(
1110 floatToInt(player->getPosition(), BS));
1111 players_blockpos.push_back(blockpos);
1115 Update list of active blocks, collecting changes
1117 const s16 active_block_range = g_settings->getS16("active_block_range");
1118 std::set<v3s16> blocks_removed;
1119 std::set<v3s16> blocks_added;
1120 m_active_blocks.update(players_blockpos, active_block_range,
1121 blocks_removed, blocks_added);
1124 Handle removed blocks
1127 // Convert active objects that are no more in active blocks to static
1128 deactivateFarObjects(false);
1130 for(std::set<v3s16>::iterator
1131 i = blocks_removed.begin();
1132 i != blocks_removed.end(); ++i)
1136 /* infostream<<"Server: Block " << PP(p)
1137 << " became inactive"<<std::endl; */
1139 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1143 // Set current time as timestamp (and let it set ChangedFlag)
1144 block->setTimestamp(m_game_time);
1151 for(std::set<v3s16>::iterator
1152 i = blocks_added.begin();
1153 i != blocks_added.end(); ++i)
1157 MapBlock *block = m_map->getBlockOrEmerge(p);
1159 m_active_blocks.m_list.erase(p);
1163 activateBlock(block);
1164 /* infostream<<"Server: Block " << PP(p)
1165 << " became active"<<std::endl; */
1170 Mess around in active blocks
1172 if(m_active_blocks_nodemetadata_interval.step(dtime, 1.0))
1174 ScopeProfiler sp(g_profiler, "SEnv: mess in act. blocks avg /1s", SPT_AVG);
1178 for(std::set<v3s16>::iterator
1179 i = m_active_blocks.m_list.begin();
1180 i != m_active_blocks.m_list.end(); ++i)
1184 /*infostream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
1185 <<") being handled"<<std::endl;*/
1187 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1191 // Reset block usage timer
1192 block->resetUsageTimer();
1194 // Set current time as timestamp
1195 block->setTimestampNoChangedFlag(m_game_time);
1196 // If time has changed much from the one on disk,
1197 // set block to be saved when it is unloaded
1198 if(block->getTimestamp() > block->getDiskTimestamp() + 60)
1199 block->raiseModified(MOD_STATE_WRITE_AT_UNLOAD,
1200 "Timestamp older than 60s (step)");
1203 std::map<v3s16, NodeTimer> elapsed_timers =
1204 block->m_node_timers.step((float)dtime);
1205 if(!elapsed_timers.empty()){
1207 for(std::map<v3s16, NodeTimer>::iterator
1208 i = elapsed_timers.begin();
1209 i != elapsed_timers.end(); i++){
1210 n = block->getNodeNoEx(i->first);
1211 p = i->first + block->getPosRelative();
1212 if(m_script->node_on_timer(p,n,i->second.elapsed))
1213 block->setNodeTimer(i->first,NodeTimer(i->second.timeout,0));
1219 const float abm_interval = 1.0;
1220 if(m_active_block_modifier_interval.step(dtime, abm_interval))
1222 if(m_active_block_interval_overload_skip > 0){
1223 ScopeProfiler sp(g_profiler, "SEnv: ABM overload skips");
1224 m_active_block_interval_overload_skip--;
1227 ScopeProfiler sp(g_profiler, "SEnv: modify in blocks avg /1s", SPT_AVG);
1228 TimeTaker timer("modify in active blocks");
1230 // Initialize handling of ActiveBlockModifiers
1231 ABMHandler abmhandler(m_abms, abm_interval, this, true);
1233 for(std::set<v3s16>::iterator
1234 i = m_active_blocks.m_list.begin();
1235 i != m_active_blocks.m_list.end(); ++i)
1239 /*infostream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
1240 <<") being handled"<<std::endl;*/
1242 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1246 // Set current time as timestamp
1247 block->setTimestampNoChangedFlag(m_game_time);
1249 /* Handle ActiveBlockModifiers */
1250 abmhandler.apply(block);
1253 u32 time_ms = timer.stop(true);
1254 u32 max_time_ms = 200;
1255 if(time_ms > max_time_ms){
1256 infostream<<"WARNING: active block modifiers took "
1257 <<time_ms<<"ms (longer than "
1258 <<max_time_ms<<"ms)"<<std::endl;
1259 m_active_block_interval_overload_skip = (time_ms / max_time_ms) + 1;
1264 Step script environment (run global on_step())
1266 m_script->environment_Step(dtime);
1272 ScopeProfiler sp(g_profiler, "SEnv: step act. objs avg", SPT_AVG);
1273 //TimeTaker timer("Step active objects");
1275 g_profiler->avg("SEnv: num of objects", m_active_objects.size());
1277 // This helps the objects to send data at the same time
1278 bool send_recommended = false;
1279 m_send_recommended_timer += dtime;
1280 if(m_send_recommended_timer > getSendRecommendedInterval())
1282 m_send_recommended_timer -= getSendRecommendedInterval();
1283 send_recommended = true;
1286 for(std::map<u16, ServerActiveObject*>::iterator
1287 i = m_active_objects.begin();
1288 i != m_active_objects.end(); ++i)
1290 ServerActiveObject* obj = i->second;
1291 // Remove non-peaceful mobs on peaceful mode
1292 if(g_settings->getBool("only_peaceful_mobs")){
1293 if(!obj->isPeaceful())
1294 obj->m_removed = true;
1296 // Don't step if is to be removed or stored statically
1297 if(obj->m_removed || obj->m_pending_deactivation)
1300 obj->step(dtime, send_recommended);
1301 // Read messages from object
1302 while(!obj->m_messages_out.empty())
1304 m_active_object_messages.push_back(
1305 obj->m_messages_out.pop_front());
1311 Manage active objects
1313 if(m_object_management_interval.step(dtime, 0.5))
1315 ScopeProfiler sp(g_profiler, "SEnv: remove removed objs avg /.5s", SPT_AVG);
1317 Remove objects that satisfy (m_removed && m_known_by_count==0)
1319 removeRemovedObjects();
1323 ServerActiveObject* ServerEnvironment::getActiveObject(u16 id)
1325 std::map<u16, ServerActiveObject*>::iterator n;
1326 n = m_active_objects.find(id);
1327 if(n == m_active_objects.end())
1332 bool isFreeServerActiveObjectId(u16 id,
1333 std::map<u16, ServerActiveObject*> &objects)
1338 return objects.find(id) == objects.end();
1341 u16 getFreeServerActiveObjectId(
1342 std::map<u16, ServerActiveObject*> &objects)
1344 //try to reuse id's as late as possible
1345 static u16 last_used_id = 0;
1346 u16 startid = last_used_id;
1350 if(isFreeServerActiveObjectId(last_used_id, objects))
1351 return last_used_id;
1353 if(last_used_id == startid)
1358 u16 ServerEnvironment::addActiveObject(ServerActiveObject *object)
1361 u16 id = addActiveObjectRaw(object, true, 0);
1366 bool ServerEnvironment::addActiveObjectAsStatic(ServerActiveObject *obj)
1370 v3f objectpos = obj->getBasePosition();
1372 // The block in which the object resides in
1373 v3s16 blockpos_o = getNodeBlockPos(floatToInt(objectpos, BS));
1376 Update the static data
1379 // Create new static object
1380 std::string staticdata = obj->getStaticData();
1381 StaticObject s_obj(obj->getType(), objectpos, staticdata);
1382 // Add to the block where the object is located in
1383 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1384 // Get or generate the block
1385 MapBlock *block = m_map->emergeBlock(blockpos);
1387 bool succeeded = false;
1391 block->m_static_objects.insert(0, s_obj);
1392 block->raiseModified(MOD_STATE_WRITE_AT_UNLOAD,
1393 "addActiveObjectAsStatic");
1397 infostream<<"ServerEnvironment::addActiveObjectAsStatic: "
1398 <<"Could not find or generate "
1399 <<"a block for storing static object"<<std::endl;
1403 if(obj->environmentDeletes())
1411 Finds out what new objects have been added to
1412 inside a radius around a position
1414 void ServerEnvironment::getAddedActiveObjects(v3s16 pos, s16 radius,
1415 std::set<u16> ¤t_objects,
1416 std::set<u16> &added_objects)
1418 v3f pos_f = intToFloat(pos, BS);
1419 f32 radius_f = radius * BS;
1421 Go through the object list,
1422 - discard m_removed objects,
1423 - discard objects that are too far away,
1424 - discard objects that are found in current_objects.
1425 - add remaining objects to added_objects
1427 for(std::map<u16, ServerActiveObject*>::iterator
1428 i = m_active_objects.begin();
1429 i != m_active_objects.end(); ++i)
1433 ServerActiveObject *object = i->second;
1436 // Discard if removed or deactivating
1437 if(object->m_removed || object->m_pending_deactivation)
1439 if(object->unlimitedTransferDistance() == false){
1440 // Discard if too far
1441 f32 distance_f = object->getBasePosition().getDistanceFrom(pos_f);
1442 if(distance_f > radius_f)
1445 // Discard if already on current_objects
1446 std::set<u16>::iterator n;
1447 n = current_objects.find(id);
1448 if(n != current_objects.end())
1450 // Add to added_objects
1451 added_objects.insert(id);
1456 Finds out what objects have been removed from
1457 inside a radius around a position
1459 void ServerEnvironment::getRemovedActiveObjects(v3s16 pos, s16 radius,
1460 std::set<u16> ¤t_objects,
1461 std::set<u16> &removed_objects)
1463 v3f pos_f = intToFloat(pos, BS);
1464 f32 radius_f = radius * BS;
1466 Go through current_objects; object is removed if:
1467 - object is not found in m_active_objects (this is actually an
1468 error condition; objects should be set m_removed=true and removed
1469 only after all clients have been informed about removal), or
1470 - object has m_removed=true, or
1471 - object is too far away
1473 for(std::set<u16>::iterator
1474 i = current_objects.begin();
1475 i != current_objects.end(); ++i)
1478 ServerActiveObject *object = getActiveObject(id);
1481 infostream<<"ServerEnvironment::getRemovedActiveObjects():"
1482 <<" object in current_objects is NULL"<<std::endl;
1483 removed_objects.insert(id);
1487 if(object->m_removed || object->m_pending_deactivation)
1489 removed_objects.insert(id);
1493 // If transfer distance is unlimited, don't remove
1494 if(object->unlimitedTransferDistance())
1497 f32 distance_f = object->getBasePosition().getDistanceFrom(pos_f);
1499 if(distance_f >= radius_f)
1501 removed_objects.insert(id);
1509 ActiveObjectMessage ServerEnvironment::getActiveObjectMessage()
1511 if(m_active_object_messages.empty())
1512 return ActiveObjectMessage(0);
1514 ActiveObjectMessage message = m_active_object_messages.front();
1515 m_active_object_messages.pop_front();
1520 ************ Private methods *************
1523 u16 ServerEnvironment::addActiveObjectRaw(ServerActiveObject *object,
1524 bool set_changed, u32 dtime_s)
1527 if(object->getId() == 0){
1528 u16 new_id = getFreeServerActiveObjectId(m_active_objects);
1531 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1532 <<"no free ids available"<<std::endl;
1533 if(object->environmentDeletes())
1537 object->setId(new_id);
1540 verbosestream<<"ServerEnvironment::addActiveObjectRaw(): "
1541 <<"supplied with id "<<object->getId()<<std::endl;
1543 if(isFreeServerActiveObjectId(object->getId(), m_active_objects) == false)
1545 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1546 <<"id is not free ("<<object->getId()<<")"<<std::endl;
1547 if(object->environmentDeletes())
1551 /*infostream<<"ServerEnvironment::addActiveObjectRaw(): "
1552 <<"added (id="<<object->getId()<<")"<<std::endl;*/
1554 m_active_objects[object->getId()] = object;
1556 verbosestream<<"ServerEnvironment::addActiveObjectRaw(): "
1557 <<"Added id="<<object->getId()<<"; there are now "
1558 <<m_active_objects.size()<<" active objects."
1561 // Register reference in scripting api (must be done before post-init)
1562 m_script->addObjectReference(object);
1563 // Post-initialize object
1564 object->addedToEnvironment(dtime_s);
1566 // Add static data to block
1567 if(object->isStaticAllowed())
1569 // Add static object to active static list of the block
1570 v3f objectpos = object->getBasePosition();
1571 std::string staticdata = object->getStaticData();
1572 StaticObject s_obj(object->getType(), objectpos, staticdata);
1573 // Add to the block where the object is located in
1574 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1575 MapBlock *block = m_map->emergeBlock(blockpos);
1577 block->m_static_objects.m_active[object->getId()] = s_obj;
1578 object->m_static_exists = true;
1579 object->m_static_block = blockpos;
1582 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1583 "addActiveObjectRaw");
1585 v3s16 p = floatToInt(objectpos, BS);
1586 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1587 <<"could not emerge block for storing id="<<object->getId()
1588 <<" statically (pos="<<PP(p)<<")"<<std::endl;
1592 return object->getId();
1596 Remove objects that satisfy (m_removed && m_known_by_count==0)
1598 void ServerEnvironment::removeRemovedObjects()
1600 std::list<u16> objects_to_remove;
1601 for(std::map<u16, ServerActiveObject*>::iterator
1602 i = m_active_objects.begin();
1603 i != m_active_objects.end(); ++i)
1606 ServerActiveObject* obj = i->second;
1607 // This shouldn't happen but check it
1610 infostream<<"NULL object found in ServerEnvironment"
1611 <<" while finding removed objects. id="<<id<<std::endl;
1612 // Id to be removed from m_active_objects
1613 objects_to_remove.push_back(id);
1618 We will delete objects that are marked as removed or thatare
1619 waiting for deletion after deactivation
1621 if(obj->m_removed == false && obj->m_pending_deactivation == false)
1625 Delete static data from block if is marked as removed
1627 if(obj->m_static_exists && obj->m_removed)
1629 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
1631 block->m_static_objects.remove(id);
1632 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1633 "removeRemovedObjects/remove");
1634 obj->m_static_exists = false;
1636 infostream<<"Failed to emerge block from which an object to "
1637 <<"be removed was loaded from. id="<<id<<std::endl;
1641 // If m_known_by_count > 0, don't actually remove. On some future
1642 // invocation this will be 0, which is when removal will continue.
1643 if(obj->m_known_by_count > 0)
1647 Move static data from active to stored if not marked as removed
1649 if(obj->m_static_exists && !obj->m_removed){
1650 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
1652 std::map<u16, StaticObject>::iterator i =
1653 block->m_static_objects.m_active.find(id);
1654 if(i != block->m_static_objects.m_active.end()){
1655 block->m_static_objects.m_stored.push_back(i->second);
1656 block->m_static_objects.m_active.erase(id);
1657 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1658 "removeRemovedObjects/deactivate");
1661 infostream<<"Failed to emerge block from which an object to "
1662 <<"be deactivated was loaded from. id="<<id<<std::endl;
1666 // Tell the object about removal
1667 obj->removingFromEnvironment();
1668 // Deregister in scripting api
1669 m_script->removeObjectReference(obj);
1672 if(obj->environmentDeletes())
1674 // Id to be removed from m_active_objects
1675 objects_to_remove.push_back(id);
1677 // Remove references from m_active_objects
1678 for(std::list<u16>::iterator i = objects_to_remove.begin();
1679 i != objects_to_remove.end(); ++i)
1681 m_active_objects.erase(*i);
1685 static void print_hexdump(std::ostream &o, const std::string &data)
1687 const int linelength = 16;
1688 for(int l=0; ; l++){
1689 int i0 = linelength * l;
1690 bool at_end = false;
1691 int thislinelength = linelength;
1692 if(i0 + thislinelength > (int)data.size()){
1693 thislinelength = data.size() - i0;
1696 for(int di=0; di<linelength; di++){
1699 if(di<thislinelength)
1700 snprintf(buf, 4, "%.2x ", data[i]);
1702 snprintf(buf, 4, " ");
1706 for(int di=0; di<thislinelength; di++){
1720 Convert stored objects from blocks near the players to active.
1722 void ServerEnvironment::activateObjects(MapBlock *block, u32 dtime_s)
1726 // Ignore if no stored objects (to not set changed flag)
1727 if(block->m_static_objects.m_stored.size() == 0)
1729 verbosestream<<"ServerEnvironment::activateObjects(): "
1730 <<"activating objects of block "<<PP(block->getPos())
1731 <<" ("<<block->m_static_objects.m_stored.size()
1732 <<" objects)"<<std::endl;
1733 bool large_amount = (block->m_static_objects.m_stored.size() > g_settings->getU16("max_objects_per_block"));
1735 errorstream<<"suspiciously large amount of objects detected: "
1736 <<block->m_static_objects.m_stored.size()<<" in "
1737 <<PP(block->getPos())
1738 <<"; removing all of them."<<std::endl;
1739 // Clear stored list
1740 block->m_static_objects.m_stored.clear();
1741 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1742 "stored list cleared in activateObjects due to "
1743 "large amount of objects");
1747 // Activate stored objects
1748 std::list<StaticObject> new_stored;
1749 for(std::list<StaticObject>::iterator
1750 i = block->m_static_objects.m_stored.begin();
1751 i != block->m_static_objects.m_stored.end(); ++i)
1753 /*infostream<<"Server: Creating an active object from "
1754 <<"static data"<<std::endl;*/
1755 StaticObject &s_obj = *i;
1756 // Create an active object from the data
1757 ServerActiveObject *obj = ServerActiveObject::create
1758 (s_obj.type, this, 0, s_obj.pos, s_obj.data);
1759 // If couldn't create object, store static data back.
1762 errorstream<<"ServerEnvironment::activateObjects(): "
1763 <<"failed to create active object from static object "
1764 <<"in block "<<PP(s_obj.pos/BS)
1765 <<" type="<<(int)s_obj.type<<" data:"<<std::endl;
1766 print_hexdump(verbosestream, s_obj.data);
1768 new_stored.push_back(s_obj);
1771 verbosestream<<"ServerEnvironment::activateObjects(): "
1772 <<"activated static object pos="<<PP(s_obj.pos/BS)
1773 <<" type="<<(int)s_obj.type<<std::endl;
1774 // This will also add the object to the active static list
1775 addActiveObjectRaw(obj, false, dtime_s);
1777 // Clear stored list
1778 block->m_static_objects.m_stored.clear();
1779 // Add leftover failed stuff to stored list
1780 for(std::list<StaticObject>::iterator
1781 i = new_stored.begin();
1782 i != new_stored.end(); ++i)
1784 StaticObject &s_obj = *i;
1785 block->m_static_objects.m_stored.push_back(s_obj);
1788 // Turn the active counterparts of activated objects not pending for
1790 for(std::map<u16, StaticObject>::iterator
1791 i = block->m_static_objects.m_active.begin();
1792 i != block->m_static_objects.m_active.end(); ++i)
1795 ServerActiveObject *object = getActiveObject(id);
1797 object->m_pending_deactivation = false;
1801 Note: Block hasn't really been modified here.
1802 The objects have just been activated and moved from the stored
1803 static list to the active static list.
1804 As such, the block is essentially the same.
1805 Thus, do not call block->raiseModified(MOD_STATE_WRITE_NEEDED).
1806 Otherwise there would be a huge amount of unnecessary I/O.
1811 Convert objects that are not standing inside active blocks to static.
1813 If m_known_by_count != 0, active object is not deleted, but static
1814 data is still updated.
1816 If force_delete is set, active object is deleted nevertheless. It
1817 shall only be set so in the destructor of the environment.
1819 If block wasn't generated (not in memory or on disk),
1821 void ServerEnvironment::deactivateFarObjects(bool force_delete)
1823 std::list<u16> objects_to_remove;
1824 for(std::map<u16, ServerActiveObject*>::iterator
1825 i = m_active_objects.begin();
1826 i != m_active_objects.end(); ++i)
1828 ServerActiveObject* obj = i->second;
1831 // Do not deactivate if static data creation not allowed
1832 if(!force_delete && !obj->isStaticAllowed())
1835 // If pending deactivation, let removeRemovedObjects() do it
1836 if(!force_delete && obj->m_pending_deactivation)
1840 v3f objectpos = obj->getBasePosition();
1842 // The block in which the object resides in
1843 v3s16 blockpos_o = getNodeBlockPos(floatToInt(objectpos, BS));
1845 // If object's static data is stored in a deactivated block and object
1846 // is actually located in an active block, re-save to the block in
1847 // which the object is actually located in.
1849 obj->m_static_exists &&
1850 !m_active_blocks.contains(obj->m_static_block) &&
1851 m_active_blocks.contains(blockpos_o))
1853 v3s16 old_static_block = obj->m_static_block;
1855 // Save to block where object is located
1856 MapBlock *block = m_map->emergeBlock(blockpos_o, false);
1858 errorstream<<"ServerEnvironment::deactivateFarObjects(): "
1859 <<"Could not save object id="<<id
1860 <<" to it's current block "<<PP(blockpos_o)
1864 std::string staticdata_new = obj->getStaticData();
1865 StaticObject s_obj(obj->getType(), objectpos, staticdata_new);
1866 block->m_static_objects.insert(id, s_obj);
1867 obj->m_static_block = blockpos_o;
1868 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1869 "deactivateFarObjects: Static data moved in");
1871 // Delete from block where object was located
1872 block = m_map->emergeBlock(old_static_block, false);
1874 errorstream<<"ServerEnvironment::deactivateFarObjects(): "
1875 <<"Could not delete object id="<<id
1876 <<" from it's previous block "<<PP(old_static_block)
1880 block->m_static_objects.remove(id);
1881 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1882 "deactivateFarObjects: Static data moved out");
1886 // If block is active, don't remove
1887 if(!force_delete && m_active_blocks.contains(blockpos_o))
1890 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
1891 <<"deactivating object id="<<id<<" on inactive block "
1892 <<PP(blockpos_o)<<std::endl;
1894 // If known by some client, don't immediately delete.
1895 bool pending_delete = (obj->m_known_by_count > 0 && !force_delete);
1898 Update the static data
1901 if(obj->isStaticAllowed())
1903 // Create new static object
1904 std::string staticdata_new = obj->getStaticData();
1905 StaticObject s_obj(obj->getType(), objectpos, staticdata_new);
1907 bool stays_in_same_block = false;
1908 bool data_changed = true;
1910 if(obj->m_static_exists){
1911 if(obj->m_static_block == blockpos_o)
1912 stays_in_same_block = true;
1914 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
1916 std::map<u16, StaticObject>::iterator n =
1917 block->m_static_objects.m_active.find(id);
1918 if(n != block->m_static_objects.m_active.end()){
1919 StaticObject static_old = n->second;
1921 float save_movem = obj->getMinimumSavedMovement();
1923 if(static_old.data == staticdata_new &&
1924 (static_old.pos - objectpos).getLength() < save_movem)
1925 data_changed = false;
1927 errorstream<<"ServerEnvironment::deactivateFarObjects(): "
1928 <<"id="<<id<<" m_static_exists=true but "
1929 <<"static data doesn't actually exist in "
1930 <<PP(obj->m_static_block)<<std::endl;
1934 bool shall_be_written = (!stays_in_same_block || data_changed);
1936 // Delete old static object
1937 if(obj->m_static_exists)
1939 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
1942 block->m_static_objects.remove(id);
1943 obj->m_static_exists = false;
1944 // Only mark block as modified if data changed considerably
1945 if(shall_be_written)
1946 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1947 "deactivateFarObjects: Static data "
1948 "changed considerably");
1952 // Add to the block where the object is located in
1953 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1954 // Get or generate the block
1955 MapBlock *block = NULL;
1957 block = m_map->emergeBlock(blockpos);
1958 } catch(InvalidPositionException &e){
1959 // Handled via NULL pointer
1960 // NOTE: emergeBlock's failure is usually determined by it
1961 // actually returning NULL
1966 if(block->m_static_objects.m_stored.size() >= g_settings->getU16("max_objects_per_block")){
1967 errorstream<<"ServerEnv: Trying to store id="<<obj->getId()
1968 <<" statically but block "<<PP(blockpos)
1969 <<" already contains "
1970 <<block->m_static_objects.m_stored.size()
1972 <<" Forcing delete."<<std::endl;
1973 force_delete = true;
1975 // If static counterpart already exists in target block,
1977 // This shouldn't happen because the object is removed from
1978 // the previous block before this according to
1979 // obj->m_static_block, but happens rarely for some unknown
1980 // reason. Unsuccessful attempts have been made to find
1982 if(id && block->m_static_objects.m_active.find(id) != block->m_static_objects.m_active.end()){
1983 infostream<<"ServerEnv: WARNING: Performing hack #83274"
1985 block->m_static_objects.remove(id);
1987 // Store static data
1988 u16 store_id = pending_delete ? id : 0;
1989 block->m_static_objects.insert(store_id, s_obj);
1991 // Only mark block as modified if data changed considerably
1992 if(shall_be_written)
1993 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1994 "deactivateFarObjects: Static data "
1995 "changed considerably");
1997 obj->m_static_exists = true;
1998 obj->m_static_block = block->getPos();
2003 v3s16 p = floatToInt(objectpos, BS);
2004 errorstream<<"ServerEnv: Could not find or generate "
2005 <<"a block for storing id="<<obj->getId()
2006 <<" statically (pos="<<PP(p)<<")"<<std::endl;
2013 If known by some client, set pending deactivation.
2014 Otherwise delete it immediately.
2017 if(pending_delete && !force_delete)
2019 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
2020 <<"object id="<<id<<" is known by clients"
2021 <<"; not deleting yet"<<std::endl;
2023 obj->m_pending_deactivation = true;
2027 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
2028 <<"object id="<<id<<" is not known by clients"
2029 <<"; deleting"<<std::endl;
2031 // Tell the object about removal
2032 obj->removingFromEnvironment();
2033 // Deregister in scripting api
2034 m_script->removeObjectReference(obj);
2036 // Delete active object
2037 if(obj->environmentDeletes())
2039 // Id to be removed from m_active_objects
2040 objects_to_remove.push_back(id);
2043 // Remove references from m_active_objects
2044 for(std::list<u16>::iterator i = objects_to_remove.begin();
2045 i != objects_to_remove.end(); ++i)
2047 m_active_objects.erase(*i);
2054 #include "clientsimpleobject.h"
2060 ClientEnvironment::ClientEnvironment(ClientMap *map, scene::ISceneManager *smgr,
2061 ITextureSource *texturesource, IGameDef *gamedef,
2062 IrrlichtDevice *irr):
2065 m_texturesource(texturesource),
2071 ClientEnvironment::~ClientEnvironment()
2073 // delete active objects
2074 for(std::map<u16, ClientActiveObject*>::iterator
2075 i = m_active_objects.begin();
2076 i != m_active_objects.end(); ++i)
2081 for(std::list<ClientSimpleObject*>::iterator
2082 i = m_simple_objects.begin(); i != m_simple_objects.end(); ++i)
2091 Map & ClientEnvironment::getMap()
2096 ClientMap & ClientEnvironment::getClientMap()
2101 void ClientEnvironment::addPlayer(Player *player)
2103 DSTACK(__FUNCTION_NAME);
2105 It is a failure if player is local and there already is a local
2108 assert(!(player->isLocal() == true && getLocalPlayer() != NULL));
2110 Environment::addPlayer(player);
2113 LocalPlayer * ClientEnvironment::getLocalPlayer()
2115 for(std::list<Player*>::iterator i = m_players.begin();
2116 i != m_players.end(); ++i)
2118 Player *player = *i;
2119 if(player->isLocal())
2120 return (LocalPlayer*)player;
2125 void ClientEnvironment::step(float dtime)
2127 DSTACK(__FUNCTION_NAME);
2129 /* Step time of day */
2130 stepTimeOfDay(dtime);
2132 // Get some settings
2133 bool fly_allowed = m_gamedef->checkLocalPrivilege("fly");
2134 bool free_move = fly_allowed && g_settings->getBool("free_move");
2137 LocalPlayer *lplayer = getLocalPlayer();
2139 // collision info queue
2140 std::list<CollisionInfo> player_collisions;
2143 Get the speed the player is going
2145 bool is_climbing = lplayer->is_climbing;
2147 f32 player_speed = lplayer->getSpeed().getLength();
2150 Maximum position increment
2152 //f32 position_max_increment = 0.05*BS;
2153 f32 position_max_increment = 0.1*BS;
2155 // Maximum time increment (for collision detection etc)
2156 // time = distance / speed
2157 f32 dtime_max_increment = 1;
2158 if(player_speed > 0.001)
2159 dtime_max_increment = position_max_increment / player_speed;
2161 // Maximum time increment is 10ms or lower
2162 if(dtime_max_increment > 0.01)
2163 dtime_max_increment = 0.01;
2165 // Don't allow overly huge dtime
2169 f32 dtime_downcount = dtime;
2172 Stuff that has a maximum time increment
2181 if(dtime_downcount > dtime_max_increment)
2183 dtime_part = dtime_max_increment;
2184 dtime_downcount -= dtime_part;
2188 dtime_part = dtime_downcount;
2190 Setting this to 0 (no -=dtime_part) disables an infinite loop
2191 when dtime_part is so small that dtime_downcount -= dtime_part
2194 dtime_downcount = 0;
2203 if(free_move == false && is_climbing == false)
2206 v3f speed = lplayer->getSpeed();
2207 if(lplayer->in_liquid == false)
2208 speed.Y -= lplayer->movement_gravity * lplayer->physics_override_gravity * dtime_part * 2;
2210 // Liquid floating / sinking
2211 if(lplayer->in_liquid && !lplayer->swimming_vertical)
2212 speed.Y -= lplayer->movement_liquid_sink * dtime_part * 2;
2214 // Liquid resistance
2215 if(lplayer->in_liquid_stable || lplayer->in_liquid)
2217 // How much the node's viscosity blocks movement, ranges between 0 and 1
2218 // Should match the scale at which viscosity increase affects other liquid attributes
2219 const f32 viscosity_factor = 0.3;
2221 v3f d_wanted = -speed / lplayer->movement_liquid_fluidity;
2222 f32 dl = d_wanted.getLength();
2223 if(dl > lplayer->movement_liquid_fluidity_smooth)
2224 dl = lplayer->movement_liquid_fluidity_smooth;
2225 dl *= (lplayer->liquid_viscosity * viscosity_factor) + (1 - viscosity_factor);
2227 v3f d = d_wanted.normalize() * dl;
2231 if(speed.X > lplayer->movement_liquid_fluidity + lplayer->movement_liquid_fluidity_smooth) speed.X -= lplayer->movement_liquid_fluidity_smooth;
2232 if(speed.X < -lplayer->movement_liquid_fluidity - lplayer->movement_liquid_fluidity_smooth) speed.X += lplayer->movement_liquid_fluidity_smooth;
2233 if(speed.Y > lplayer->movement_liquid_fluidity + lplayer->movement_liquid_fluidity_smooth) speed.Y -= lplayer->movement_liquid_fluidity_smooth;
2234 if(speed.Y < -lplayer->movement_liquid_fluidity - lplayer->movement_liquid_fluidity_smooth) speed.Y += lplayer->movement_liquid_fluidity_smooth;
2235 if(speed.Z > lplayer->movement_liquid_fluidity + lplayer->movement_liquid_fluidity_smooth) speed.Z -= lplayer->movement_liquid_fluidity_smooth;
2236 if(speed.Z < -lplayer->movement_liquid_fluidity - lplayer->movement_liquid_fluidity_smooth) speed.Z += lplayer->movement_liquid_fluidity_smooth;
2240 lplayer->setSpeed(speed);
2245 This also does collision detection.
2247 lplayer->move(dtime_part, this, position_max_increment,
2248 &player_collisions);
2251 while(dtime_downcount > 0.001);
2253 //std::cout<<"Looped "<<loopcount<<" times."<<std::endl;
2255 for(std::list<CollisionInfo>::iterator
2256 i = player_collisions.begin();
2257 i != player_collisions.end(); ++i)
2259 CollisionInfo &info = *i;
2260 v3f speed_diff = info.new_speed - info.old_speed;;
2261 // Handle only fall damage
2262 // (because otherwise walking against something in fast_move kills you)
2263 if(speed_diff.Y < 0 || info.old_speed.Y >= 0)
2265 // Get rid of other components
2268 f32 pre_factor = 1; // 1 hp per node/s
2269 f32 tolerance = BS*14; // 5 without damage
2270 f32 post_factor = 1; // 1 hp per node/s
2271 if(info.type == COLLISION_NODE)
2273 const ContentFeatures &f = m_gamedef->ndef()->
2274 get(m_map->getNodeNoEx(info.node_p));
2275 // Determine fall damage multiplier
2276 int addp = itemgroup_get(f.groups, "fall_damage_add_percent");
2277 pre_factor = 1.0 + (float)addp/100.0;
2279 float speed = pre_factor * speed_diff.getLength();
2280 if(speed > tolerance)
2282 f32 damage_f = (speed - tolerance)/BS * post_factor;
2283 u16 damage = (u16)(damage_f+0.5);
2285 damageLocalPlayer(damage, true);
2286 MtEvent *e = new SimpleTriggerEvent("PlayerFallingDamage");
2287 m_gamedef->event()->put(e);
2293 A quick draft of lava damage
2295 if(m_lava_hurt_interval.step(dtime, 1.0))
2297 v3f pf = lplayer->getPosition();
2299 // Feet, middle and head
2300 v3s16 p1 = floatToInt(pf + v3f(0, BS*0.1, 0), BS);
2301 MapNode n1 = m_map->getNodeNoEx(p1);
2302 v3s16 p2 = floatToInt(pf + v3f(0, BS*0.8, 0), BS);
2303 MapNode n2 = m_map->getNodeNoEx(p2);
2304 v3s16 p3 = floatToInt(pf + v3f(0, BS*1.6, 0), BS);
2305 MapNode n3 = m_map->getNodeNoEx(p3);
2307 u32 damage_per_second = 0;
2308 damage_per_second = MYMAX(damage_per_second,
2309 m_gamedef->ndef()->get(n1).damage_per_second);
2310 damage_per_second = MYMAX(damage_per_second,
2311 m_gamedef->ndef()->get(n2).damage_per_second);
2312 damage_per_second = MYMAX(damage_per_second,
2313 m_gamedef->ndef()->get(n3).damage_per_second);
2315 if(damage_per_second != 0)
2317 damageLocalPlayer(damage_per_second, true);
2324 if(m_drowning_interval.step(dtime, 2.0))
2326 v3f pf = lplayer->getPosition();
2329 v3s16 p = floatToInt(pf + v3f(0, BS*1.6, 0), BS);
2330 MapNode n = m_map->getNodeNoEx(p);
2331 ContentFeatures c = m_gamedef->ndef()->get(n);
2332 u8 drowning_damage = c.drowning;
2333 if(drowning_damage > 0 && lplayer->hp > 0){
2334 u16 breath = lplayer->getBreath();
2341 lplayer->setBreath(breath);
2342 updateLocalPlayerBreath(breath);
2345 if(lplayer->getBreath() == 0 && drowning_damage > 0){
2346 damageLocalPlayer(drowning_damage, true);
2349 if(m_breathing_interval.step(dtime, 0.5))
2351 v3f pf = lplayer->getPosition();
2354 v3s16 p = floatToInt(pf + v3f(0, BS*1.6, 0), BS);
2355 MapNode n = m_map->getNodeNoEx(p);
2356 ContentFeatures c = m_gamedef->ndef()->get(n);
2358 lplayer->setBreath(11);
2360 else if(c.drowning == 0){
2361 u16 breath = lplayer->getBreath();
2364 lplayer->setBreath(breath);
2365 updateLocalPlayerBreath(breath);
2371 Stuff that can be done in an arbitarily large dtime
2373 for(std::list<Player*>::iterator i = m_players.begin();
2374 i != m_players.end(); ++i)
2376 Player *player = *i;
2379 Handle non-local players
2381 if(player->isLocal() == false)
2384 player->move(dtime, *m_map, 100*BS);
2388 // Update lighting on all players on client
2392 v3s16 p = player->getLightPosition();
2393 MapNode n = m_map->getNode(p);
2394 light = n.getLightBlendF1((float)getDayNightRatio()/1000, m_gamedef->ndef());
2396 catch(InvalidPositionException &e){
2397 light = blend_light_f1((float)getDayNightRatio()/1000, LIGHT_SUN, 0);
2399 player->light = light;
2403 Step active objects and update lighting of them
2406 g_profiler->avg("CEnv: num of objects", m_active_objects.size());
2407 bool update_lighting = m_active_object_light_update_interval.step(dtime, 0.21);
2408 for(std::map<u16, ClientActiveObject*>::iterator
2409 i = m_active_objects.begin();
2410 i != m_active_objects.end(); ++i)
2412 ClientActiveObject* obj = i->second;
2414 obj->step(dtime, this);
2422 v3s16 p = obj->getLightPosition();
2423 MapNode n = m_map->getNode(p);
2424 light = n.getLightBlend(getDayNightRatio(), m_gamedef->ndef());
2426 catch(InvalidPositionException &e){
2427 light = blend_light(getDayNightRatio(), LIGHT_SUN, 0);
2429 obj->updateLight(light);
2434 Step and handle simple objects
2436 g_profiler->avg("CEnv: num of simple objects", m_simple_objects.size());
2437 for(std::list<ClientSimpleObject*>::iterator
2438 i = m_simple_objects.begin(); i != m_simple_objects.end();)
2440 ClientSimpleObject *simple = *i;
2441 std::list<ClientSimpleObject*>::iterator cur = i;
2443 simple->step(dtime);
2444 if(simple->m_to_be_removed){
2446 m_simple_objects.erase(cur);
2451 void ClientEnvironment::addSimpleObject(ClientSimpleObject *simple)
2453 m_simple_objects.push_back(simple);
2456 ClientActiveObject* ClientEnvironment::getActiveObject(u16 id)
2458 std::map<u16, ClientActiveObject*>::iterator n;
2459 n = m_active_objects.find(id);
2460 if(n == m_active_objects.end())
2465 bool isFreeClientActiveObjectId(u16 id,
2466 std::map<u16, ClientActiveObject*> &objects)
2471 return objects.find(id) == objects.end();
2474 u16 getFreeClientActiveObjectId(
2475 std::map<u16, ClientActiveObject*> &objects)
2477 //try to reuse id's as late as possible
2478 static u16 last_used_id = 0;
2479 u16 startid = last_used_id;
2483 if(isFreeClientActiveObjectId(last_used_id, objects))
2484 return last_used_id;
2486 if(last_used_id == startid)
2491 u16 ClientEnvironment::addActiveObject(ClientActiveObject *object)
2494 if(object->getId() == 0)
2496 u16 new_id = getFreeClientActiveObjectId(m_active_objects);
2499 infostream<<"ClientEnvironment::addActiveObject(): "
2500 <<"no free ids available"<<std::endl;
2504 object->setId(new_id);
2506 if(isFreeClientActiveObjectId(object->getId(), m_active_objects) == false)
2508 infostream<<"ClientEnvironment::addActiveObject(): "
2509 <<"id is not free ("<<object->getId()<<")"<<std::endl;
2513 infostream<<"ClientEnvironment::addActiveObject(): "
2514 <<"added (id="<<object->getId()<<")"<<std::endl;
2515 m_active_objects[object->getId()] = object;
2516 object->addToScene(m_smgr, m_texturesource, m_irr);
2517 { // Update lighting immediately
2521 v3s16 p = object->getLightPosition();
2522 MapNode n = m_map->getNode(p);
2523 light = n.getLightBlend(getDayNightRatio(), m_gamedef->ndef());
2525 catch(InvalidPositionException &e){
2526 light = blend_light(getDayNightRatio(), LIGHT_SUN, 0);
2528 object->updateLight(light);
2530 return object->getId();
2533 void ClientEnvironment::addActiveObject(u16 id, u8 type,
2534 const std::string &init_data)
2536 ClientActiveObject* obj =
2537 ClientActiveObject::create(type, m_gamedef, this);
2540 infostream<<"ClientEnvironment::addActiveObject(): "
2541 <<"id="<<id<<" type="<<type<<": Couldn't create object"
2550 obj->initialize(init_data);
2552 catch(SerializationError &e)
2554 errorstream<<"ClientEnvironment::addActiveObject():"
2555 <<" id="<<id<<" type="<<type
2556 <<": SerializationError in initialize(): "
2558 <<": init_data="<<serializeJsonString(init_data)
2562 addActiveObject(obj);
2565 void ClientEnvironment::removeActiveObject(u16 id)
2567 verbosestream<<"ClientEnvironment::removeActiveObject(): "
2568 <<"id="<<id<<std::endl;
2569 ClientActiveObject* obj = getActiveObject(id);
2572 infostream<<"ClientEnvironment::removeActiveObject(): "
2573 <<"id="<<id<<" not found"<<std::endl;
2576 obj->removeFromScene(true);
2578 m_active_objects.erase(id);
2581 void ClientEnvironment::processActiveObjectMessage(u16 id,
2582 const std::string &data)
2584 ClientActiveObject* obj = getActiveObject(id);
2587 infostream<<"ClientEnvironment::processActiveObjectMessage():"
2588 <<" got message for id="<<id<<", which doesn't exist."
2594 obj->processMessage(data);
2596 catch(SerializationError &e)
2598 errorstream<<"ClientEnvironment::processActiveObjectMessage():"
2599 <<" id="<<id<<" type="<<obj->getType()
2600 <<" SerializationError in processMessage(),"
2601 <<" message="<<serializeJsonString(data)
2607 Callbacks for activeobjects
2610 void ClientEnvironment::damageLocalPlayer(u8 damage, bool handle_hp)
2612 LocalPlayer *lplayer = getLocalPlayer();
2616 if(lplayer->hp > damage)
2617 lplayer->hp -= damage;
2622 ClientEnvEvent event;
2623 event.type = CEE_PLAYER_DAMAGE;
2624 event.player_damage.amount = damage;
2625 event.player_damage.send_to_server = handle_hp;
2626 m_client_event_queue.push_back(event);
2629 void ClientEnvironment::updateLocalPlayerBreath(u16 breath)
2631 ClientEnvEvent event;
2632 event.type = CEE_PLAYER_BREATH;
2633 event.player_breath.amount = breath;
2634 m_client_event_queue.push_back(event);
2638 Client likes to call these
2641 void ClientEnvironment::getActiveObjects(v3f origin, f32 max_d,
2642 std::vector<DistanceSortedActiveObject> &dest)
2644 for(std::map<u16, ClientActiveObject*>::iterator
2645 i = m_active_objects.begin();
2646 i != m_active_objects.end(); ++i)
2648 ClientActiveObject* obj = i->second;
2650 f32 d = (obj->getPosition() - origin).getLength();
2655 DistanceSortedActiveObject dso(obj, d);
2657 dest.push_back(dso);
2661 ClientEnvEvent ClientEnvironment::getClientEvent()
2663 ClientEnvEvent event;
2664 if(m_client_event_queue.empty())
2665 event.type = CEE_NONE;
2667 event = m_client_event_queue.front();
2668 m_client_event_queue.pop_front();
2673 #endif // #ifndef SERVER