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.
21 #include "environment.h"
24 #include "collision.h"
25 #include "content_mapnode.h"
27 #include "serverobject.h"
28 #include "content_sao.h"
32 #include "scripting_game.h"
34 #include "nodemetadata.h"
37 #include "clientmap.h"
38 #include "localplayer.h"
39 #include "mapblock_mesh.h"
43 #include "daynightratio.h"
46 #include "util/serialize.h"
47 #include "threading/mutex_auto_lock.h"
49 #define PP(x) "("<<(x).X<<","<<(x).Y<<","<<(x).Z<<")"
51 Environment::Environment():
52 m_time_of_day_speed(0),
54 m_time_of_day_f(9000./24000),
55 m_time_conversion_skew(0.0f),
56 m_enable_day_night_ratio_override(false),
57 m_day_night_ratio_override(0.0f)
59 m_cache_enable_shaders = g_settings->getBool("enable_shaders");
62 Environment::~Environment()
65 for(std::vector<Player*>::iterator i = m_players.begin();
66 i != m_players.end(); ++i) {
71 void Environment::addPlayer(Player *player)
73 DSTACK(FUNCTION_NAME);
75 Check that peer_ids are unique.
76 Also check that names are unique.
77 Exception: there can be multiple players with peer_id=0
79 // If peer id is non-zero, it has to be unique.
80 if(player->peer_id != 0)
81 FATAL_ERROR_IF(getPlayer(player->peer_id) != NULL, "Peer id not unique");
82 // Name has to be unique.
83 FATAL_ERROR_IF(getPlayer(player->getName()) != NULL, "Player name not unique");
85 m_players.push_back(player);
88 void Environment::removePlayer(Player* player)
90 for (std::vector<Player*>::iterator it = m_players.begin();
91 it != m_players.end(); ++it) {
92 if ((*it) == player) {
100 Player * Environment::getPlayer(u16 peer_id)
102 for(std::vector<Player*>::iterator i = m_players.begin();
103 i != m_players.end(); ++i) {
105 if(player->peer_id == peer_id)
111 Player * Environment::getPlayer(const char *name)
113 for(std::vector<Player*>::iterator i = m_players.begin();
114 i != m_players.end(); ++i) {
116 if(strcmp(player->getName(), name) == 0)
122 Player * Environment::getRandomConnectedPlayer()
124 std::vector<Player*> connected_players = getPlayers(true);
125 u32 chosen_one = myrand() % connected_players.size();
127 for(std::vector<Player*>::iterator
128 i = connected_players.begin();
129 i != connected_players.end(); ++i) {
130 if(j == chosen_one) {
139 Player * Environment::getNearestConnectedPlayer(v3f pos)
141 std::vector<Player*> connected_players = getPlayers(true);
143 Player *nearest_player = NULL;
144 for(std::vector<Player*>::iterator
145 i = connected_players.begin();
146 i != connected_players.end(); ++i) {
148 f32 d = player->getPosition().getDistanceFrom(pos);
149 if(d < nearest_d || nearest_player == NULL) {
151 nearest_player = player;
154 return nearest_player;
157 std::vector<Player*> Environment::getPlayers()
162 std::vector<Player*> Environment::getPlayers(bool ignore_disconnected)
164 std::vector<Player*> newlist;
165 for(std::vector<Player*>::iterator
166 i = m_players.begin();
167 i != m_players.end(); ++i) {
170 if(ignore_disconnected) {
171 // Ignore disconnected players
172 if(player->peer_id == 0)
176 newlist.push_back(player);
181 u32 Environment::getDayNightRatio()
183 MutexAutoLock lock(this->m_time_lock);
184 if (m_enable_day_night_ratio_override)
185 return m_day_night_ratio_override;
186 return time_to_daynight_ratio(m_time_of_day_f * 24000, m_cache_enable_shaders);
189 void Environment::setTimeOfDaySpeed(float speed)
191 m_time_of_day_speed = speed;
194 float Environment::getTimeOfDaySpeed()
196 return m_time_of_day_speed;
199 void Environment::setDayNightRatioOverride(bool enable, u32 value)
201 MutexAutoLock lock(this->m_time_lock);
202 m_enable_day_night_ratio_override = enable;
203 m_day_night_ratio_override = value;
206 void Environment::setTimeOfDay(u32 time)
208 MutexAutoLock lock(this->m_time_lock);
209 m_time_of_day = time;
210 m_time_of_day_f = (float)time / 24000.0;
213 u32 Environment::getTimeOfDay()
215 MutexAutoLock lock(this->m_time_lock);
216 return m_time_of_day;
219 float Environment::getTimeOfDayF()
221 MutexAutoLock lock(this->m_time_lock);
222 return m_time_of_day_f;
225 void Environment::stepTimeOfDay(float dtime)
227 MutexAutoLock lock(this->m_time_lock);
229 // Cached in order to prevent the two reads we do to give
230 // different results (can be written by code not under the lock)
231 f32 cached_time_of_day_speed = m_time_of_day_speed;
233 f32 speed = cached_time_of_day_speed * 24000. / (24. * 3600);
234 m_time_conversion_skew += dtime;
235 u32 units = (u32)(m_time_conversion_skew * speed);
239 if (m_time_of_day + units >= 24000)
241 m_time_of_day = (m_time_of_day + units) % 24000;
243 m_time_of_day_f = (float)m_time_of_day / 24000.0;
246 m_time_conversion_skew -= (f32)units / speed;
249 m_time_of_day_f += cached_time_of_day_speed / 24 / 3600 * dtime;
250 if (m_time_of_day_f > 1.0)
251 m_time_of_day_f -= 1.0;
252 if (m_time_of_day_f < 0.0)
253 m_time_of_day_f += 1.0;
261 ABMWithState::ABMWithState(ActiveBlockModifier *abm_):
265 // Initialize timer to random value to spread processing
266 float itv = abm->getTriggerInterval();
267 itv = MYMAX(0.001, itv); // No less than 1ms
268 int minval = MYMAX(-0.51*itv, -60); // Clamp to
269 int maxval = MYMIN(0.51*itv, 60); // +-60 seconds
270 timer = myrand_range(minval, maxval);
277 void fillRadiusBlock(v3s16 p0, s16 r, std::set<v3s16> &list)
280 for(p.X=p0.X-r; p.X<=p0.X+r; p.X++)
281 for(p.Y=p0.Y-r; p.Y<=p0.Y+r; p.Y++)
282 for(p.Z=p0.Z-r; p.Z<=p0.Z+r; p.Z++)
289 void ActiveBlockList::update(std::vector<v3s16> &active_positions,
291 std::set<v3s16> &blocks_removed,
292 std::set<v3s16> &blocks_added)
297 std::set<v3s16> newlist = m_forceloaded_list;
298 for(std::vector<v3s16>::iterator i = active_positions.begin();
299 i != active_positions.end(); ++i)
301 fillRadiusBlock(*i, radius, newlist);
305 Find out which blocks on the old list are not on the new list
307 // Go through old list
308 for(std::set<v3s16>::iterator i = m_list.begin();
309 i != m_list.end(); ++i)
312 // If not on new list, it's been removed
313 if(newlist.find(p) == newlist.end())
314 blocks_removed.insert(p);
318 Find out which blocks on the new list are not on the old list
320 // Go through new list
321 for(std::set<v3s16>::iterator i = newlist.begin();
322 i != newlist.end(); ++i)
325 // If not on old list, it's been added
326 if(m_list.find(p) == m_list.end())
327 blocks_added.insert(p);
334 for(std::set<v3s16>::iterator i = newlist.begin();
335 i != newlist.end(); ++i)
346 ServerEnvironment::ServerEnvironment(ServerMap *map,
347 GameScripting *scriptIface, IGameDef *gamedef,
348 const std::string &path_world) :
350 m_script(scriptIface),
352 m_path_world(path_world),
353 m_send_recommended_timer(0),
354 m_active_block_interval_overload_skip(0),
356 m_game_time_fraction_counter(0),
357 m_recommended_send_interval(0.1),
358 m_max_lag_estimate(0.1)
362 ServerEnvironment::~ServerEnvironment()
364 // Clear active block list.
365 // This makes the next one delete all active objects.
366 m_active_blocks.clear();
368 // Convert all objects to static and delete the active objects
369 deactivateFarObjects(true);
374 // Delete ActiveBlockModifiers
375 for(std::vector<ABMWithState>::iterator
376 i = m_abms.begin(); i != m_abms.end(); ++i){
381 Map & ServerEnvironment::getMap()
386 ServerMap & ServerEnvironment::getServerMap()
391 bool ServerEnvironment::line_of_sight(v3f pos1, v3f pos2, float stepsize, v3s16 *p)
393 float distance = pos1.getDistanceFrom(pos2);
395 //calculate normalized direction vector
396 v3f normalized_vector = v3f((pos2.X - pos1.X)/distance,
397 (pos2.Y - pos1.Y)/distance,
398 (pos2.Z - pos1.Z)/distance);
400 //find out if there's a node on path between pos1 and pos2
401 for (float i = 1; i < distance; i += stepsize) {
402 v3s16 pos = floatToInt(v3f(normalized_vector.X * i,
403 normalized_vector.Y * i,
404 normalized_vector.Z * i) +pos1,BS);
406 MapNode n = getMap().getNodeNoEx(pos);
408 if(n.param0 != CONTENT_AIR) {
418 void ServerEnvironment::kickAllPlayers(AccessDeniedCode reason,
419 const std::string &str_reason, bool reconnect)
421 for (std::vector<Player*>::iterator it = m_players.begin();
422 it != m_players.end();
424 ((Server*)m_gamedef)->DenyAccessVerCompliant((*it)->peer_id,
425 (*it)->protocol_version, (AccessDeniedCode)reason,
426 str_reason, reconnect);
430 void ServerEnvironment::saveLoadedPlayers()
432 std::string players_path = m_path_world + DIR_DELIM "players";
433 fs::CreateDir(players_path);
435 for (std::vector<Player*>::iterator it = m_players.begin();
436 it != m_players.end();
438 RemotePlayer *player = static_cast<RemotePlayer*>(*it);
439 if (player->checkModified()) {
440 player->save(players_path);
445 void ServerEnvironment::savePlayer(RemotePlayer *player)
447 std::string players_path = m_path_world + DIR_DELIM "players";
448 fs::CreateDir(players_path);
450 player->save(players_path);
453 Player *ServerEnvironment::loadPlayer(const std::string &playername)
455 bool newplayer = false;
457 std::string players_path = m_path_world + DIR_DELIM "players" DIR_DELIM;
458 std::string path = players_path + playername;
460 RemotePlayer *player = static_cast<RemotePlayer *>(getPlayer(playername.c_str()));
462 player = new RemotePlayer(m_gamedef, "");
466 for (u32 i = 0; i < PLAYER_FILE_ALTERNATE_TRIES; i++) {
467 //// Open file and deserialize
468 std::ifstream is(path.c_str(), std::ios_base::binary);
471 player->deSerialize(is, path);
474 if (player->getName() == playername) {
479 path = players_path + playername + itos(i);
483 infostream << "Player file for player " << playername
484 << " not found" << std::endl;
492 player->setModified(false);
496 void ServerEnvironment::saveMeta()
498 std::string path = m_path_world + DIR_DELIM "env_meta.txt";
500 // Open file and serialize
501 std::ostringstream ss(std::ios_base::binary);
504 args.setU64("game_time", m_game_time);
505 args.setU64("time_of_day", getTimeOfDay());
509 if(!fs::safeWriteToFile(path, ss.str()))
511 infostream<<"ServerEnvironment::saveMeta(): Failed to write "
513 throw SerializationError("Couldn't save env meta");
517 void ServerEnvironment::loadMeta()
519 std::string path = m_path_world + DIR_DELIM "env_meta.txt";
521 // Open file and deserialize
522 std::ifstream is(path.c_str(), std::ios_base::binary);
524 infostream << "ServerEnvironment::loadMeta(): Failed to open "
525 << path << std::endl;
526 throw SerializationError("Couldn't load env meta");
531 if (!args.parseConfigLines(is, "EnvArgsEnd")) {
532 throw SerializationError("ServerEnvironment::loadMeta(): "
533 "EnvArgsEnd not found!");
537 m_game_time = args.getU64("game_time");
538 } catch (SettingNotFoundException &e) {
539 // Getting this is crucial, otherwise timestamps are useless
540 throw SerializationError("Couldn't load env meta game_time");
544 setTimeOfDay(args.getU64("time_of_day"));
545 } catch (SettingNotFoundException &e) {
546 // This is not as important
553 ActiveBlockModifier *abm;
555 std::set<content_t> required_neighbors;
561 ServerEnvironment *m_env;
562 std::map<content_t, std::vector<ActiveABM> > m_aabms;
564 ABMHandler(std::vector<ABMWithState> &abms,
565 float dtime_s, ServerEnvironment *env,
571 INodeDefManager *ndef = env->getGameDef()->ndef();
572 for(std::vector<ABMWithState>::iterator
573 i = abms.begin(); i != abms.end(); ++i) {
574 ActiveBlockModifier *abm = i->abm;
575 float trigger_interval = abm->getTriggerInterval();
576 if(trigger_interval < 0.001)
577 trigger_interval = 0.001;
578 float actual_interval = dtime_s;
581 if(i->timer < trigger_interval)
583 i->timer -= trigger_interval;
584 actual_interval = trigger_interval;
586 float chance = abm->getTriggerChance();
591 if(abm->getSimpleCatchUp()) {
592 float intervals = actual_interval / trigger_interval;
595 aabm.chance = chance / intervals;
599 aabm.chance = chance;
602 std::set<std::string> required_neighbors_s
603 = abm->getRequiredNeighbors();
604 for(std::set<std::string>::iterator
605 i = required_neighbors_s.begin();
606 i != required_neighbors_s.end(); ++i)
608 ndef->getIds(*i, aabm.required_neighbors);
611 std::set<std::string> contents_s = abm->getTriggerContents();
612 for(std::set<std::string>::iterator
613 i = contents_s.begin(); i != contents_s.end(); ++i)
615 std::set<content_t> ids;
616 ndef->getIds(*i, ids);
617 for(std::set<content_t>::const_iterator k = ids.begin();
621 std::map<content_t, std::vector<ActiveABM> >::iterator j;
623 if(j == m_aabms.end()){
624 std::vector<ActiveABM> aabmlist;
625 m_aabms[c] = aabmlist;
628 j->second.push_back(aabm);
633 // Find out how many objects the given block and its neighbours contain.
634 // Returns the number of objects in the block, and also in 'wider' the
635 // number of objects in the block and all its neighbours. The latter
636 // may an estimate if any neighbours are unloaded.
637 u32 countObjects(MapBlock *block, ServerMap * map, u32 &wider)
640 u32 wider_unknown_count = 0;
641 for(s16 x=-1; x<=1; x++)
642 for(s16 y=-1; y<=1; y++)
643 for(s16 z=-1; z<=1; z++)
645 MapBlock *block2 = map->getBlockNoCreateNoEx(
646 block->getPos() + v3s16(x,y,z));
648 wider_unknown_count++;
651 wider += block2->m_static_objects.m_active.size()
652 + block2->m_static_objects.m_stored.size();
655 u32 active_object_count = block->m_static_objects.m_active.size();
656 u32 wider_known_count = 3*3*3 - wider_unknown_count;
657 wider += wider_unknown_count * wider / wider_known_count;
658 return active_object_count;
661 void apply(MapBlock *block)
666 ServerMap *map = &m_env->getServerMap();
668 u32 active_object_count_wider;
669 u32 active_object_count = this->countObjects(block, map, active_object_count_wider);
670 m_env->m_added_objects = 0;
673 for(p0.X=0; p0.X<MAP_BLOCKSIZE; p0.X++)
674 for(p0.Y=0; p0.Y<MAP_BLOCKSIZE; p0.Y++)
675 for(p0.Z=0; p0.Z<MAP_BLOCKSIZE; p0.Z++)
677 MapNode n = block->getNodeNoEx(p0);
678 content_t c = n.getContent();
679 v3s16 p = p0 + block->getPosRelative();
681 std::map<content_t, std::vector<ActiveABM> >::iterator j;
683 if(j == m_aabms.end())
686 for(std::vector<ActiveABM>::iterator
687 i = j->second.begin(); i != j->second.end(); ++i) {
688 if(myrand() % i->chance != 0)
692 if(!i->required_neighbors.empty())
695 for(p1.X = p.X-1; p1.X <= p.X+1; p1.X++)
696 for(p1.Y = p.Y-1; p1.Y <= p.Y+1; p1.Y++)
697 for(p1.Z = p.Z-1; p1.Z <= p.Z+1; p1.Z++)
701 MapNode n = map->getNodeNoEx(p1);
702 content_t c = n.getContent();
703 std::set<content_t>::const_iterator k;
704 k = i->required_neighbors.find(c);
705 if(k != i->required_neighbors.end()){
709 // No required neighbor found
714 // Call all the trigger variations
715 i->abm->trigger(m_env, p, n);
716 i->abm->trigger(m_env, p, n,
717 active_object_count, active_object_count_wider);
719 // Count surrounding objects again if the abms added any
720 if(m_env->m_added_objects > 0) {
721 active_object_count = countObjects(block, map, active_object_count_wider);
722 m_env->m_added_objects = 0;
729 void ServerEnvironment::activateBlock(MapBlock *block, u32 additional_dtime)
731 // Reset usage timer immediately, otherwise a block that becomes active
732 // again at around the same time as it would normally be unloaded will
733 // get unloaded incorrectly. (I think this still leaves a small possibility
734 // of a race condition between this and server::AsyncRunStep, which only
735 // some kind of synchronisation will fix, but it at least reduces the window
736 // of opportunity for it to break from seconds to nanoseconds)
737 block->resetUsageTimer();
739 // Get time difference
741 u32 stamp = block->getTimestamp();
742 if(m_game_time > stamp && stamp != BLOCK_TIMESTAMP_UNDEFINED)
743 dtime_s = m_game_time - block->getTimestamp();
744 dtime_s += additional_dtime;
746 /*infostream<<"ServerEnvironment::activateBlock(): block timestamp: "
747 <<stamp<<", game time: "<<m_game_time<<std::endl;*/
749 // Set current time as timestamp
750 block->setTimestampNoChangedFlag(m_game_time);
752 /*infostream<<"ServerEnvironment::activateBlock(): block is "
753 <<dtime_s<<" seconds old."<<std::endl;*/
755 // Activate stored objects
756 activateObjects(block, dtime_s);
759 std::map<v3s16, NodeTimer> elapsed_timers =
760 block->m_node_timers.step((float)dtime_s);
761 if(!elapsed_timers.empty()){
763 for(std::map<v3s16, NodeTimer>::iterator
764 i = elapsed_timers.begin();
765 i != elapsed_timers.end(); ++i){
766 n = block->getNodeNoEx(i->first);
767 v3s16 p = i->first + block->getPosRelative();
768 if(m_script->node_on_timer(p,n,i->second.elapsed))
769 block->setNodeTimer(i->first,NodeTimer(i->second.timeout,0));
773 /* Handle ActiveBlockModifiers */
774 ABMHandler abmhandler(m_abms, dtime_s, this, false);
775 abmhandler.apply(block);
778 void ServerEnvironment::addActiveBlockModifier(ActiveBlockModifier *abm)
780 m_abms.push_back(ABMWithState(abm));
783 bool ServerEnvironment::setNode(v3s16 p, const MapNode &n)
785 INodeDefManager *ndef = m_gamedef->ndef();
786 MapNode n_old = m_map->getNodeNoEx(p);
789 if (ndef->get(n_old).has_on_destruct)
790 m_script->node_on_destruct(p, n_old);
793 if (!m_map->addNodeWithEvent(p, n))
796 // Update active VoxelManipulator if a mapgen thread
797 m_map->updateVManip(p);
799 // Call post-destructor
800 if (ndef->get(n_old).has_after_destruct)
801 m_script->node_after_destruct(p, n_old);
804 if (ndef->get(n).has_on_construct)
805 m_script->node_on_construct(p, n);
810 bool ServerEnvironment::removeNode(v3s16 p)
812 INodeDefManager *ndef = m_gamedef->ndef();
813 MapNode n_old = m_map->getNodeNoEx(p);
816 if (ndef->get(n_old).has_on_destruct)
817 m_script->node_on_destruct(p, n_old);
820 // This is slightly optimized compared to addNodeWithEvent(air)
821 if (!m_map->removeNodeWithEvent(p))
824 // Update active VoxelManipulator if a mapgen thread
825 m_map->updateVManip(p);
827 // Call post-destructor
828 if (ndef->get(n_old).has_after_destruct)
829 m_script->node_after_destruct(p, n_old);
831 // Air doesn't require constructor
835 bool ServerEnvironment::swapNode(v3s16 p, const MapNode &n)
837 if (!m_map->addNodeWithEvent(p, n, false))
840 // Update active VoxelManipulator if a mapgen thread
841 m_map->updateVManip(p);
846 void ServerEnvironment::getObjectsInsideRadius(std::vector<u16> &objects, v3f pos, float radius)
848 for(std::map<u16, ServerActiveObject*>::iterator
849 i = m_active_objects.begin();
850 i != m_active_objects.end(); ++i)
852 ServerActiveObject* obj = i->second;
854 v3f objectpos = obj->getBasePosition();
855 if(objectpos.getDistanceFrom(pos) > radius)
857 objects.push_back(id);
861 void ServerEnvironment::clearAllObjects()
863 infostream<<"ServerEnvironment::clearAllObjects(): "
864 <<"Removing all active objects"<<std::endl;
865 std::vector<u16> objects_to_remove;
866 for(std::map<u16, ServerActiveObject*>::iterator
867 i = m_active_objects.begin();
868 i != m_active_objects.end(); ++i) {
869 ServerActiveObject* obj = i->second;
870 if(obj->getType() == ACTIVEOBJECT_TYPE_PLAYER)
873 // Delete static object if block is loaded
874 if(obj->m_static_exists){
875 MapBlock *block = m_map->getBlockNoCreateNoEx(obj->m_static_block);
877 block->m_static_objects.remove(id);
878 block->raiseModified(MOD_STATE_WRITE_NEEDED,
879 MOD_REASON_CLEAR_ALL_OBJECTS);
880 obj->m_static_exists = false;
883 // If known by some client, don't delete immediately
884 if(obj->m_known_by_count > 0){
885 obj->m_pending_deactivation = true;
886 obj->m_removed = true;
890 // Tell the object about removal
891 obj->removingFromEnvironment();
892 // Deregister in scripting api
893 m_script->removeObjectReference(obj);
895 // Delete active object
896 if(obj->environmentDeletes())
898 // Id to be removed from m_active_objects
899 objects_to_remove.push_back(id);
902 // Remove references from m_active_objects
903 for(std::vector<u16>::iterator i = objects_to_remove.begin();
904 i != objects_to_remove.end(); ++i) {
905 m_active_objects.erase(*i);
908 // Get list of loaded blocks
909 std::vector<v3s16> loaded_blocks;
910 infostream<<"ServerEnvironment::clearAllObjects(): "
911 <<"Listing all loaded blocks"<<std::endl;
912 m_map->listAllLoadedBlocks(loaded_blocks);
913 infostream<<"ServerEnvironment::clearAllObjects(): "
914 <<"Done listing all loaded blocks: "
915 <<loaded_blocks.size()<<std::endl;
917 // Get list of loadable blocks
918 std::vector<v3s16> loadable_blocks;
919 infostream<<"ServerEnvironment::clearAllObjects(): "
920 <<"Listing all loadable blocks"<<std::endl;
921 m_map->listAllLoadableBlocks(loadable_blocks);
922 infostream<<"ServerEnvironment::clearAllObjects(): "
923 <<"Done listing all loadable blocks: "
924 <<loadable_blocks.size()
925 <<", now clearing"<<std::endl;
927 // Grab a reference on each loaded block to avoid unloading it
928 for(std::vector<v3s16>::iterator i = loaded_blocks.begin();
929 i != loaded_blocks.end(); ++i) {
931 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
932 assert(block != NULL);
936 // Remove objects in all loadable blocks
937 u32 unload_interval = g_settings->getS32("max_clearobjects_extra_loaded_blocks");
938 unload_interval = MYMAX(unload_interval, 1);
939 u32 report_interval = loadable_blocks.size() / 10;
940 u32 num_blocks_checked = 0;
941 u32 num_blocks_cleared = 0;
942 u32 num_objs_cleared = 0;
943 for(std::vector<v3s16>::iterator i = loadable_blocks.begin();
944 i != loadable_blocks.end(); ++i) {
946 MapBlock *block = m_map->emergeBlock(p, false);
948 errorstream<<"ServerEnvironment::clearAllObjects(): "
949 <<"Failed to emerge block "<<PP(p)<<std::endl;
952 u32 num_stored = block->m_static_objects.m_stored.size();
953 u32 num_active = block->m_static_objects.m_active.size();
954 if(num_stored != 0 || num_active != 0){
955 block->m_static_objects.m_stored.clear();
956 block->m_static_objects.m_active.clear();
957 block->raiseModified(MOD_STATE_WRITE_NEEDED,
958 MOD_REASON_CLEAR_ALL_OBJECTS);
959 num_objs_cleared += num_stored + num_active;
960 num_blocks_cleared++;
962 num_blocks_checked++;
964 if(report_interval != 0 &&
965 num_blocks_checked % report_interval == 0){
966 float percent = 100.0 * (float)num_blocks_checked /
967 loadable_blocks.size();
968 infostream<<"ServerEnvironment::clearAllObjects(): "
969 <<"Cleared "<<num_objs_cleared<<" objects"
970 <<" in "<<num_blocks_cleared<<" blocks ("
971 <<percent<<"%)"<<std::endl;
973 if(num_blocks_checked % unload_interval == 0){
974 m_map->unloadUnreferencedBlocks();
977 m_map->unloadUnreferencedBlocks();
979 // Drop references that were added above
980 for(std::vector<v3s16>::iterator i = loaded_blocks.begin();
981 i != loaded_blocks.end(); ++i) {
983 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
988 infostream<<"ServerEnvironment::clearAllObjects(): "
989 <<"Finished: Cleared "<<num_objs_cleared<<" objects"
990 <<" in "<<num_blocks_cleared<<" blocks"<<std::endl;
993 void ServerEnvironment::step(float dtime)
995 DSTACK(FUNCTION_NAME);
997 //TimeTaker timer("ServerEnv step");
999 /* Step time of day */
1000 stepTimeOfDay(dtime);
1003 // NOTE: This is kind of funny on a singleplayer game, but doesn't
1004 // really matter that much.
1005 m_recommended_send_interval = g_settings->getFloat("dedicated_server_step");
1011 m_game_time_fraction_counter += dtime;
1012 u32 inc_i = (u32)m_game_time_fraction_counter;
1013 m_game_time += inc_i;
1014 m_game_time_fraction_counter -= (float)inc_i;
1021 ScopeProfiler sp(g_profiler, "SEnv: handle players avg", SPT_AVG);
1022 for(std::vector<Player*>::iterator i = m_players.begin();
1023 i != m_players.end(); ++i)
1025 Player *player = *i;
1027 // Ignore disconnected players
1028 if(player->peer_id == 0)
1032 player->move(dtime, this, 100*BS);
1037 Manage active block list
1039 if(m_active_blocks_management_interval.step(dtime, 2.0))
1041 ScopeProfiler sp(g_profiler, "SEnv: manage act. block list avg /2s", SPT_AVG);
1043 Get player block positions
1045 std::vector<v3s16> players_blockpos;
1046 for(std::vector<Player*>::iterator
1047 i = m_players.begin();
1048 i != m_players.end(); ++i) {
1049 Player *player = *i;
1050 // Ignore disconnected players
1051 if(player->peer_id == 0)
1054 v3s16 blockpos = getNodeBlockPos(
1055 floatToInt(player->getPosition(), BS));
1056 players_blockpos.push_back(blockpos);
1060 Update list of active blocks, collecting changes
1062 const s16 active_block_range = g_settings->getS16("active_block_range");
1063 std::set<v3s16> blocks_removed;
1064 std::set<v3s16> blocks_added;
1065 m_active_blocks.update(players_blockpos, active_block_range,
1066 blocks_removed, blocks_added);
1069 Handle removed blocks
1072 // Convert active objects that are no more in active blocks to static
1073 deactivateFarObjects(false);
1075 for(std::set<v3s16>::iterator
1076 i = blocks_removed.begin();
1077 i != blocks_removed.end(); ++i)
1081 /* infostream<<"Server: Block " << PP(p)
1082 << " became inactive"<<std::endl; */
1084 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1088 // Set current time as timestamp (and let it set ChangedFlag)
1089 block->setTimestamp(m_game_time);
1096 for(std::set<v3s16>::iterator
1097 i = blocks_added.begin();
1098 i != blocks_added.end(); ++i)
1102 MapBlock *block = m_map->getBlockOrEmerge(p);
1104 m_active_blocks.m_list.erase(p);
1108 activateBlock(block);
1109 /* infostream<<"Server: Block " << PP(p)
1110 << " became active"<<std::endl; */
1115 Mess around in active blocks
1117 if(m_active_blocks_nodemetadata_interval.step(dtime, 1.0))
1119 ScopeProfiler sp(g_profiler, "SEnv: mess in act. blocks avg /1s", SPT_AVG);
1123 for(std::set<v3s16>::iterator
1124 i = m_active_blocks.m_list.begin();
1125 i != m_active_blocks.m_list.end(); ++i)
1129 /*infostream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
1130 <<") being handled"<<std::endl;*/
1132 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1136 // Reset block usage timer
1137 block->resetUsageTimer();
1139 // Set current time as timestamp
1140 block->setTimestampNoChangedFlag(m_game_time);
1141 // If time has changed much from the one on disk,
1142 // set block to be saved when it is unloaded
1143 if(block->getTimestamp() > block->getDiskTimestamp() + 60)
1144 block->raiseModified(MOD_STATE_WRITE_AT_UNLOAD,
1145 MOD_REASON_BLOCK_EXPIRED);
1148 std::map<v3s16, NodeTimer> elapsed_timers =
1149 block->m_node_timers.step((float)dtime);
1150 if(!elapsed_timers.empty()){
1152 for(std::map<v3s16, NodeTimer>::iterator
1153 i = elapsed_timers.begin();
1154 i != elapsed_timers.end(); ++i){
1155 n = block->getNodeNoEx(i->first);
1156 p = i->first + block->getPosRelative();
1157 if(m_script->node_on_timer(p,n,i->second.elapsed))
1158 block->setNodeTimer(i->first,NodeTimer(i->second.timeout,0));
1164 const float abm_interval = 1.0;
1165 if(m_active_block_modifier_interval.step(dtime, abm_interval))
1167 if(m_active_block_interval_overload_skip > 0){
1168 ScopeProfiler sp(g_profiler, "SEnv: ABM overload skips");
1169 m_active_block_interval_overload_skip--;
1172 ScopeProfiler sp(g_profiler, "SEnv: modify in blocks avg /1s", SPT_AVG);
1173 TimeTaker timer("modify in active blocks");
1175 // Initialize handling of ActiveBlockModifiers
1176 ABMHandler abmhandler(m_abms, abm_interval, this, true);
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 // Set current time as timestamp
1192 block->setTimestampNoChangedFlag(m_game_time);
1194 /* Handle ActiveBlockModifiers */
1195 abmhandler.apply(block);
1198 u32 time_ms = timer.stop(true);
1199 u32 max_time_ms = 200;
1200 if(time_ms > max_time_ms){
1201 warningstream<<"active block modifiers took "
1202 <<time_ms<<"ms (longer than "
1203 <<max_time_ms<<"ms)"<<std::endl;
1204 m_active_block_interval_overload_skip = (time_ms / max_time_ms) + 1;
1209 Step script environment (run global on_step())
1211 m_script->environment_Step(dtime);
1217 ScopeProfiler sp(g_profiler, "SEnv: step act. objs avg", SPT_AVG);
1218 //TimeTaker timer("Step active objects");
1220 g_profiler->avg("SEnv: num of objects", m_active_objects.size());
1222 // This helps the objects to send data at the same time
1223 bool send_recommended = false;
1224 m_send_recommended_timer += dtime;
1225 if(m_send_recommended_timer > getSendRecommendedInterval())
1227 m_send_recommended_timer -= getSendRecommendedInterval();
1228 send_recommended = true;
1231 for(std::map<u16, ServerActiveObject*>::iterator
1232 i = m_active_objects.begin();
1233 i != m_active_objects.end(); ++i)
1235 ServerActiveObject* obj = i->second;
1236 // Don't step if is to be removed or stored statically
1237 if(obj->m_removed || obj->m_pending_deactivation)
1240 obj->step(dtime, send_recommended);
1241 // Read messages from object
1242 while(!obj->m_messages_out.empty())
1244 m_active_object_messages.push(
1245 obj->m_messages_out.front());
1246 obj->m_messages_out.pop();
1252 Manage active objects
1254 if(m_object_management_interval.step(dtime, 0.5))
1256 ScopeProfiler sp(g_profiler, "SEnv: remove removed objs avg /.5s", SPT_AVG);
1258 Remove objects that satisfy (m_removed && m_known_by_count==0)
1260 removeRemovedObjects();
1264 ServerActiveObject* ServerEnvironment::getActiveObject(u16 id)
1266 std::map<u16, ServerActiveObject*>::iterator n;
1267 n = m_active_objects.find(id);
1268 if(n == m_active_objects.end())
1273 bool isFreeServerActiveObjectId(u16 id,
1274 std::map<u16, ServerActiveObject*> &objects)
1279 return objects.find(id) == objects.end();
1282 u16 getFreeServerActiveObjectId(
1283 std::map<u16, ServerActiveObject*> &objects)
1285 //try to reuse id's as late as possible
1286 static u16 last_used_id = 0;
1287 u16 startid = last_used_id;
1291 if(isFreeServerActiveObjectId(last_used_id, objects))
1292 return last_used_id;
1294 if(last_used_id == startid)
1299 u16 ServerEnvironment::addActiveObject(ServerActiveObject *object)
1301 assert(object); // Pre-condition
1303 u16 id = addActiveObjectRaw(object, true, 0);
1308 Finds out what new objects have been added to
1309 inside a radius around a position
1311 void ServerEnvironment::getAddedActiveObjects(Player *player, s16 radius,
1313 std::set<u16> ¤t_objects,
1314 std::queue<u16> &added_objects)
1316 f32 radius_f = radius * BS;
1317 f32 player_radius_f = player_radius * BS;
1319 if (player_radius_f < 0)
1320 player_radius_f = 0;
1323 Go through the object list,
1324 - discard m_removed objects,
1325 - discard objects that are too far away,
1326 - discard objects that are found in current_objects.
1327 - add remaining objects to added_objects
1329 for(std::map<u16, ServerActiveObject*>::iterator
1330 i = m_active_objects.begin();
1331 i != m_active_objects.end(); ++i) {
1335 ServerActiveObject *object = i->second;
1339 // Discard if removed or deactivating
1340 if(object->m_removed || object->m_pending_deactivation)
1343 f32 distance_f = object->getBasePosition().getDistanceFrom(player->getPosition());
1344 if (object->getType() == ACTIVEOBJECT_TYPE_PLAYER) {
1345 // Discard if too far
1346 if (distance_f > player_radius_f && player_radius_f != 0)
1348 } else if (distance_f > radius_f)
1351 // Discard if already on current_objects
1352 std::set<u16>::iterator n;
1353 n = current_objects.find(id);
1354 if(n != current_objects.end())
1356 // Add to added_objects
1357 added_objects.push(id);
1362 Finds out what objects have been removed from
1363 inside a radius around a position
1365 void ServerEnvironment::getRemovedActiveObjects(Player *player, s16 radius,
1367 std::set<u16> ¤t_objects,
1368 std::queue<u16> &removed_objects)
1370 f32 radius_f = radius * BS;
1371 f32 player_radius_f = player_radius * BS;
1373 if (player_radius_f < 0)
1374 player_radius_f = 0;
1377 Go through current_objects; object is removed if:
1378 - object is not found in m_active_objects (this is actually an
1379 error condition; objects should be set m_removed=true and removed
1380 only after all clients have been informed about removal), or
1381 - object has m_removed=true, or
1382 - object is too far away
1384 for(std::set<u16>::iterator
1385 i = current_objects.begin();
1386 i != current_objects.end(); ++i)
1389 ServerActiveObject *object = getActiveObject(id);
1391 if (object == NULL) {
1392 infostream << "ServerEnvironment::getRemovedActiveObjects():"
1393 << " object in current_objects is NULL" << std::endl;
1394 removed_objects.push(id);
1398 if (object->m_removed || object->m_pending_deactivation) {
1399 removed_objects.push(id);
1403 f32 distance_f = object->getBasePosition().getDistanceFrom(player->getPosition());
1404 if (object->getType() == ACTIVEOBJECT_TYPE_PLAYER) {
1405 if (distance_f <= player_radius_f || player_radius_f == 0)
1407 } else if (distance_f <= radius_f)
1410 // Object is no longer visible
1411 removed_objects.push(id);
1415 void ServerEnvironment::setStaticForActiveObjectsInBlock(
1416 v3s16 blockpos, bool static_exists, v3s16 static_block)
1418 MapBlock *block = m_map->getBlockNoCreateNoEx(blockpos);
1422 for (std::map<u16, StaticObject>::iterator
1423 so_it = block->m_static_objects.m_active.begin();
1424 so_it != block->m_static_objects.m_active.end(); ++so_it) {
1425 // Get the ServerActiveObject counterpart to this StaticObject
1426 std::map<u16, ServerActiveObject *>::iterator ao_it;
1427 ao_it = m_active_objects.find(so_it->first);
1428 if (ao_it == m_active_objects.end()) {
1429 // If this ever happens, there must be some kind of nasty bug.
1430 errorstream << "ServerEnvironment::setStaticForObjectsInBlock(): "
1431 "Object from MapBlock::m_static_objects::m_active not found "
1432 "in m_active_objects";
1436 ServerActiveObject *sao = ao_it->second;
1437 sao->m_static_exists = static_exists;
1438 sao->m_static_block = static_block;
1442 ActiveObjectMessage ServerEnvironment::getActiveObjectMessage()
1444 if(m_active_object_messages.empty())
1445 return ActiveObjectMessage(0);
1447 ActiveObjectMessage message = m_active_object_messages.front();
1448 m_active_object_messages.pop();
1453 ************ Private methods *************
1456 u16 ServerEnvironment::addActiveObjectRaw(ServerActiveObject *object,
1457 bool set_changed, u32 dtime_s)
1459 assert(object); // Pre-condition
1460 if(object->getId() == 0){
1461 u16 new_id = getFreeServerActiveObjectId(m_active_objects);
1464 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1465 <<"no free ids available"<<std::endl;
1466 if(object->environmentDeletes())
1470 object->setId(new_id);
1473 verbosestream<<"ServerEnvironment::addActiveObjectRaw(): "
1474 <<"supplied with id "<<object->getId()<<std::endl;
1476 if(isFreeServerActiveObjectId(object->getId(), m_active_objects) == false)
1478 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1479 <<"id is not free ("<<object->getId()<<")"<<std::endl;
1480 if(object->environmentDeletes())
1485 if (objectpos_over_limit(object->getBasePosition())) {
1486 v3f p = object->getBasePosition();
1487 errorstream << "ServerEnvironment::addActiveObjectRaw(): "
1488 << "object position (" << p.X << "," << p.Y << "," << p.Z
1489 << ") outside maximum range" << std::endl;
1490 if (object->environmentDeletes())
1495 /*infostream<<"ServerEnvironment::addActiveObjectRaw(): "
1496 <<"added (id="<<object->getId()<<")"<<std::endl;*/
1498 m_active_objects[object->getId()] = object;
1500 verbosestream<<"ServerEnvironment::addActiveObjectRaw(): "
1501 <<"Added id="<<object->getId()<<"; there are now "
1502 <<m_active_objects.size()<<" active objects."
1505 // Register reference in scripting api (must be done before post-init)
1506 m_script->addObjectReference(object);
1507 // Post-initialize object
1508 object->addedToEnvironment(dtime_s);
1510 // Add static data to block
1511 if(object->isStaticAllowed())
1513 // Add static object to active static list of the block
1514 v3f objectpos = object->getBasePosition();
1515 std::string staticdata = object->getStaticData();
1516 StaticObject s_obj(object->getType(), objectpos, staticdata);
1517 // Add to the block where the object is located in
1518 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1519 MapBlock *block = m_map->emergeBlock(blockpos);
1521 block->m_static_objects.m_active[object->getId()] = s_obj;
1522 object->m_static_exists = true;
1523 object->m_static_block = blockpos;
1526 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1527 MOD_REASON_ADD_ACTIVE_OBJECT_RAW);
1529 v3s16 p = floatToInt(objectpos, BS);
1530 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1531 <<"could not emerge block for storing id="<<object->getId()
1532 <<" statically (pos="<<PP(p)<<")"<<std::endl;
1536 return object->getId();
1540 Remove objects that satisfy (m_removed && m_known_by_count==0)
1542 void ServerEnvironment::removeRemovedObjects()
1544 std::vector<u16> objects_to_remove;
1545 for(std::map<u16, ServerActiveObject*>::iterator
1546 i = m_active_objects.begin();
1547 i != m_active_objects.end(); ++i) {
1549 ServerActiveObject* obj = i->second;
1550 // This shouldn't happen but check it
1553 infostream<<"NULL object found in ServerEnvironment"
1554 <<" while finding removed objects. id="<<id<<std::endl;
1555 // Id to be removed from m_active_objects
1556 objects_to_remove.push_back(id);
1561 We will delete objects that are marked as removed or thatare
1562 waiting for deletion after deactivation
1564 if(obj->m_removed == false && obj->m_pending_deactivation == false)
1568 Delete static data from block if is marked as removed
1570 if(obj->m_static_exists && obj->m_removed)
1572 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
1574 block->m_static_objects.remove(id);
1575 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1576 MOD_REASON_REMOVE_OBJECTS_REMOVE);
1577 obj->m_static_exists = false;
1579 infostream<<"Failed to emerge block from which an object to "
1580 <<"be removed was loaded from. id="<<id<<std::endl;
1584 // If m_known_by_count > 0, don't actually remove. On some future
1585 // invocation this will be 0, which is when removal will continue.
1586 if(obj->m_known_by_count > 0)
1590 Move static data from active to stored if not marked as removed
1592 if(obj->m_static_exists && !obj->m_removed){
1593 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
1595 std::map<u16, StaticObject>::iterator i =
1596 block->m_static_objects.m_active.find(id);
1597 if(i != block->m_static_objects.m_active.end()){
1598 block->m_static_objects.m_stored.push_back(i->second);
1599 block->m_static_objects.m_active.erase(id);
1600 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1601 MOD_REASON_REMOVE_OBJECTS_DEACTIVATE);
1604 infostream<<"Failed to emerge block from which an object to "
1605 <<"be deactivated was loaded from. id="<<id<<std::endl;
1609 // Tell the object about removal
1610 obj->removingFromEnvironment();
1611 // Deregister in scripting api
1612 m_script->removeObjectReference(obj);
1615 if(obj->environmentDeletes())
1618 // Id to be removed from m_active_objects
1619 objects_to_remove.push_back(id);
1621 // Remove references from m_active_objects
1622 for(std::vector<u16>::iterator i = objects_to_remove.begin();
1623 i != objects_to_remove.end(); ++i) {
1624 m_active_objects.erase(*i);
1628 static void print_hexdump(std::ostream &o, const std::string &data)
1630 const int linelength = 16;
1631 for(int l=0; ; l++){
1632 int i0 = linelength * l;
1633 bool at_end = false;
1634 int thislinelength = linelength;
1635 if(i0 + thislinelength > (int)data.size()){
1636 thislinelength = data.size() - i0;
1639 for(int di=0; di<linelength; di++){
1642 if(di<thislinelength)
1643 snprintf(buf, 4, "%.2x ", data[i]);
1645 snprintf(buf, 4, " ");
1649 for(int di=0; di<thislinelength; di++){
1663 Convert stored objects from blocks near the players to active.
1665 void ServerEnvironment::activateObjects(MapBlock *block, u32 dtime_s)
1670 // Ignore if no stored objects (to not set changed flag)
1671 if(block->m_static_objects.m_stored.empty())
1674 verbosestream<<"ServerEnvironment::activateObjects(): "
1675 <<"activating objects of block "<<PP(block->getPos())
1676 <<" ("<<block->m_static_objects.m_stored.size()
1677 <<" objects)"<<std::endl;
1678 bool large_amount = (block->m_static_objects.m_stored.size() > g_settings->getU16("max_objects_per_block"));
1680 errorstream<<"suspiciously large amount of objects detected: "
1681 <<block->m_static_objects.m_stored.size()<<" in "
1682 <<PP(block->getPos())
1683 <<"; removing all of them."<<std::endl;
1684 // Clear stored list
1685 block->m_static_objects.m_stored.clear();
1686 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1687 MOD_REASON_TOO_MANY_OBJECTS);
1691 // Activate stored objects
1692 std::vector<StaticObject> new_stored;
1693 for (std::vector<StaticObject>::iterator
1694 i = block->m_static_objects.m_stored.begin();
1695 i != block->m_static_objects.m_stored.end(); ++i) {
1696 StaticObject &s_obj = *i;
1698 // Create an active object from the data
1699 ServerActiveObject *obj = ServerActiveObject::create
1700 ((ActiveObjectType) s_obj.type, this, 0, s_obj.pos, s_obj.data);
1701 // If couldn't create object, store static data back.
1703 errorstream<<"ServerEnvironment::activateObjects(): "
1704 <<"failed to create active object from static object "
1705 <<"in block "<<PP(s_obj.pos/BS)
1706 <<" type="<<(int)s_obj.type<<" data:"<<std::endl;
1707 print_hexdump(verbosestream, s_obj.data);
1709 new_stored.push_back(s_obj);
1712 verbosestream<<"ServerEnvironment::activateObjects(): "
1713 <<"activated static object pos="<<PP(s_obj.pos/BS)
1714 <<" type="<<(int)s_obj.type<<std::endl;
1715 // This will also add the object to the active static list
1716 addActiveObjectRaw(obj, false, dtime_s);
1718 // Clear stored list
1719 block->m_static_objects.m_stored.clear();
1720 // Add leftover failed stuff to stored list
1721 for(std::vector<StaticObject>::iterator
1722 i = new_stored.begin();
1723 i != new_stored.end(); ++i) {
1724 StaticObject &s_obj = *i;
1725 block->m_static_objects.m_stored.push_back(s_obj);
1728 // Turn the active counterparts of activated objects not pending for
1730 for(std::map<u16, StaticObject>::iterator
1731 i = block->m_static_objects.m_active.begin();
1732 i != block->m_static_objects.m_active.end(); ++i)
1735 ServerActiveObject *object = getActiveObject(id);
1737 object->m_pending_deactivation = false;
1741 Note: Block hasn't really been modified here.
1742 The objects have just been activated and moved from the stored
1743 static list to the active static list.
1744 As such, the block is essentially the same.
1745 Thus, do not call block->raiseModified(MOD_STATE_WRITE_NEEDED).
1746 Otherwise there would be a huge amount of unnecessary I/O.
1751 Convert objects that are not standing inside active blocks to static.
1753 If m_known_by_count != 0, active object is not deleted, but static
1754 data is still updated.
1756 If force_delete is set, active object is deleted nevertheless. It
1757 shall only be set so in the destructor of the environment.
1759 If block wasn't generated (not in memory or on disk),
1761 void ServerEnvironment::deactivateFarObjects(bool force_delete)
1763 std::vector<u16> objects_to_remove;
1764 for(std::map<u16, ServerActiveObject*>::iterator
1765 i = m_active_objects.begin();
1766 i != m_active_objects.end(); ++i) {
1767 ServerActiveObject* obj = i->second;
1770 // Do not deactivate if static data creation not allowed
1771 if(!force_delete && !obj->isStaticAllowed())
1774 // If pending deactivation, let removeRemovedObjects() do it
1775 if(!force_delete && obj->m_pending_deactivation)
1779 v3f objectpos = obj->getBasePosition();
1781 // The block in which the object resides in
1782 v3s16 blockpos_o = getNodeBlockPos(floatToInt(objectpos, BS));
1784 // If object's static data is stored in a deactivated block and object
1785 // is actually located in an active block, re-save to the block in
1786 // which the object is actually located in.
1788 obj->m_static_exists &&
1789 !m_active_blocks.contains(obj->m_static_block) &&
1790 m_active_blocks.contains(blockpos_o))
1792 v3s16 old_static_block = obj->m_static_block;
1794 // Save to block where object is located
1795 MapBlock *block = m_map->emergeBlock(blockpos_o, false);
1797 errorstream<<"ServerEnvironment::deactivateFarObjects(): "
1798 <<"Could not save object id="<<id
1799 <<" to it's current block "<<PP(blockpos_o)
1803 std::string staticdata_new = obj->getStaticData();
1804 StaticObject s_obj(obj->getType(), objectpos, staticdata_new);
1805 block->m_static_objects.insert(id, s_obj);
1806 obj->m_static_block = blockpos_o;
1807 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1808 MOD_REASON_STATIC_DATA_ADDED);
1810 // Delete from block where object was located
1811 block = m_map->emergeBlock(old_static_block, false);
1813 errorstream<<"ServerEnvironment::deactivateFarObjects(): "
1814 <<"Could not delete object id="<<id
1815 <<" from it's previous block "<<PP(old_static_block)
1819 block->m_static_objects.remove(id);
1820 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1821 MOD_REASON_STATIC_DATA_REMOVED);
1825 // If block is active, don't remove
1826 if(!force_delete && m_active_blocks.contains(blockpos_o))
1829 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
1830 <<"deactivating object id="<<id<<" on inactive block "
1831 <<PP(blockpos_o)<<std::endl;
1833 // If known by some client, don't immediately delete.
1834 bool pending_delete = (obj->m_known_by_count > 0 && !force_delete);
1837 Update the static data
1840 if(obj->isStaticAllowed())
1842 // Create new static object
1843 std::string staticdata_new = obj->getStaticData();
1844 StaticObject s_obj(obj->getType(), objectpos, staticdata_new);
1846 bool stays_in_same_block = false;
1847 bool data_changed = true;
1849 if (obj->m_static_exists) {
1850 if (obj->m_static_block == blockpos_o)
1851 stays_in_same_block = true;
1853 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
1856 std::map<u16, StaticObject>::iterator n =
1857 block->m_static_objects.m_active.find(id);
1858 if (n != block->m_static_objects.m_active.end()) {
1859 StaticObject static_old = n->second;
1861 float save_movem = obj->getMinimumSavedMovement();
1863 if (static_old.data == staticdata_new &&
1864 (static_old.pos - objectpos).getLength() < save_movem)
1865 data_changed = false;
1867 errorstream<<"ServerEnvironment::deactivateFarObjects(): "
1868 <<"id="<<id<<" m_static_exists=true but "
1869 <<"static data doesn't actually exist in "
1870 <<PP(obj->m_static_block)<<std::endl;
1875 bool shall_be_written = (!stays_in_same_block || data_changed);
1877 // Delete old static object
1878 if(obj->m_static_exists)
1880 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
1883 block->m_static_objects.remove(id);
1884 obj->m_static_exists = false;
1885 // Only mark block as modified if data changed considerably
1886 if(shall_be_written)
1887 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1888 MOD_REASON_STATIC_DATA_CHANGED);
1892 // Add to the block where the object is located in
1893 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1894 // Get or generate the block
1895 MapBlock *block = NULL;
1897 block = m_map->emergeBlock(blockpos);
1898 } catch(InvalidPositionException &e){
1899 // Handled via NULL pointer
1900 // NOTE: emergeBlock's failure is usually determined by it
1901 // actually returning NULL
1906 if(block->m_static_objects.m_stored.size() >= g_settings->getU16("max_objects_per_block")){
1907 errorstream<<"ServerEnv: Trying to store id="<<obj->getId()
1908 <<" statically but block "<<PP(blockpos)
1909 <<" already contains "
1910 <<block->m_static_objects.m_stored.size()
1912 <<" Forcing delete."<<std::endl;
1913 force_delete = true;
1915 // If static counterpart already exists in target block,
1917 // This shouldn't happen because the object is removed from
1918 // the previous block before this according to
1919 // obj->m_static_block, but happens rarely for some unknown
1920 // reason. Unsuccessful attempts have been made to find
1922 if(id && block->m_static_objects.m_active.find(id) != block->m_static_objects.m_active.end()){
1923 warningstream<<"ServerEnv: Performing hack #83274"
1925 block->m_static_objects.remove(id);
1927 // Store static data
1928 u16 store_id = pending_delete ? id : 0;
1929 block->m_static_objects.insert(store_id, s_obj);
1931 // Only mark block as modified if data changed considerably
1932 if(shall_be_written)
1933 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1934 MOD_REASON_STATIC_DATA_CHANGED);
1936 obj->m_static_exists = true;
1937 obj->m_static_block = block->getPos();
1942 v3s16 p = floatToInt(objectpos, BS);
1943 errorstream<<"ServerEnv: Could not find or generate "
1944 <<"a block for storing id="<<obj->getId()
1945 <<" statically (pos="<<PP(p)<<")"<<std::endl;
1952 If known by some client, set pending deactivation.
1953 Otherwise delete it immediately.
1956 if(pending_delete && !force_delete)
1958 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
1959 <<"object id="<<id<<" is known by clients"
1960 <<"; not deleting yet"<<std::endl;
1962 obj->m_pending_deactivation = true;
1966 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
1967 <<"object id="<<id<<" is not known by clients"
1968 <<"; deleting"<<std::endl;
1970 // Tell the object about removal
1971 obj->removingFromEnvironment();
1972 // Deregister in scripting api
1973 m_script->removeObjectReference(obj);
1975 // Delete active object
1976 if(obj->environmentDeletes())
1978 // Id to be removed from m_active_objects
1979 objects_to_remove.push_back(id);
1982 // Remove references from m_active_objects
1983 for(std::vector<u16>::iterator i = objects_to_remove.begin();
1984 i != objects_to_remove.end(); ++i) {
1985 m_active_objects.erase(*i);
1991 #include "clientsimpleobject.h"
1997 ClientEnvironment::ClientEnvironment(ClientMap *map, scene::ISceneManager *smgr,
1998 ITextureSource *texturesource, IGameDef *gamedef,
1999 IrrlichtDevice *irr):
2002 m_texturesource(texturesource),
2007 memset(attachement_parent_ids, zero, sizeof(attachement_parent_ids));
2010 ClientEnvironment::~ClientEnvironment()
2012 // delete active objects
2013 for(std::map<u16, ClientActiveObject*>::iterator
2014 i = m_active_objects.begin();
2015 i != m_active_objects.end(); ++i)
2020 for(std::vector<ClientSimpleObject*>::iterator
2021 i = m_simple_objects.begin(); i != m_simple_objects.end(); ++i) {
2029 Map & ClientEnvironment::getMap()
2034 ClientMap & ClientEnvironment::getClientMap()
2039 void ClientEnvironment::addPlayer(Player *player)
2041 DSTACK(FUNCTION_NAME);
2043 It is a failure if player is local and there already is a local
2046 FATAL_ERROR_IF(player->isLocal() == true && getLocalPlayer() != NULL,
2047 "Player is local but there is already a local player");
2049 Environment::addPlayer(player);
2052 LocalPlayer * ClientEnvironment::getLocalPlayer()
2054 for(std::vector<Player*>::iterator i = m_players.begin();
2055 i != m_players.end(); ++i) {
2056 Player *player = *i;
2057 if(player->isLocal())
2058 return (LocalPlayer*)player;
2063 void ClientEnvironment::step(float dtime)
2065 DSTACK(FUNCTION_NAME);
2067 /* Step time of day */
2068 stepTimeOfDay(dtime);
2070 // Get some settings
2071 bool fly_allowed = m_gamedef->checkLocalPrivilege("fly");
2072 bool free_move = fly_allowed && g_settings->getBool("free_move");
2075 LocalPlayer *lplayer = getLocalPlayer();
2077 // collision info queue
2078 std::vector<CollisionInfo> player_collisions;
2081 Get the speed the player is going
2083 bool is_climbing = lplayer->is_climbing;
2085 f32 player_speed = lplayer->getSpeed().getLength();
2088 Maximum position increment
2090 //f32 position_max_increment = 0.05*BS;
2091 f32 position_max_increment = 0.1*BS;
2093 // Maximum time increment (for collision detection etc)
2094 // time = distance / speed
2095 f32 dtime_max_increment = 1;
2096 if(player_speed > 0.001)
2097 dtime_max_increment = position_max_increment / player_speed;
2099 // Maximum time increment is 10ms or lower
2100 if(dtime_max_increment > 0.01)
2101 dtime_max_increment = 0.01;
2103 // Don't allow overly huge dtime
2107 f32 dtime_downcount = dtime;
2110 Stuff that has a maximum time increment
2119 if(dtime_downcount > dtime_max_increment)
2121 dtime_part = dtime_max_increment;
2122 dtime_downcount -= dtime_part;
2126 dtime_part = dtime_downcount;
2128 Setting this to 0 (no -=dtime_part) disables an infinite loop
2129 when dtime_part is so small that dtime_downcount -= dtime_part
2132 dtime_downcount = 0;
2141 if(free_move == false && is_climbing == false)
2144 v3f speed = lplayer->getSpeed();
2145 if(lplayer->in_liquid == false)
2146 speed.Y -= lplayer->movement_gravity * lplayer->physics_override_gravity * dtime_part * 2;
2148 // Liquid floating / sinking
2149 if(lplayer->in_liquid && !lplayer->swimming_vertical)
2150 speed.Y -= lplayer->movement_liquid_sink * dtime_part * 2;
2152 // Liquid resistance
2153 if(lplayer->in_liquid_stable || lplayer->in_liquid)
2155 // How much the node's viscosity blocks movement, ranges between 0 and 1
2156 // Should match the scale at which viscosity increase affects other liquid attributes
2157 const f32 viscosity_factor = 0.3;
2159 v3f d_wanted = -speed / lplayer->movement_liquid_fluidity;
2160 f32 dl = d_wanted.getLength();
2161 if(dl > lplayer->movement_liquid_fluidity_smooth)
2162 dl = lplayer->movement_liquid_fluidity_smooth;
2163 dl *= (lplayer->liquid_viscosity * viscosity_factor) + (1 - viscosity_factor);
2165 v3f d = d_wanted.normalize() * dl;
2169 lplayer->setSpeed(speed);
2174 This also does collision detection.
2176 lplayer->move(dtime_part, this, position_max_increment,
2177 &player_collisions);
2180 while(dtime_downcount > 0.001);
2182 //std::cout<<"Looped "<<loopcount<<" times."<<std::endl;
2184 for(std::vector<CollisionInfo>::iterator i = player_collisions.begin();
2185 i != player_collisions.end(); ++i) {
2186 CollisionInfo &info = *i;
2187 v3f speed_diff = info.new_speed - info.old_speed;;
2188 // Handle only fall damage
2189 // (because otherwise walking against something in fast_move kills you)
2190 if(speed_diff.Y < 0 || info.old_speed.Y >= 0)
2192 // Get rid of other components
2195 f32 pre_factor = 1; // 1 hp per node/s
2196 f32 tolerance = BS*14; // 5 without damage
2197 f32 post_factor = 1; // 1 hp per node/s
2198 if(info.type == COLLISION_NODE)
2200 const ContentFeatures &f = m_gamedef->ndef()->
2201 get(m_map->getNodeNoEx(info.node_p));
2202 // Determine fall damage multiplier
2203 int addp = itemgroup_get(f.groups, "fall_damage_add_percent");
2204 pre_factor = 1.0 + (float)addp/100.0;
2206 float speed = pre_factor * speed_diff.getLength();
2207 if(speed > tolerance)
2209 f32 damage_f = (speed - tolerance)/BS * post_factor;
2210 u16 damage = (u16)(damage_f+0.5);
2212 damageLocalPlayer(damage, true);
2213 MtEvent *e = new SimpleTriggerEvent("PlayerFallingDamage");
2214 m_gamedef->event()->put(e);
2220 A quick draft of lava damage
2222 if(m_lava_hurt_interval.step(dtime, 1.0))
2224 v3f pf = lplayer->getPosition();
2226 // Feet, middle and head
2227 v3s16 p1 = floatToInt(pf + v3f(0, BS*0.1, 0), BS);
2228 MapNode n1 = m_map->getNodeNoEx(p1);
2229 v3s16 p2 = floatToInt(pf + v3f(0, BS*0.8, 0), BS);
2230 MapNode n2 = m_map->getNodeNoEx(p2);
2231 v3s16 p3 = floatToInt(pf + v3f(0, BS*1.6, 0), BS);
2232 MapNode n3 = m_map->getNodeNoEx(p3);
2234 u32 damage_per_second = 0;
2235 damage_per_second = MYMAX(damage_per_second,
2236 m_gamedef->ndef()->get(n1).damage_per_second);
2237 damage_per_second = MYMAX(damage_per_second,
2238 m_gamedef->ndef()->get(n2).damage_per_second);
2239 damage_per_second = MYMAX(damage_per_second,
2240 m_gamedef->ndef()->get(n3).damage_per_second);
2242 if(damage_per_second != 0)
2244 damageLocalPlayer(damage_per_second, true);
2251 if(m_drowning_interval.step(dtime, 2.0))
2253 v3f pf = lplayer->getPosition();
2256 v3s16 p = floatToInt(pf + v3f(0, BS*1.6, 0), BS);
2257 MapNode n = m_map->getNodeNoEx(p);
2258 ContentFeatures c = m_gamedef->ndef()->get(n);
2259 u8 drowning_damage = c.drowning;
2260 if(drowning_damage > 0 && lplayer->hp > 0){
2261 u16 breath = lplayer->getBreath();
2268 lplayer->setBreath(breath);
2269 updateLocalPlayerBreath(breath);
2272 if(lplayer->getBreath() == 0 && drowning_damage > 0){
2273 damageLocalPlayer(drowning_damage, true);
2276 if(m_breathing_interval.step(dtime, 0.5))
2278 v3f pf = lplayer->getPosition();
2281 v3s16 p = floatToInt(pf + v3f(0, BS*1.6, 0), BS);
2282 MapNode n = m_map->getNodeNoEx(p);
2283 ContentFeatures c = m_gamedef->ndef()->get(n);
2285 lplayer->setBreath(11);
2287 else if(c.drowning == 0){
2288 u16 breath = lplayer->getBreath();
2291 lplayer->setBreath(breath);
2292 updateLocalPlayerBreath(breath);
2298 Stuff that can be done in an arbitarily large dtime
2300 for(std::vector<Player*>::iterator i = m_players.begin();
2301 i != m_players.end(); ++i) {
2302 Player *player = *i;
2305 Handle non-local players
2307 if(player->isLocal() == false) {
2309 player->move(dtime, this, 100*BS);
2314 // Update lighting on local player (used for wield item)
2315 u32 day_night_ratio = getDayNightRatio();
2319 // On InvalidPositionException, use this as default
2320 // (day: LIGHT_SUN, night: 0)
2321 MapNode node_at_lplayer(CONTENT_AIR, 0x0f, 0);
2323 v3s16 p = lplayer->getLightPosition();
2324 node_at_lplayer = m_map->getNodeNoEx(p);
2326 u16 light = getInteriorLight(node_at_lplayer, 0, m_gamedef->ndef());
2327 u8 day = light & 0xff;
2328 u8 night = (light >> 8) & 0xff;
2329 finalColorBlend(lplayer->light_color, day, night, day_night_ratio);
2333 Step active objects and update lighting of them
2336 g_profiler->avg("CEnv: num of objects", m_active_objects.size());
2337 bool update_lighting = m_active_object_light_update_interval.step(dtime, 0.21);
2338 for(std::map<u16, ClientActiveObject*>::iterator
2339 i = m_active_objects.begin();
2340 i != m_active_objects.end(); ++i)
2342 ClientActiveObject* obj = i->second;
2344 obj->step(dtime, this);
2353 v3s16 p = obj->getLightPosition();
2354 MapNode n = m_map->getNodeNoEx(p, &pos_ok);
2356 light = n.getLightBlend(day_night_ratio, m_gamedef->ndef());
2358 light = blend_light(day_night_ratio, LIGHT_SUN, 0);
2360 obj->updateLight(light);
2365 Step and handle simple objects
2367 g_profiler->avg("CEnv: num of simple objects", m_simple_objects.size());
2368 for(std::vector<ClientSimpleObject*>::iterator
2369 i = m_simple_objects.begin(); i != m_simple_objects.end();) {
2370 std::vector<ClientSimpleObject*>::iterator cur = i;
2371 ClientSimpleObject *simple = *cur;
2373 simple->step(dtime);
2374 if(simple->m_to_be_removed) {
2376 i = m_simple_objects.erase(cur);
2384 void ClientEnvironment::addSimpleObject(ClientSimpleObject *simple)
2386 m_simple_objects.push_back(simple);
2389 GenericCAO* ClientEnvironment::getGenericCAO(u16 id)
2391 ClientActiveObject *obj = getActiveObject(id);
2392 if (obj && obj->getType() == ACTIVEOBJECT_TYPE_GENERIC)
2393 return (GenericCAO*) obj;
2398 ClientActiveObject* ClientEnvironment::getActiveObject(u16 id)
2400 std::map<u16, ClientActiveObject*>::iterator n;
2401 n = m_active_objects.find(id);
2402 if(n == m_active_objects.end())
2407 bool isFreeClientActiveObjectId(u16 id,
2408 std::map<u16, ClientActiveObject*> &objects)
2413 return objects.find(id) == objects.end();
2416 u16 getFreeClientActiveObjectId(
2417 std::map<u16, ClientActiveObject*> &objects)
2419 //try to reuse id's as late as possible
2420 static u16 last_used_id = 0;
2421 u16 startid = last_used_id;
2425 if(isFreeClientActiveObjectId(last_used_id, objects))
2426 return last_used_id;
2428 if(last_used_id == startid)
2433 u16 ClientEnvironment::addActiveObject(ClientActiveObject *object)
2435 assert(object); // Pre-condition
2436 if(object->getId() == 0)
2438 u16 new_id = getFreeClientActiveObjectId(m_active_objects);
2441 infostream<<"ClientEnvironment::addActiveObject(): "
2442 <<"no free ids available"<<std::endl;
2446 object->setId(new_id);
2448 if(isFreeClientActiveObjectId(object->getId(), m_active_objects) == false)
2450 infostream<<"ClientEnvironment::addActiveObject(): "
2451 <<"id is not free ("<<object->getId()<<")"<<std::endl;
2455 infostream<<"ClientEnvironment::addActiveObject(): "
2456 <<"added (id="<<object->getId()<<")"<<std::endl;
2457 m_active_objects[object->getId()] = object;
2458 object->addToScene(m_smgr, m_texturesource, m_irr);
2459 { // Update lighting immediately
2464 v3s16 p = object->getLightPosition();
2465 MapNode n = m_map->getNodeNoEx(p, &pos_ok);
2467 light = n.getLightBlend(getDayNightRatio(), m_gamedef->ndef());
2469 light = blend_light(getDayNightRatio(), LIGHT_SUN, 0);
2471 object->updateLight(light);
2473 return object->getId();
2476 void ClientEnvironment::addActiveObject(u16 id, u8 type,
2477 const std::string &init_data)
2479 ClientActiveObject* obj =
2480 ClientActiveObject::create((ActiveObjectType) type, m_gamedef, this);
2483 infostream<<"ClientEnvironment::addActiveObject(): "
2484 <<"id="<<id<<" type="<<type<<": Couldn't create object"
2493 obj->initialize(init_data);
2495 catch(SerializationError &e)
2497 errorstream<<"ClientEnvironment::addActiveObject():"
2498 <<" id="<<id<<" type="<<type
2499 <<": SerializationError in initialize(): "
2501 <<": init_data="<<serializeJsonString(init_data)
2505 addActiveObject(obj);
2508 void ClientEnvironment::removeActiveObject(u16 id)
2510 verbosestream<<"ClientEnvironment::removeActiveObject(): "
2511 <<"id="<<id<<std::endl;
2512 ClientActiveObject* obj = getActiveObject(id);
2515 infostream<<"ClientEnvironment::removeActiveObject(): "
2516 <<"id="<<id<<" not found"<<std::endl;
2519 obj->removeFromScene(true);
2521 m_active_objects.erase(id);
2524 void ClientEnvironment::processActiveObjectMessage(u16 id, const std::string &data)
2526 ClientActiveObject *obj = getActiveObject(id);
2528 infostream << "ClientEnvironment::processActiveObjectMessage():"
2529 << " got message for id=" << id << ", which doesn't exist."
2535 obj->processMessage(data);
2536 } catch (SerializationError &e) {
2537 errorstream<<"ClientEnvironment::processActiveObjectMessage():"
2538 << " id=" << id << " type=" << obj->getType()
2539 << " SerializationError in processMessage(): " << e.what()
2545 Callbacks for activeobjects
2548 void ClientEnvironment::damageLocalPlayer(u8 damage, bool handle_hp)
2550 LocalPlayer *lplayer = getLocalPlayer();
2554 if (lplayer->hp > damage)
2555 lplayer->hp -= damage;
2560 ClientEnvEvent event;
2561 event.type = CEE_PLAYER_DAMAGE;
2562 event.player_damage.amount = damage;
2563 event.player_damage.send_to_server = handle_hp;
2564 m_client_event_queue.push(event);
2567 void ClientEnvironment::updateLocalPlayerBreath(u16 breath)
2569 ClientEnvEvent event;
2570 event.type = CEE_PLAYER_BREATH;
2571 event.player_breath.amount = breath;
2572 m_client_event_queue.push(event);
2576 Client likes to call these
2579 void ClientEnvironment::getActiveObjects(v3f origin, f32 max_d,
2580 std::vector<DistanceSortedActiveObject> &dest)
2582 for(std::map<u16, ClientActiveObject*>::iterator
2583 i = m_active_objects.begin();
2584 i != m_active_objects.end(); ++i)
2586 ClientActiveObject* obj = i->second;
2588 f32 d = (obj->getPosition() - origin).getLength();
2593 DistanceSortedActiveObject dso(obj, d);
2595 dest.push_back(dso);
2599 ClientEnvEvent ClientEnvironment::getClientEvent()
2601 ClientEnvEvent event;
2602 if(m_client_event_queue.empty())
2603 event.type = CEE_NONE;
2605 event = m_client_event_queue.front();
2606 m_client_event_queue.pop();
2611 #endif // #ifndef SERVER