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 "jthread/jmutexautolock.h"
49 #define PP(x) "("<<(x).X<<","<<(x).Y<<","<<(x).Z<<")"
51 Environment::Environment():
53 m_time_of_day_f(9000./24000),
54 m_time_of_day_speed(0),
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(u16 peer_id)
90 DSTACK(__FUNCTION_NAME);
92 for(std::vector<Player*>::iterator i = m_players.begin();
93 i != m_players.end();)
96 if(player->peer_id == peer_id) {
98 i = m_players.erase(i);
105 void Environment::removePlayer(const char *name)
107 for (std::vector<Player*>::iterator it = m_players.begin();
108 it != m_players.end(); ++it) {
109 if (strcmp((*it)->getName(), name) == 0) {
117 Player * Environment::getPlayer(u16 peer_id)
119 for(std::vector<Player*>::iterator i = m_players.begin();
120 i != m_players.end(); ++i) {
122 if(player->peer_id == peer_id)
128 Player * Environment::getPlayer(const char *name)
130 for(std::vector<Player*>::iterator i = m_players.begin();
131 i != m_players.end(); ++i) {
133 if(strcmp(player->getName(), name) == 0)
139 Player * Environment::getRandomConnectedPlayer()
141 std::vector<Player*> connected_players = getPlayers(true);
142 u32 chosen_one = myrand() % connected_players.size();
144 for(std::vector<Player*>::iterator
145 i = connected_players.begin();
146 i != connected_players.end(); ++i) {
147 if(j == chosen_one) {
156 Player * Environment::getNearestConnectedPlayer(v3f pos)
158 std::vector<Player*> connected_players = getPlayers(true);
160 Player *nearest_player = NULL;
161 for(std::vector<Player*>::iterator
162 i = connected_players.begin();
163 i != connected_players.end(); ++i) {
165 f32 d = player->getPosition().getDistanceFrom(pos);
166 if(d < nearest_d || nearest_player == NULL) {
168 nearest_player = player;
171 return nearest_player;
174 std::vector<Player*> Environment::getPlayers()
179 std::vector<Player*> Environment::getPlayers(bool ignore_disconnected)
181 std::vector<Player*> newlist;
182 for(std::vector<Player*>::iterator
183 i = m_players.begin();
184 i != m_players.end(); ++i) {
187 if(ignore_disconnected) {
188 // Ignore disconnected players
189 if(player->peer_id == 0)
193 newlist.push_back(player);
198 u32 Environment::getDayNightRatio()
200 if(m_enable_day_night_ratio_override)
201 return m_day_night_ratio_override;
202 return time_to_daynight_ratio(m_time_of_day_f*24000, m_cache_enable_shaders);
205 void Environment::setTimeOfDaySpeed(float speed)
207 JMutexAutoLock(this->m_timeofday_lock);
208 m_time_of_day_speed = speed;
211 float Environment::getTimeOfDaySpeed()
213 JMutexAutoLock(this->m_timeofday_lock);
214 float retval = m_time_of_day_speed;
218 void Environment::setTimeOfDay(u32 time)
220 JMutexAutoLock(this->m_time_lock);
221 m_time_of_day = time;
222 m_time_of_day_f = (float)time / 24000.0;
225 u32 Environment::getTimeOfDay()
227 JMutexAutoLock(this->m_time_lock);
228 u32 retval = m_time_of_day;
232 float Environment::getTimeOfDayF()
234 JMutexAutoLock(this->m_time_lock);
235 float retval = m_time_of_day_f;
239 void Environment::stepTimeOfDay(float dtime)
241 // getTimeOfDaySpeed lock the value we need to prevent MT problems
242 float day_speed = getTimeOfDaySpeed();
244 m_time_counter += dtime;
245 f32 speed = day_speed * 24000./(24.*3600);
246 u32 units = (u32)(m_time_counter*speed);
250 if(m_time_of_day + units >= 24000)
252 m_time_of_day = (m_time_of_day + units) % 24000;
254 m_time_of_day_f = (float)m_time_of_day / 24000.0;
257 m_time_counter -= (f32)units / speed;
260 m_time_of_day_f += day_speed/24/3600*dtime;
261 if(m_time_of_day_f > 1.0)
262 m_time_of_day_f -= 1.0;
263 if(m_time_of_day_f < 0.0)
264 m_time_of_day_f += 1.0;
272 ABMWithState::ABMWithState(ActiveBlockModifier *abm_):
276 // Initialize timer to random value to spread processing
277 float itv = abm->getTriggerInterval();
278 itv = MYMAX(0.001, itv); // No less than 1ms
279 int minval = MYMAX(-0.51*itv, -60); // Clamp to
280 int maxval = MYMIN(0.51*itv, 60); // +-60 seconds
281 timer = myrand_range(minval, maxval);
288 void fillRadiusBlock(v3s16 p0, s16 r, std::set<v3s16> &list)
291 for(p.X=p0.X-r; p.X<=p0.X+r; p.X++)
292 for(p.Y=p0.Y-r; p.Y<=p0.Y+r; p.Y++)
293 for(p.Z=p0.Z-r; p.Z<=p0.Z+r; p.Z++)
300 void ActiveBlockList::update(std::vector<v3s16> &active_positions,
302 std::set<v3s16> &blocks_removed,
303 std::set<v3s16> &blocks_added)
308 std::set<v3s16> newlist = m_forceloaded_list;
309 for(std::vector<v3s16>::iterator i = active_positions.begin();
310 i != active_positions.end(); ++i)
312 fillRadiusBlock(*i, radius, newlist);
316 Find out which blocks on the old list are not on the new list
318 // Go through old list
319 for(std::set<v3s16>::iterator i = m_list.begin();
320 i != m_list.end(); ++i)
323 // If not on new list, it's been removed
324 if(newlist.find(p) == newlist.end())
325 blocks_removed.insert(p);
329 Find out which blocks on the new list are not on the old list
331 // Go through new list
332 for(std::set<v3s16>::iterator i = newlist.begin();
333 i != newlist.end(); ++i)
336 // If not on old list, it's been added
337 if(m_list.find(p) == m_list.end())
338 blocks_added.insert(p);
345 for(std::set<v3s16>::iterator i = newlist.begin();
346 i != newlist.end(); ++i)
357 ServerEnvironment::ServerEnvironment(ServerMap *map,
358 GameScripting *scriptIface, IGameDef *gamedef,
359 const std::string &path_world) :
361 m_script(scriptIface),
363 m_path_world(path_world),
364 m_send_recommended_timer(0),
365 m_active_block_interval_overload_skip(0),
367 m_game_time_fraction_counter(0),
368 m_recommended_send_interval(0.1),
369 m_max_lag_estimate(0.1)
373 ServerEnvironment::~ServerEnvironment()
375 // Clear active block list.
376 // This makes the next one delete all active objects.
377 m_active_blocks.clear();
379 // Convert all objects to static and delete the active objects
380 deactivateFarObjects(true);
385 // Delete ActiveBlockModifiers
386 for(std::vector<ABMWithState>::iterator
387 i = m_abms.begin(); i != m_abms.end(); ++i){
392 Map & ServerEnvironment::getMap()
397 ServerMap & ServerEnvironment::getServerMap()
402 bool ServerEnvironment::line_of_sight(v3f pos1, v3f pos2, float stepsize, v3s16 *p)
404 float distance = pos1.getDistanceFrom(pos2);
406 //calculate normalized direction vector
407 v3f normalized_vector = v3f((pos2.X - pos1.X)/distance,
408 (pos2.Y - pos1.Y)/distance,
409 (pos2.Z - pos1.Z)/distance);
411 //find out if there's a node on path between pos1 and pos2
412 for (float i = 1; i < distance; i += stepsize) {
413 v3s16 pos = floatToInt(v3f(normalized_vector.X * i,
414 normalized_vector.Y * i,
415 normalized_vector.Z * i) +pos1,BS);
417 MapNode n = getMap().getNodeNoEx(pos);
419 if(n.param0 != CONTENT_AIR) {
429 void ServerEnvironment::kickAllPlayers(const std::string &reason)
431 std::wstring wreason = utf8_to_wide(reason);
432 for (std::vector<Player*>::iterator it = m_players.begin();
433 it != m_players.end();
435 ((Server*)m_gamedef)->DenyAccess_Legacy((*it)->peer_id, wreason);
439 void ServerEnvironment::saveLoadedPlayers()
441 std::string players_path = m_path_world + DIR_DELIM "players";
442 fs::CreateDir(players_path);
444 for (std::vector<Player*>::iterator it = m_players.begin();
445 it != m_players.end();
447 RemotePlayer *player = static_cast<RemotePlayer*>(*it);
448 if (player->checkModified()) {
449 player->save(players_path);
454 void ServerEnvironment::savePlayer(const std::string &playername)
456 std::string players_path = m_path_world + DIR_DELIM "players";
457 fs::CreateDir(players_path);
459 RemotePlayer *player = static_cast<RemotePlayer*>(getPlayer(playername.c_str()));
461 player->save(players_path);
465 Player *ServerEnvironment::loadPlayer(const std::string &playername)
467 bool newplayer = false;
469 std::string players_path = m_path_world + DIR_DELIM "players" DIR_DELIM;
470 std::string path = players_path + playername;
472 RemotePlayer *player = static_cast<RemotePlayer *>(getPlayer(playername.c_str()));
474 player = new RemotePlayer(m_gamedef, "");
478 for (u32 i = 0; i < PLAYER_FILE_ALTERNATE_TRIES; i++) {
479 //// Open file and deserialize
480 std::ifstream is(path.c_str(), std::ios_base::binary);
483 player->deSerialize(is, path);
486 if (player->getName() == playername) {
491 path = players_path + playername + itos(i);
495 infostream << "Player file for player " << playername
496 << " not found" << std::endl;
504 player->setModified(false);
508 void ServerEnvironment::saveMeta()
510 std::string path = m_path_world + DIR_DELIM "env_meta.txt";
512 // Open file and serialize
513 std::ostringstream ss(std::ios_base::binary);
516 args.setU64("game_time", m_game_time);
517 args.setU64("time_of_day", getTimeOfDay());
521 if(!fs::safeWriteToFile(path, ss.str()))
523 infostream<<"ServerEnvironment::saveMeta(): Failed to write "
525 throw SerializationError("Couldn't save env meta");
529 void ServerEnvironment::loadMeta()
531 std::string path = m_path_world + DIR_DELIM "env_meta.txt";
533 // Open file and deserialize
534 std::ifstream is(path.c_str(), std::ios_base::binary);
536 infostream << "ServerEnvironment::loadMeta(): Failed to open "
537 << path << std::endl;
538 throw SerializationError("Couldn't load env meta");
543 if (!args.parseConfigLines(is, "EnvArgsEnd")) {
544 throw SerializationError("ServerEnvironment::loadMeta(): "
545 "EnvArgsEnd not found!");
549 m_game_time = args.getU64("game_time");
550 } catch (SettingNotFoundException &e) {
551 // Getting this is crucial, otherwise timestamps are useless
552 throw SerializationError("Couldn't load env meta game_time");
556 m_time_of_day = args.getU64("time_of_day");
557 } catch (SettingNotFoundException &e) {
558 // This is not as important
559 m_time_of_day = 9000;
565 ActiveBlockModifier *abm;
567 std::set<content_t> required_neighbors;
573 ServerEnvironment *m_env;
574 std::map<content_t, std::vector<ActiveABM> > m_aabms;
576 ABMHandler(std::vector<ABMWithState> &abms,
577 float dtime_s, ServerEnvironment *env,
583 INodeDefManager *ndef = env->getGameDef()->ndef();
584 for(std::vector<ABMWithState>::iterator
585 i = abms.begin(); i != abms.end(); ++i) {
586 ActiveBlockModifier *abm = i->abm;
587 float trigger_interval = abm->getTriggerInterval();
588 if(trigger_interval < 0.001)
589 trigger_interval = 0.001;
590 float actual_interval = dtime_s;
593 if(i->timer < trigger_interval)
595 i->timer -= trigger_interval;
596 actual_interval = trigger_interval;
598 float intervals = actual_interval / trigger_interval;
601 float chance = abm->getTriggerChance();
606 aabm.chance = chance / intervals;
610 std::set<std::string> required_neighbors_s
611 = abm->getRequiredNeighbors();
612 for(std::set<std::string>::iterator
613 i = required_neighbors_s.begin();
614 i != required_neighbors_s.end(); i++)
616 ndef->getIds(*i, aabm.required_neighbors);
619 std::set<std::string> contents_s = abm->getTriggerContents();
620 for(std::set<std::string>::iterator
621 i = contents_s.begin(); i != contents_s.end(); i++)
623 std::set<content_t> ids;
624 ndef->getIds(*i, ids);
625 for(std::set<content_t>::const_iterator k = ids.begin();
629 std::map<content_t, std::vector<ActiveABM> >::iterator j;
631 if(j == m_aabms.end()){
632 std::vector<ActiveABM> aabmlist;
633 m_aabms[c] = aabmlist;
636 j->second.push_back(aabm);
641 // Find out how many objects the given block and its neighbours contain.
642 // Returns the number of objects in the block, and also in 'wider' the
643 // number of objects in the block and all its neighbours. The latter
644 // may an estimate if any neighbours are unloaded.
645 u32 countObjects(MapBlock *block, ServerMap * map, u32 &wider)
648 u32 wider_unknown_count = 0;
649 for(s16 x=-1; x<=1; x++)
650 for(s16 y=-1; y<=1; y++)
651 for(s16 z=-1; z<=1; z++)
653 MapBlock *block2 = map->getBlockNoCreateNoEx(
654 block->getPos() + v3s16(x,y,z));
656 wider_unknown_count++;
659 wider += block2->m_static_objects.m_active.size()
660 + block2->m_static_objects.m_stored.size();
663 u32 active_object_count = block->m_static_objects.m_active.size();
664 u32 wider_known_count = 3*3*3 - wider_unknown_count;
665 wider += wider_unknown_count * wider / wider_known_count;
666 return active_object_count;
669 void apply(MapBlock *block)
674 ServerMap *map = &m_env->getServerMap();
676 u32 active_object_count_wider;
677 u32 active_object_count = this->countObjects(block, map, active_object_count_wider);
678 m_env->m_added_objects = 0;
681 for(p0.X=0; p0.X<MAP_BLOCKSIZE; p0.X++)
682 for(p0.Y=0; p0.Y<MAP_BLOCKSIZE; p0.Y++)
683 for(p0.Z=0; p0.Z<MAP_BLOCKSIZE; p0.Z++)
685 MapNode n = block->getNodeNoEx(p0);
686 content_t c = n.getContent();
687 v3s16 p = p0 + block->getPosRelative();
689 std::map<content_t, std::vector<ActiveABM> >::iterator j;
691 if(j == m_aabms.end())
694 for(std::vector<ActiveABM>::iterator
695 i = j->second.begin(); i != j->second.end(); i++) {
696 if(myrand() % i->chance != 0)
700 if(!i->required_neighbors.empty())
703 for(p1.X = p.X-1; p1.X <= p.X+1; p1.X++)
704 for(p1.Y = p.Y-1; p1.Y <= p.Y+1; p1.Y++)
705 for(p1.Z = p.Z-1; p1.Z <= p.Z+1; p1.Z++)
709 MapNode n = map->getNodeNoEx(p1);
710 content_t c = n.getContent();
711 std::set<content_t>::const_iterator k;
712 k = i->required_neighbors.find(c);
713 if(k != i->required_neighbors.end()){
717 // No required neighbor found
722 // Call all the trigger variations
723 i->abm->trigger(m_env, p, n);
724 i->abm->trigger(m_env, p, n,
725 active_object_count, active_object_count_wider);
727 // Count surrounding objects again if the abms added any
728 if(m_env->m_added_objects > 0) {
729 active_object_count = countObjects(block, map, active_object_count_wider);
730 m_env->m_added_objects = 0;
737 void ServerEnvironment::activateBlock(MapBlock *block, u32 additional_dtime)
739 // Reset usage timer immediately, otherwise a block that becomes active
740 // again at around the same time as it would normally be unloaded will
741 // get unloaded incorrectly. (I think this still leaves a small possibility
742 // of a race condition between this and server::AsyncRunStep, which only
743 // some kind of synchronisation will fix, but it at least reduces the window
744 // of opportunity for it to break from seconds to nanoseconds)
745 block->resetUsageTimer();
747 // Get time difference
749 u32 stamp = block->getTimestamp();
750 if(m_game_time > stamp && stamp != BLOCK_TIMESTAMP_UNDEFINED)
751 dtime_s = m_game_time - block->getTimestamp();
752 dtime_s += additional_dtime;
754 /*infostream<<"ServerEnvironment::activateBlock(): block timestamp: "
755 <<stamp<<", game time: "<<m_game_time<<std::endl;*/
757 // Set current time as timestamp
758 block->setTimestampNoChangedFlag(m_game_time);
760 /*infostream<<"ServerEnvironment::activateBlock(): block is "
761 <<dtime_s<<" seconds old."<<std::endl;*/
763 // Activate stored objects
764 activateObjects(block, dtime_s);
767 std::map<v3s16, NodeTimer> elapsed_timers =
768 block->m_node_timers.step((float)dtime_s);
769 if(!elapsed_timers.empty()){
771 for(std::map<v3s16, NodeTimer>::iterator
772 i = elapsed_timers.begin();
773 i != elapsed_timers.end(); i++){
774 n = block->getNodeNoEx(i->first);
775 v3s16 p = i->first + block->getPosRelative();
776 if(m_script->node_on_timer(p,n,i->second.elapsed))
777 block->setNodeTimer(i->first,NodeTimer(i->second.timeout,0));
781 /* Handle ActiveBlockModifiers */
782 ABMHandler abmhandler(m_abms, dtime_s, this, false);
783 abmhandler.apply(block);
786 void ServerEnvironment::addActiveBlockModifier(ActiveBlockModifier *abm)
788 m_abms.push_back(ABMWithState(abm));
791 bool ServerEnvironment::setNode(v3s16 p, const MapNode &n)
793 INodeDefManager *ndef = m_gamedef->ndef();
794 MapNode n_old = m_map->getNodeNoEx(p);
797 if (ndef->get(n_old).has_on_destruct)
798 m_script->node_on_destruct(p, n_old);
801 if (!m_map->addNodeWithEvent(p, n))
804 // Update active VoxelManipulator if a mapgen thread
805 m_map->updateVManip(p);
807 // Call post-destructor
808 if (ndef->get(n_old).has_after_destruct)
809 m_script->node_after_destruct(p, n_old);
812 if (ndef->get(n).has_on_construct)
813 m_script->node_on_construct(p, n);
818 bool ServerEnvironment::removeNode(v3s16 p)
820 INodeDefManager *ndef = m_gamedef->ndef();
821 MapNode n_old = m_map->getNodeNoEx(p);
824 if (ndef->get(n_old).has_on_destruct)
825 m_script->node_on_destruct(p, n_old);
828 // This is slightly optimized compared to addNodeWithEvent(air)
829 if (!m_map->removeNodeWithEvent(p))
832 // Update active VoxelManipulator if a mapgen thread
833 m_map->updateVManip(p);
835 // Call post-destructor
836 if (ndef->get(n_old).has_after_destruct)
837 m_script->node_after_destruct(p, n_old);
839 // Air doesn't require constructor
843 bool ServerEnvironment::swapNode(v3s16 p, const MapNode &n)
845 if (!m_map->addNodeWithEvent(p, n, false))
848 // Update active VoxelManipulator if a mapgen thread
849 m_map->updateVManip(p);
854 void ServerEnvironment::getObjectsInsideRadius(std::vector<u16> &objects, v3f pos, float radius)
856 for(std::map<u16, ServerActiveObject*>::iterator
857 i = m_active_objects.begin();
858 i != m_active_objects.end(); ++i)
860 ServerActiveObject* obj = i->second;
862 v3f objectpos = obj->getBasePosition();
863 if(objectpos.getDistanceFrom(pos) > radius)
865 objects.push_back(id);
869 void ServerEnvironment::clearAllObjects()
871 infostream<<"ServerEnvironment::clearAllObjects(): "
872 <<"Removing all active objects"<<std::endl;
873 std::vector<u16> objects_to_remove;
874 for(std::map<u16, ServerActiveObject*>::iterator
875 i = m_active_objects.begin();
876 i != m_active_objects.end(); ++i) {
877 ServerActiveObject* obj = i->second;
878 if(obj->getType() == ACTIVEOBJECT_TYPE_PLAYER)
881 // Delete static object if block is loaded
882 if(obj->m_static_exists){
883 MapBlock *block = m_map->getBlockNoCreateNoEx(obj->m_static_block);
885 block->m_static_objects.remove(id);
886 block->raiseModified(MOD_STATE_WRITE_NEEDED,
887 MOD_REASON_CLEAR_ALL_OBJECTS);
888 obj->m_static_exists = false;
891 // If known by some client, don't delete immediately
892 if(obj->m_known_by_count > 0){
893 obj->m_pending_deactivation = true;
894 obj->m_removed = true;
898 // Tell the object about removal
899 obj->removingFromEnvironment();
900 // Deregister in scripting api
901 m_script->removeObjectReference(obj);
903 // Delete active object
904 if(obj->environmentDeletes())
906 // Id to be removed from m_active_objects
907 objects_to_remove.push_back(id);
910 // Remove references from m_active_objects
911 for(std::vector<u16>::iterator i = objects_to_remove.begin();
912 i != objects_to_remove.end(); ++i) {
913 m_active_objects.erase(*i);
916 // Get list of loaded blocks
917 std::vector<v3s16> loaded_blocks;
918 infostream<<"ServerEnvironment::clearAllObjects(): "
919 <<"Listing all loaded blocks"<<std::endl;
920 m_map->listAllLoadedBlocks(loaded_blocks);
921 infostream<<"ServerEnvironment::clearAllObjects(): "
922 <<"Done listing all loaded blocks: "
923 <<loaded_blocks.size()<<std::endl;
925 // Get list of loadable blocks
926 std::vector<v3s16> loadable_blocks;
927 infostream<<"ServerEnvironment::clearAllObjects(): "
928 <<"Listing all loadable blocks"<<std::endl;
929 m_map->listAllLoadableBlocks(loadable_blocks);
930 infostream<<"ServerEnvironment::clearAllObjects(): "
931 <<"Done listing all loadable blocks: "
932 <<loadable_blocks.size()
933 <<", now clearing"<<std::endl;
935 // Grab a reference on each loaded block to avoid unloading it
936 for(std::vector<v3s16>::iterator i = loaded_blocks.begin();
937 i != loaded_blocks.end(); ++i) {
939 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
940 assert(block != NULL);
944 // Remove objects in all loadable blocks
945 u32 unload_interval = g_settings->getS32("max_clearobjects_extra_loaded_blocks");
946 unload_interval = MYMAX(unload_interval, 1);
947 u32 report_interval = loadable_blocks.size() / 10;
948 u32 num_blocks_checked = 0;
949 u32 num_blocks_cleared = 0;
950 u32 num_objs_cleared = 0;
951 for(std::vector<v3s16>::iterator i = loadable_blocks.begin();
952 i != loadable_blocks.end(); ++i) {
954 MapBlock *block = m_map->emergeBlock(p, false);
956 errorstream<<"ServerEnvironment::clearAllObjects(): "
957 <<"Failed to emerge block "<<PP(p)<<std::endl;
960 u32 num_stored = block->m_static_objects.m_stored.size();
961 u32 num_active = block->m_static_objects.m_active.size();
962 if(num_stored != 0 || num_active != 0){
963 block->m_static_objects.m_stored.clear();
964 block->m_static_objects.m_active.clear();
965 block->raiseModified(MOD_STATE_WRITE_NEEDED,
966 MOD_REASON_CLEAR_ALL_OBJECTS);
967 num_objs_cleared += num_stored + num_active;
968 num_blocks_cleared++;
970 num_blocks_checked++;
972 if(report_interval != 0 &&
973 num_blocks_checked % report_interval == 0){
974 float percent = 100.0 * (float)num_blocks_checked /
975 loadable_blocks.size();
976 infostream<<"ServerEnvironment::clearAllObjects(): "
977 <<"Cleared "<<num_objs_cleared<<" objects"
978 <<" in "<<num_blocks_cleared<<" blocks ("
979 <<percent<<"%)"<<std::endl;
981 if(num_blocks_checked % unload_interval == 0){
982 m_map->unloadUnreferencedBlocks();
985 m_map->unloadUnreferencedBlocks();
987 // Drop references that were added above
988 for(std::vector<v3s16>::iterator i = loaded_blocks.begin();
989 i != loaded_blocks.end(); ++i) {
991 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
996 infostream<<"ServerEnvironment::clearAllObjects(): "
997 <<"Finished: Cleared "<<num_objs_cleared<<" objects"
998 <<" in "<<num_blocks_cleared<<" blocks"<<std::endl;
1001 void ServerEnvironment::step(float dtime)
1003 DSTACK(__FUNCTION_NAME);
1005 //TimeTaker timer("ServerEnv step");
1007 /* Step time of day */
1008 stepTimeOfDay(dtime);
1011 // NOTE: This is kind of funny on a singleplayer game, but doesn't
1012 // really matter that much.
1013 m_recommended_send_interval = g_settings->getFloat("dedicated_server_step");
1019 m_game_time_fraction_counter += dtime;
1020 u32 inc_i = (u32)m_game_time_fraction_counter;
1021 m_game_time += inc_i;
1022 m_game_time_fraction_counter -= (float)inc_i;
1029 ScopeProfiler sp(g_profiler, "SEnv: handle players avg", SPT_AVG);
1030 for(std::vector<Player*>::iterator i = m_players.begin();
1031 i != m_players.end(); ++i)
1033 Player *player = *i;
1035 // Ignore disconnected players
1036 if(player->peer_id == 0)
1040 player->move(dtime, this, 100*BS);
1045 Manage active block list
1047 if(m_active_blocks_management_interval.step(dtime, 2.0))
1049 ScopeProfiler sp(g_profiler, "SEnv: manage act. block list avg /2s", SPT_AVG);
1051 Get player block positions
1053 std::vector<v3s16> players_blockpos;
1054 for(std::vector<Player*>::iterator
1055 i = m_players.begin();
1056 i != m_players.end(); ++i) {
1057 Player *player = *i;
1058 // Ignore disconnected players
1059 if(player->peer_id == 0)
1062 v3s16 blockpos = getNodeBlockPos(
1063 floatToInt(player->getPosition(), BS));
1064 players_blockpos.push_back(blockpos);
1068 Update list of active blocks, collecting changes
1070 const s16 active_block_range = g_settings->getS16("active_block_range");
1071 std::set<v3s16> blocks_removed;
1072 std::set<v3s16> blocks_added;
1073 m_active_blocks.update(players_blockpos, active_block_range,
1074 blocks_removed, blocks_added);
1077 Handle removed blocks
1080 // Convert active objects that are no more in active blocks to static
1081 deactivateFarObjects(false);
1083 for(std::set<v3s16>::iterator
1084 i = blocks_removed.begin();
1085 i != blocks_removed.end(); ++i)
1089 /* infostream<<"Server: Block " << PP(p)
1090 << " became inactive"<<std::endl; */
1092 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1096 // Set current time as timestamp (and let it set ChangedFlag)
1097 block->setTimestamp(m_game_time);
1104 for(std::set<v3s16>::iterator
1105 i = blocks_added.begin();
1106 i != blocks_added.end(); ++i)
1110 MapBlock *block = m_map->getBlockOrEmerge(p);
1112 m_active_blocks.m_list.erase(p);
1116 activateBlock(block);
1117 /* infostream<<"Server: Block " << PP(p)
1118 << " became active"<<std::endl; */
1123 Mess around in active blocks
1125 if(m_active_blocks_nodemetadata_interval.step(dtime, 1.0))
1127 ScopeProfiler sp(g_profiler, "SEnv: mess in act. blocks avg /1s", SPT_AVG);
1131 for(std::set<v3s16>::iterator
1132 i = m_active_blocks.m_list.begin();
1133 i != m_active_blocks.m_list.end(); ++i)
1137 /*infostream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
1138 <<") being handled"<<std::endl;*/
1140 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1144 // Reset block usage timer
1145 block->resetUsageTimer();
1147 // Set current time as timestamp
1148 block->setTimestampNoChangedFlag(m_game_time);
1149 // If time has changed much from the one on disk,
1150 // set block to be saved when it is unloaded
1151 if(block->getTimestamp() > block->getDiskTimestamp() + 60)
1152 block->raiseModified(MOD_STATE_WRITE_AT_UNLOAD,
1153 MOD_REASON_BLOCK_EXPIRED);
1156 std::map<v3s16, NodeTimer> elapsed_timers =
1157 block->m_node_timers.step((float)dtime);
1158 if(!elapsed_timers.empty()){
1160 for(std::map<v3s16, NodeTimer>::iterator
1161 i = elapsed_timers.begin();
1162 i != elapsed_timers.end(); i++){
1163 n = block->getNodeNoEx(i->first);
1164 p = i->first + block->getPosRelative();
1165 if(m_script->node_on_timer(p,n,i->second.elapsed))
1166 block->setNodeTimer(i->first,NodeTimer(i->second.timeout,0));
1172 const float abm_interval = 1.0;
1173 if(m_active_block_modifier_interval.step(dtime, abm_interval))
1175 if(m_active_block_interval_overload_skip > 0){
1176 ScopeProfiler sp(g_profiler, "SEnv: ABM overload skips");
1177 m_active_block_interval_overload_skip--;
1180 ScopeProfiler sp(g_profiler, "SEnv: modify in blocks avg /1s", SPT_AVG);
1181 TimeTaker timer("modify in active blocks");
1183 // Initialize handling of ActiveBlockModifiers
1184 ABMHandler abmhandler(m_abms, abm_interval, this, true);
1186 for(std::set<v3s16>::iterator
1187 i = m_active_blocks.m_list.begin();
1188 i != m_active_blocks.m_list.end(); ++i)
1192 /*infostream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
1193 <<") being handled"<<std::endl;*/
1195 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1199 // Set current time as timestamp
1200 block->setTimestampNoChangedFlag(m_game_time);
1202 /* Handle ActiveBlockModifiers */
1203 abmhandler.apply(block);
1206 u32 time_ms = timer.stop(true);
1207 u32 max_time_ms = 200;
1208 if(time_ms > max_time_ms){
1209 infostream<<"WARNING: active block modifiers took "
1210 <<time_ms<<"ms (longer than "
1211 <<max_time_ms<<"ms)"<<std::endl;
1212 m_active_block_interval_overload_skip = (time_ms / max_time_ms) + 1;
1217 Step script environment (run global on_step())
1219 m_script->environment_Step(dtime);
1225 ScopeProfiler sp(g_profiler, "SEnv: step act. objs avg", SPT_AVG);
1226 //TimeTaker timer("Step active objects");
1228 g_profiler->avg("SEnv: num of objects", m_active_objects.size());
1230 // This helps the objects to send data at the same time
1231 bool send_recommended = false;
1232 m_send_recommended_timer += dtime;
1233 if(m_send_recommended_timer > getSendRecommendedInterval())
1235 m_send_recommended_timer -= getSendRecommendedInterval();
1236 send_recommended = true;
1239 for(std::map<u16, ServerActiveObject*>::iterator
1240 i = m_active_objects.begin();
1241 i != m_active_objects.end(); ++i)
1243 ServerActiveObject* obj = i->second;
1244 // Don't step if is to be removed or stored statically
1245 if(obj->m_removed || obj->m_pending_deactivation)
1248 obj->step(dtime, send_recommended);
1249 // Read messages from object
1250 while(!obj->m_messages_out.empty())
1252 m_active_object_messages.push(
1253 obj->m_messages_out.front());
1254 obj->m_messages_out.pop();
1260 Manage active objects
1262 if(m_object_management_interval.step(dtime, 0.5))
1264 ScopeProfiler sp(g_profiler, "SEnv: remove removed objs avg /.5s", SPT_AVG);
1266 Remove objects that satisfy (m_removed && m_known_by_count==0)
1268 removeRemovedObjects();
1272 ServerActiveObject* ServerEnvironment::getActiveObject(u16 id)
1274 std::map<u16, ServerActiveObject*>::iterator n;
1275 n = m_active_objects.find(id);
1276 if(n == m_active_objects.end())
1281 bool isFreeServerActiveObjectId(u16 id,
1282 std::map<u16, ServerActiveObject*> &objects)
1287 return objects.find(id) == objects.end();
1290 u16 getFreeServerActiveObjectId(
1291 std::map<u16, ServerActiveObject*> &objects)
1293 //try to reuse id's as late as possible
1294 static u16 last_used_id = 0;
1295 u16 startid = last_used_id;
1299 if(isFreeServerActiveObjectId(last_used_id, objects))
1300 return last_used_id;
1302 if(last_used_id == startid)
1307 u16 ServerEnvironment::addActiveObject(ServerActiveObject *object)
1309 assert(object); // Pre-condition
1311 u16 id = addActiveObjectRaw(object, true, 0);
1316 bool ServerEnvironment::addActiveObjectAsStatic(ServerActiveObject *obj)
1320 v3f objectpos = obj->getBasePosition();
1322 // The block in which the object resides in
1323 v3s16 blockpos_o = getNodeBlockPos(floatToInt(objectpos, BS));
1326 Update the static data
1329 // Create new static object
1330 std::string staticdata = obj->getStaticData();
1331 StaticObject s_obj(obj->getType(), objectpos, staticdata);
1332 // Add to the block where the object is located in
1333 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1334 // Get or generate the block
1335 MapBlock *block = m_map->emergeBlock(blockpos);
1337 bool succeeded = false;
1341 block->m_static_objects.insert(0, s_obj);
1342 block->raiseModified(MOD_STATE_WRITE_AT_UNLOAD,
1343 "addActiveObjectAsStatic");
1347 infostream<<"ServerEnvironment::addActiveObjectAsStatic: "
1348 <<"Could not find or generate "
1349 <<"a block for storing static object"<<std::endl;
1353 if(obj->environmentDeletes())
1361 Finds out what new objects have been added to
1362 inside a radius around a position
1364 void ServerEnvironment::getAddedActiveObjects(v3s16 pos, s16 radius,
1366 std::set<u16> ¤t_objects,
1367 std::set<u16> &added_objects)
1369 v3f pos_f = intToFloat(pos, BS);
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 the object list,
1378 - discard m_removed objects,
1379 - discard objects that are too far away,
1380 - discard objects that are found in current_objects.
1381 - add remaining objects to added_objects
1383 for(std::map<u16, ServerActiveObject*>::iterator
1384 i = m_active_objects.begin();
1385 i != m_active_objects.end(); ++i)
1389 ServerActiveObject *object = i->second;
1392 // Discard if removed or deactivating
1393 if(object->m_removed || object->m_pending_deactivation)
1396 f32 distance_f = object->getBasePosition().getDistanceFrom(pos_f);
1397 if (object->getType() == ACTIVEOBJECT_TYPE_PLAYER) {
1398 // Discard if too far
1399 if (distance_f > player_radius_f && player_radius_f != 0)
1401 } else if (distance_f > radius_f)
1404 // Discard if already on current_objects
1405 std::set<u16>::iterator n;
1406 n = current_objects.find(id);
1407 if(n != current_objects.end())
1409 // Add to added_objects
1410 added_objects.insert(id);
1415 Finds out what objects have been removed from
1416 inside a radius around a position
1418 void ServerEnvironment::getRemovedActiveObjects(v3s16 pos, s16 radius,
1420 std::set<u16> ¤t_objects,
1421 std::set<u16> &removed_objects)
1423 v3f pos_f = intToFloat(pos, BS);
1424 f32 radius_f = radius * BS;
1425 f32 player_radius_f = player_radius * BS;
1427 if (player_radius_f < 0)
1428 player_radius_f = 0;
1431 Go through current_objects; object is removed if:
1432 - object is not found in m_active_objects (this is actually an
1433 error condition; objects should be set m_removed=true and removed
1434 only after all clients have been informed about removal), or
1435 - object has m_removed=true, or
1436 - object is too far away
1438 for(std::set<u16>::iterator
1439 i = current_objects.begin();
1440 i != current_objects.end(); ++i)
1443 ServerActiveObject *object = getActiveObject(id);
1446 infostream<<"ServerEnvironment::getRemovedActiveObjects():"
1447 <<" object in current_objects is NULL"<<std::endl;
1448 removed_objects.insert(id);
1452 if(object->m_removed || object->m_pending_deactivation)
1454 removed_objects.insert(id);
1458 f32 distance_f = object->getBasePosition().getDistanceFrom(pos_f);
1459 if (object->getType() == ACTIVEOBJECT_TYPE_PLAYER) {
1460 if (distance_f <= player_radius_f || player_radius_f == 0)
1462 } else if (distance_f <= radius_f)
1465 // Object is no longer visible
1466 removed_objects.insert(id);
1470 ActiveObjectMessage ServerEnvironment::getActiveObjectMessage()
1472 if(m_active_object_messages.empty())
1473 return ActiveObjectMessage(0);
1475 ActiveObjectMessage message = m_active_object_messages.front();
1476 m_active_object_messages.pop();
1481 ************ Private methods *************
1484 u16 ServerEnvironment::addActiveObjectRaw(ServerActiveObject *object,
1485 bool set_changed, u32 dtime_s)
1487 assert(object); // Pre-condition
1488 if(object->getId() == 0){
1489 u16 new_id = getFreeServerActiveObjectId(m_active_objects);
1492 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1493 <<"no free ids available"<<std::endl;
1494 if(object->environmentDeletes())
1498 object->setId(new_id);
1501 verbosestream<<"ServerEnvironment::addActiveObjectRaw(): "
1502 <<"supplied with id "<<object->getId()<<std::endl;
1504 if(isFreeServerActiveObjectId(object->getId(), m_active_objects) == false)
1506 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1507 <<"id is not free ("<<object->getId()<<")"<<std::endl;
1508 if(object->environmentDeletes())
1512 /*infostream<<"ServerEnvironment::addActiveObjectRaw(): "
1513 <<"added (id="<<object->getId()<<")"<<std::endl;*/
1515 m_active_objects[object->getId()] = object;
1517 verbosestream<<"ServerEnvironment::addActiveObjectRaw(): "
1518 <<"Added id="<<object->getId()<<"; there are now "
1519 <<m_active_objects.size()<<" active objects."
1522 // Register reference in scripting api (must be done before post-init)
1523 m_script->addObjectReference(object);
1524 // Post-initialize object
1525 object->addedToEnvironment(dtime_s);
1527 // Add static data to block
1528 if(object->isStaticAllowed())
1530 // Add static object to active static list of the block
1531 v3f objectpos = object->getBasePosition();
1532 std::string staticdata = object->getStaticData();
1533 StaticObject s_obj(object->getType(), objectpos, staticdata);
1534 // Add to the block where the object is located in
1535 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1536 MapBlock *block = m_map->emergeBlock(blockpos);
1538 block->m_static_objects.m_active[object->getId()] = s_obj;
1539 object->m_static_exists = true;
1540 object->m_static_block = blockpos;
1543 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1544 MOD_REASON_ADD_ACTIVE_OBJECT_RAW);
1546 v3s16 p = floatToInt(objectpos, BS);
1547 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1548 <<"could not emerge block for storing id="<<object->getId()
1549 <<" statically (pos="<<PP(p)<<")"<<std::endl;
1553 return object->getId();
1557 Remove objects that satisfy (m_removed && m_known_by_count==0)
1559 void ServerEnvironment::removeRemovedObjects()
1561 std::vector<u16> objects_to_remove;
1562 for(std::map<u16, ServerActiveObject*>::iterator
1563 i = m_active_objects.begin();
1564 i != m_active_objects.end(); ++i) {
1566 ServerActiveObject* obj = i->second;
1567 // This shouldn't happen but check it
1570 infostream<<"NULL object found in ServerEnvironment"
1571 <<" while finding removed objects. id="<<id<<std::endl;
1572 // Id to be removed from m_active_objects
1573 objects_to_remove.push_back(id);
1578 We will delete objects that are marked as removed or thatare
1579 waiting for deletion after deactivation
1581 if(obj->m_removed == false && obj->m_pending_deactivation == false)
1585 Delete static data from block if is marked as removed
1587 if(obj->m_static_exists && obj->m_removed)
1589 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
1591 block->m_static_objects.remove(id);
1592 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1593 MOD_REASON_REMOVE_OBJECTS_REMOVE);
1594 obj->m_static_exists = false;
1596 infostream<<"Failed to emerge block from which an object to "
1597 <<"be removed was loaded from. id="<<id<<std::endl;
1601 // If m_known_by_count > 0, don't actually remove. On some future
1602 // invocation this will be 0, which is when removal will continue.
1603 if(obj->m_known_by_count > 0)
1607 Move static data from active to stored if not marked as removed
1609 if(obj->m_static_exists && !obj->m_removed){
1610 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
1612 std::map<u16, StaticObject>::iterator i =
1613 block->m_static_objects.m_active.find(id);
1614 if(i != block->m_static_objects.m_active.end()){
1615 block->m_static_objects.m_stored.push_back(i->second);
1616 block->m_static_objects.m_active.erase(id);
1617 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1618 MOD_REASON_REMOVE_OBJECTS_DEACTIVATE);
1621 infostream<<"Failed to emerge block from which an object to "
1622 <<"be deactivated was loaded from. id="<<id<<std::endl;
1626 // Tell the object about removal
1627 obj->removingFromEnvironment();
1628 // Deregister in scripting api
1629 m_script->removeObjectReference(obj);
1632 if(obj->environmentDeletes())
1635 // Id to be removed from m_active_objects
1636 objects_to_remove.push_back(id);
1638 // Remove references from m_active_objects
1639 for(std::vector<u16>::iterator i = objects_to_remove.begin();
1640 i != objects_to_remove.end(); ++i) {
1641 m_active_objects.erase(*i);
1645 static void print_hexdump(std::ostream &o, const std::string &data)
1647 const int linelength = 16;
1648 for(int l=0; ; l++){
1649 int i0 = linelength * l;
1650 bool at_end = false;
1651 int thislinelength = linelength;
1652 if(i0 + thislinelength > (int)data.size()){
1653 thislinelength = data.size() - i0;
1656 for(int di=0; di<linelength; di++){
1659 if(di<thislinelength)
1660 snprintf(buf, 4, "%.2x ", data[i]);
1662 snprintf(buf, 4, " ");
1666 for(int di=0; di<thislinelength; di++){
1680 Convert stored objects from blocks near the players to active.
1682 void ServerEnvironment::activateObjects(MapBlock *block, u32 dtime_s)
1687 // Ignore if no stored objects (to not set changed flag)
1688 if(block->m_static_objects.m_stored.empty())
1691 verbosestream<<"ServerEnvironment::activateObjects(): "
1692 <<"activating objects of block "<<PP(block->getPos())
1693 <<" ("<<block->m_static_objects.m_stored.size()
1694 <<" objects)"<<std::endl;
1695 bool large_amount = (block->m_static_objects.m_stored.size() > g_settings->getU16("max_objects_per_block"));
1697 errorstream<<"suspiciously large amount of objects detected: "
1698 <<block->m_static_objects.m_stored.size()<<" in "
1699 <<PP(block->getPos())
1700 <<"; removing all of them."<<std::endl;
1701 // Clear stored list
1702 block->m_static_objects.m_stored.clear();
1703 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1704 MOD_REASON_TOO_MANY_OBJECTS);
1708 // Activate stored objects
1709 std::vector<StaticObject> new_stored;
1710 for (std::vector<StaticObject>::iterator
1711 i = block->m_static_objects.m_stored.begin();
1712 i != block->m_static_objects.m_stored.end(); ++i) {
1713 StaticObject &s_obj = *i;
1715 // Create an active object from the data
1716 ServerActiveObject *obj = ServerActiveObject::create
1717 ((ActiveObjectType) s_obj.type, this, 0, s_obj.pos, s_obj.data);
1718 // If couldn't create object, store static data back.
1720 errorstream<<"ServerEnvironment::activateObjects(): "
1721 <<"failed to create active object from static object "
1722 <<"in block "<<PP(s_obj.pos/BS)
1723 <<" type="<<(int)s_obj.type<<" data:"<<std::endl;
1724 print_hexdump(verbosestream, s_obj.data);
1726 new_stored.push_back(s_obj);
1729 verbosestream<<"ServerEnvironment::activateObjects(): "
1730 <<"activated static object pos="<<PP(s_obj.pos/BS)
1731 <<" type="<<(int)s_obj.type<<std::endl;
1732 // This will also add the object to the active static list
1733 addActiveObjectRaw(obj, false, dtime_s);
1735 // Clear stored list
1736 block->m_static_objects.m_stored.clear();
1737 // Add leftover failed stuff to stored list
1738 for(std::vector<StaticObject>::iterator
1739 i = new_stored.begin();
1740 i != new_stored.end(); ++i) {
1741 StaticObject &s_obj = *i;
1742 block->m_static_objects.m_stored.push_back(s_obj);
1745 // Turn the active counterparts of activated objects not pending for
1747 for(std::map<u16, StaticObject>::iterator
1748 i = block->m_static_objects.m_active.begin();
1749 i != block->m_static_objects.m_active.end(); ++i)
1752 ServerActiveObject *object = getActiveObject(id);
1754 object->m_pending_deactivation = false;
1758 Note: Block hasn't really been modified here.
1759 The objects have just been activated and moved from the stored
1760 static list to the active static list.
1761 As such, the block is essentially the same.
1762 Thus, do not call block->raiseModified(MOD_STATE_WRITE_NEEDED).
1763 Otherwise there would be a huge amount of unnecessary I/O.
1768 Convert objects that are not standing inside active blocks to static.
1770 If m_known_by_count != 0, active object is not deleted, but static
1771 data is still updated.
1773 If force_delete is set, active object is deleted nevertheless. It
1774 shall only be set so in the destructor of the environment.
1776 If block wasn't generated (not in memory or on disk),
1778 void ServerEnvironment::deactivateFarObjects(bool force_delete)
1780 std::vector<u16> objects_to_remove;
1781 for(std::map<u16, ServerActiveObject*>::iterator
1782 i = m_active_objects.begin();
1783 i != m_active_objects.end(); ++i) {
1784 ServerActiveObject* obj = i->second;
1787 // Do not deactivate if static data creation not allowed
1788 if(!force_delete && !obj->isStaticAllowed())
1791 // If pending deactivation, let removeRemovedObjects() do it
1792 if(!force_delete && obj->m_pending_deactivation)
1796 v3f objectpos = obj->getBasePosition();
1798 // The block in which the object resides in
1799 v3s16 blockpos_o = getNodeBlockPos(floatToInt(objectpos, BS));
1801 // If object's static data is stored in a deactivated block and object
1802 // is actually located in an active block, re-save to the block in
1803 // which the object is actually located in.
1805 obj->m_static_exists &&
1806 !m_active_blocks.contains(obj->m_static_block) &&
1807 m_active_blocks.contains(blockpos_o))
1809 v3s16 old_static_block = obj->m_static_block;
1811 // Save to block where object is located
1812 MapBlock *block = m_map->emergeBlock(blockpos_o, false);
1814 errorstream<<"ServerEnvironment::deactivateFarObjects(): "
1815 <<"Could not save object id="<<id
1816 <<" to it's current block "<<PP(blockpos_o)
1820 std::string staticdata_new = obj->getStaticData();
1821 StaticObject s_obj(obj->getType(), objectpos, staticdata_new);
1822 block->m_static_objects.insert(id, s_obj);
1823 obj->m_static_block = blockpos_o;
1824 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1825 MOD_REASON_STATIC_DATA_ADDED);
1827 // Delete from block where object was located
1828 block = m_map->emergeBlock(old_static_block, false);
1830 errorstream<<"ServerEnvironment::deactivateFarObjects(): "
1831 <<"Could not delete object id="<<id
1832 <<" from it's previous block "<<PP(old_static_block)
1836 block->m_static_objects.remove(id);
1837 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1838 MOD_REASON_STATIC_DATA_REMOVED);
1842 // If block is active, don't remove
1843 if(!force_delete && m_active_blocks.contains(blockpos_o))
1846 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
1847 <<"deactivating object id="<<id<<" on inactive block "
1848 <<PP(blockpos_o)<<std::endl;
1850 // If known by some client, don't immediately delete.
1851 bool pending_delete = (obj->m_known_by_count > 0 && !force_delete);
1854 Update the static data
1857 if(obj->isStaticAllowed())
1859 // Create new static object
1860 std::string staticdata_new = obj->getStaticData();
1861 StaticObject s_obj(obj->getType(), objectpos, staticdata_new);
1863 bool stays_in_same_block = false;
1864 bool data_changed = true;
1866 if(obj->m_static_exists){
1867 if(obj->m_static_block == blockpos_o)
1868 stays_in_same_block = true;
1870 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
1872 std::map<u16, StaticObject>::iterator n =
1873 block->m_static_objects.m_active.find(id);
1874 if(n != block->m_static_objects.m_active.end()){
1875 StaticObject static_old = n->second;
1877 float save_movem = obj->getMinimumSavedMovement();
1879 if(static_old.data == staticdata_new &&
1880 (static_old.pos - objectpos).getLength() < save_movem)
1881 data_changed = false;
1883 errorstream<<"ServerEnvironment::deactivateFarObjects(): "
1884 <<"id="<<id<<" m_static_exists=true but "
1885 <<"static data doesn't actually exist in "
1886 <<PP(obj->m_static_block)<<std::endl;
1890 bool shall_be_written = (!stays_in_same_block || data_changed);
1892 // Delete old static object
1893 if(obj->m_static_exists)
1895 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
1898 block->m_static_objects.remove(id);
1899 obj->m_static_exists = false;
1900 // Only mark block as modified if data changed considerably
1901 if(shall_be_written)
1902 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1903 MOD_REASON_STATIC_DATA_CHANGED);
1907 // Add to the block where the object is located in
1908 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1909 // Get or generate the block
1910 MapBlock *block = NULL;
1912 block = m_map->emergeBlock(blockpos);
1913 } catch(InvalidPositionException &e){
1914 // Handled via NULL pointer
1915 // NOTE: emergeBlock's failure is usually determined by it
1916 // actually returning NULL
1921 if(block->m_static_objects.m_stored.size() >= g_settings->getU16("max_objects_per_block")){
1922 errorstream<<"ServerEnv: Trying to store id="<<obj->getId()
1923 <<" statically but block "<<PP(blockpos)
1924 <<" already contains "
1925 <<block->m_static_objects.m_stored.size()
1927 <<" Forcing delete."<<std::endl;
1928 force_delete = true;
1930 // If static counterpart already exists in target block,
1932 // This shouldn't happen because the object is removed from
1933 // the previous block before this according to
1934 // obj->m_static_block, but happens rarely for some unknown
1935 // reason. Unsuccessful attempts have been made to find
1937 if(id && block->m_static_objects.m_active.find(id) != block->m_static_objects.m_active.end()){
1938 infostream<<"ServerEnv: WARNING: Performing hack #83274"
1940 block->m_static_objects.remove(id);
1942 // Store static data
1943 u16 store_id = pending_delete ? id : 0;
1944 block->m_static_objects.insert(store_id, s_obj);
1946 // Only mark block as modified if data changed considerably
1947 if(shall_be_written)
1948 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1949 MOD_REASON_STATIC_DATA_CHANGED);
1951 obj->m_static_exists = true;
1952 obj->m_static_block = block->getPos();
1957 v3s16 p = floatToInt(objectpos, BS);
1958 errorstream<<"ServerEnv: Could not find or generate "
1959 <<"a block for storing id="<<obj->getId()
1960 <<" statically (pos="<<PP(p)<<")"<<std::endl;
1967 If known by some client, set pending deactivation.
1968 Otherwise delete it immediately.
1971 if(pending_delete && !force_delete)
1973 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
1974 <<"object id="<<id<<" is known by clients"
1975 <<"; not deleting yet"<<std::endl;
1977 obj->m_pending_deactivation = true;
1981 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
1982 <<"object id="<<id<<" is not known by clients"
1983 <<"; deleting"<<std::endl;
1985 // Tell the object about removal
1986 obj->removingFromEnvironment();
1987 // Deregister in scripting api
1988 m_script->removeObjectReference(obj);
1990 // Delete active object
1991 if(obj->environmentDeletes())
1993 // Id to be removed from m_active_objects
1994 objects_to_remove.push_back(id);
1997 // Remove references from m_active_objects
1998 for(std::vector<u16>::iterator i = objects_to_remove.begin();
1999 i != objects_to_remove.end(); ++i) {
2000 m_active_objects.erase(*i);
2007 #include "clientsimpleobject.h"
2013 ClientEnvironment::ClientEnvironment(ClientMap *map, scene::ISceneManager *smgr,
2014 ITextureSource *texturesource, IGameDef *gamedef,
2015 IrrlichtDevice *irr):
2018 m_texturesource(texturesource),
2023 memset(attachement_parent_ids, zero, sizeof(attachement_parent_ids));
2026 ClientEnvironment::~ClientEnvironment()
2028 // delete active objects
2029 for(std::map<u16, ClientActiveObject*>::iterator
2030 i = m_active_objects.begin();
2031 i != m_active_objects.end(); ++i)
2036 for(std::vector<ClientSimpleObject*>::iterator
2037 i = m_simple_objects.begin(); i != m_simple_objects.end(); ++i) {
2045 Map & ClientEnvironment::getMap()
2050 ClientMap & ClientEnvironment::getClientMap()
2055 void ClientEnvironment::addPlayer(Player *player)
2057 DSTACK(__FUNCTION_NAME);
2059 It is a failure if player is local and there already is a local
2062 FATAL_ERROR_IF(player->isLocal() == true && getLocalPlayer() != NULL,
2063 "Player is local but there is already a local player");
2065 Environment::addPlayer(player);
2068 LocalPlayer * ClientEnvironment::getLocalPlayer()
2070 for(std::vector<Player*>::iterator i = m_players.begin();
2071 i != m_players.end(); ++i) {
2072 Player *player = *i;
2073 if(player->isLocal())
2074 return (LocalPlayer*)player;
2079 void ClientEnvironment::step(float dtime)
2081 DSTACK(__FUNCTION_NAME);
2083 /* Step time of day */
2084 stepTimeOfDay(dtime);
2086 // Get some settings
2087 bool fly_allowed = m_gamedef->checkLocalPrivilege("fly");
2088 bool free_move = fly_allowed && g_settings->getBool("free_move");
2091 LocalPlayer *lplayer = getLocalPlayer();
2093 // collision info queue
2094 std::vector<CollisionInfo> player_collisions;
2097 Get the speed the player is going
2099 bool is_climbing = lplayer->is_climbing;
2101 f32 player_speed = lplayer->getSpeed().getLength();
2104 Maximum position increment
2106 //f32 position_max_increment = 0.05*BS;
2107 f32 position_max_increment = 0.1*BS;
2109 // Maximum time increment (for collision detection etc)
2110 // time = distance / speed
2111 f32 dtime_max_increment = 1;
2112 if(player_speed > 0.001)
2113 dtime_max_increment = position_max_increment / player_speed;
2115 // Maximum time increment is 10ms or lower
2116 if(dtime_max_increment > 0.01)
2117 dtime_max_increment = 0.01;
2119 // Don't allow overly huge dtime
2123 f32 dtime_downcount = dtime;
2126 Stuff that has a maximum time increment
2135 if(dtime_downcount > dtime_max_increment)
2137 dtime_part = dtime_max_increment;
2138 dtime_downcount -= dtime_part;
2142 dtime_part = dtime_downcount;
2144 Setting this to 0 (no -=dtime_part) disables an infinite loop
2145 when dtime_part is so small that dtime_downcount -= dtime_part
2148 dtime_downcount = 0;
2157 if(free_move == false && is_climbing == false)
2160 v3f speed = lplayer->getSpeed();
2161 if(lplayer->in_liquid == false)
2162 speed.Y -= lplayer->movement_gravity * lplayer->physics_override_gravity * dtime_part * 2;
2164 // Liquid floating / sinking
2165 if(lplayer->in_liquid && !lplayer->swimming_vertical)
2166 speed.Y -= lplayer->movement_liquid_sink * dtime_part * 2;
2168 // Liquid resistance
2169 if(lplayer->in_liquid_stable || lplayer->in_liquid)
2171 // How much the node's viscosity blocks movement, ranges between 0 and 1
2172 // Should match the scale at which viscosity increase affects other liquid attributes
2173 const f32 viscosity_factor = 0.3;
2175 v3f d_wanted = -speed / lplayer->movement_liquid_fluidity;
2176 f32 dl = d_wanted.getLength();
2177 if(dl > lplayer->movement_liquid_fluidity_smooth)
2178 dl = lplayer->movement_liquid_fluidity_smooth;
2179 dl *= (lplayer->liquid_viscosity * viscosity_factor) + (1 - viscosity_factor);
2181 v3f d = d_wanted.normalize() * dl;
2185 if(speed.X > lplayer->movement_liquid_fluidity + lplayer->movement_liquid_fluidity_smooth) speed.X -= lplayer->movement_liquid_fluidity_smooth;
2186 if(speed.X < -lplayer->movement_liquid_fluidity - lplayer->movement_liquid_fluidity_smooth) speed.X += lplayer->movement_liquid_fluidity_smooth;
2187 if(speed.Y > lplayer->movement_liquid_fluidity + lplayer->movement_liquid_fluidity_smooth) speed.Y -= lplayer->movement_liquid_fluidity_smooth;
2188 if(speed.Y < -lplayer->movement_liquid_fluidity - lplayer->movement_liquid_fluidity_smooth) speed.Y += lplayer->movement_liquid_fluidity_smooth;
2189 if(speed.Z > lplayer->movement_liquid_fluidity + lplayer->movement_liquid_fluidity_smooth) speed.Z -= lplayer->movement_liquid_fluidity_smooth;
2190 if(speed.Z < -lplayer->movement_liquid_fluidity - lplayer->movement_liquid_fluidity_smooth) speed.Z += lplayer->movement_liquid_fluidity_smooth;
2194 lplayer->setSpeed(speed);
2199 This also does collision detection.
2201 lplayer->move(dtime_part, this, position_max_increment,
2202 &player_collisions);
2205 while(dtime_downcount > 0.001);
2207 //std::cout<<"Looped "<<loopcount<<" times."<<std::endl;
2209 for(std::vector<CollisionInfo>::iterator i = player_collisions.begin();
2210 i != player_collisions.end(); ++i) {
2211 CollisionInfo &info = *i;
2212 v3f speed_diff = info.new_speed - info.old_speed;;
2213 // Handle only fall damage
2214 // (because otherwise walking against something in fast_move kills you)
2215 if(speed_diff.Y < 0 || info.old_speed.Y >= 0)
2217 // Get rid of other components
2220 f32 pre_factor = 1; // 1 hp per node/s
2221 f32 tolerance = BS*14; // 5 without damage
2222 f32 post_factor = 1; // 1 hp per node/s
2223 if(info.type == COLLISION_NODE)
2225 const ContentFeatures &f = m_gamedef->ndef()->
2226 get(m_map->getNodeNoEx(info.node_p));
2227 // Determine fall damage multiplier
2228 int addp = itemgroup_get(f.groups, "fall_damage_add_percent");
2229 pre_factor = 1.0 + (float)addp/100.0;
2231 float speed = pre_factor * speed_diff.getLength();
2232 if(speed > tolerance)
2234 f32 damage_f = (speed - tolerance)/BS * post_factor;
2235 u16 damage = (u16)(damage_f+0.5);
2237 damageLocalPlayer(damage, true);
2238 MtEvent *e = new SimpleTriggerEvent("PlayerFallingDamage");
2239 m_gamedef->event()->put(e);
2245 A quick draft of lava damage
2247 if(m_lava_hurt_interval.step(dtime, 1.0))
2249 v3f pf = lplayer->getPosition();
2251 // Feet, middle and head
2252 v3s16 p1 = floatToInt(pf + v3f(0, BS*0.1, 0), BS);
2253 MapNode n1 = m_map->getNodeNoEx(p1);
2254 v3s16 p2 = floatToInt(pf + v3f(0, BS*0.8, 0), BS);
2255 MapNode n2 = m_map->getNodeNoEx(p2);
2256 v3s16 p3 = floatToInt(pf + v3f(0, BS*1.6, 0), BS);
2257 MapNode n3 = m_map->getNodeNoEx(p3);
2259 u32 damage_per_second = 0;
2260 damage_per_second = MYMAX(damage_per_second,
2261 m_gamedef->ndef()->get(n1).damage_per_second);
2262 damage_per_second = MYMAX(damage_per_second,
2263 m_gamedef->ndef()->get(n2).damage_per_second);
2264 damage_per_second = MYMAX(damage_per_second,
2265 m_gamedef->ndef()->get(n3).damage_per_second);
2267 if(damage_per_second != 0)
2269 damageLocalPlayer(damage_per_second, true);
2276 if(m_drowning_interval.step(dtime, 2.0))
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);
2284 u8 drowning_damage = c.drowning;
2285 if(drowning_damage > 0 && lplayer->hp > 0){
2286 u16 breath = lplayer->getBreath();
2293 lplayer->setBreath(breath);
2294 updateLocalPlayerBreath(breath);
2297 if(lplayer->getBreath() == 0 && drowning_damage > 0){
2298 damageLocalPlayer(drowning_damage, true);
2301 if(m_breathing_interval.step(dtime, 0.5))
2303 v3f pf = lplayer->getPosition();
2306 v3s16 p = floatToInt(pf + v3f(0, BS*1.6, 0), BS);
2307 MapNode n = m_map->getNodeNoEx(p);
2308 ContentFeatures c = m_gamedef->ndef()->get(n);
2310 lplayer->setBreath(11);
2312 else if(c.drowning == 0){
2313 u16 breath = lplayer->getBreath();
2316 lplayer->setBreath(breath);
2317 updateLocalPlayerBreath(breath);
2323 Stuff that can be done in an arbitarily large dtime
2325 for(std::vector<Player*>::iterator i = m_players.begin();
2326 i != m_players.end(); ++i) {
2327 Player *player = *i;
2330 Handle non-local players
2332 if(player->isLocal() == false) {
2334 player->move(dtime, this, 100*BS);
2339 // Update lighting on local player (used for wield item)
2340 u32 day_night_ratio = getDayNightRatio();
2344 // On InvalidPositionException, use this as default
2345 // (day: LIGHT_SUN, night: 0)
2346 MapNode node_at_lplayer(CONTENT_AIR, 0x0f, 0);
2348 v3s16 p = lplayer->getLightPosition();
2349 node_at_lplayer = m_map->getNodeNoEx(p);
2351 u16 light = getInteriorLight(node_at_lplayer, 0, m_gamedef->ndef());
2352 u8 day = light & 0xff;
2353 u8 night = (light >> 8) & 0xff;
2354 finalColorBlend(lplayer->light_color, day, night, day_night_ratio);
2358 Step active objects and update lighting of them
2361 g_profiler->avg("CEnv: num of objects", m_active_objects.size());
2362 bool update_lighting = m_active_object_light_update_interval.step(dtime, 0.21);
2363 for(std::map<u16, ClientActiveObject*>::iterator
2364 i = m_active_objects.begin();
2365 i != m_active_objects.end(); ++i)
2367 ClientActiveObject* obj = i->second;
2369 obj->step(dtime, this);
2378 v3s16 p = obj->getLightPosition();
2379 MapNode n = m_map->getNodeNoEx(p, &pos_ok);
2381 light = n.getLightBlend(day_night_ratio, m_gamedef->ndef());
2383 light = blend_light(day_night_ratio, LIGHT_SUN, 0);
2385 obj->updateLight(light);
2390 Step and handle simple objects
2392 g_profiler->avg("CEnv: num of simple objects", m_simple_objects.size());
2393 for(std::vector<ClientSimpleObject*>::iterator
2394 i = m_simple_objects.begin(); i != m_simple_objects.end();) {
2395 std::vector<ClientSimpleObject*>::iterator cur = i;
2396 ClientSimpleObject *simple = *cur;
2398 simple->step(dtime);
2399 if(simple->m_to_be_removed) {
2401 i = m_simple_objects.erase(cur);
2409 void ClientEnvironment::addSimpleObject(ClientSimpleObject *simple)
2411 m_simple_objects.push_back(simple);
2414 GenericCAO* ClientEnvironment::getGenericCAO(u16 id)
2416 ClientActiveObject *obj = getActiveObject(id);
2417 if (obj && obj->getType() == ACTIVEOBJECT_TYPE_GENERIC)
2418 return (GenericCAO*) obj;
2423 ClientActiveObject* ClientEnvironment::getActiveObject(u16 id)
2425 std::map<u16, ClientActiveObject*>::iterator n;
2426 n = m_active_objects.find(id);
2427 if(n == m_active_objects.end())
2432 bool isFreeClientActiveObjectId(u16 id,
2433 std::map<u16, ClientActiveObject*> &objects)
2438 return objects.find(id) == objects.end();
2441 u16 getFreeClientActiveObjectId(
2442 std::map<u16, ClientActiveObject*> &objects)
2444 //try to reuse id's as late as possible
2445 static u16 last_used_id = 0;
2446 u16 startid = last_used_id;
2450 if(isFreeClientActiveObjectId(last_used_id, objects))
2451 return last_used_id;
2453 if(last_used_id == startid)
2458 u16 ClientEnvironment::addActiveObject(ClientActiveObject *object)
2460 assert(object); // Pre-condition
2461 if(object->getId() == 0)
2463 u16 new_id = getFreeClientActiveObjectId(m_active_objects);
2466 infostream<<"ClientEnvironment::addActiveObject(): "
2467 <<"no free ids available"<<std::endl;
2471 object->setId(new_id);
2473 if(isFreeClientActiveObjectId(object->getId(), m_active_objects) == false)
2475 infostream<<"ClientEnvironment::addActiveObject(): "
2476 <<"id is not free ("<<object->getId()<<")"<<std::endl;
2480 infostream<<"ClientEnvironment::addActiveObject(): "
2481 <<"added (id="<<object->getId()<<")"<<std::endl;
2482 m_active_objects[object->getId()] = object;
2483 object->addToScene(m_smgr, m_texturesource, m_irr);
2484 { // Update lighting immediately
2489 v3s16 p = object->getLightPosition();
2490 MapNode n = m_map->getNodeNoEx(p, &pos_ok);
2492 light = n.getLightBlend(getDayNightRatio(), m_gamedef->ndef());
2494 light = blend_light(getDayNightRatio(), LIGHT_SUN, 0);
2496 object->updateLight(light);
2498 return object->getId();
2501 void ClientEnvironment::addActiveObject(u16 id, u8 type,
2502 const std::string &init_data)
2504 ClientActiveObject* obj =
2505 ClientActiveObject::create((ActiveObjectType) type, m_gamedef, this);
2508 infostream<<"ClientEnvironment::addActiveObject(): "
2509 <<"id="<<id<<" type="<<type<<": Couldn't create object"
2518 obj->initialize(init_data);
2520 catch(SerializationError &e)
2522 errorstream<<"ClientEnvironment::addActiveObject():"
2523 <<" id="<<id<<" type="<<type
2524 <<": SerializationError in initialize(): "
2526 <<": init_data="<<serializeJsonString(init_data)
2530 addActiveObject(obj);
2533 void ClientEnvironment::removeActiveObject(u16 id)
2535 verbosestream<<"ClientEnvironment::removeActiveObject(): "
2536 <<"id="<<id<<std::endl;
2537 ClientActiveObject* obj = getActiveObject(id);
2540 infostream<<"ClientEnvironment::removeActiveObject(): "
2541 <<"id="<<id<<" not found"<<std::endl;
2544 obj->removeFromScene(true);
2546 m_active_objects.erase(id);
2549 void ClientEnvironment::processActiveObjectMessage(u16 id, const std::string &data)
2551 ClientActiveObject *obj = getActiveObject(id);
2553 infostream << "ClientEnvironment::processActiveObjectMessage():"
2554 << " got message for id=" << id << ", which doesn't exist."
2560 obj->processMessage(data);
2561 } catch (SerializationError &e) {
2562 errorstream<<"ClientEnvironment::processActiveObjectMessage():"
2563 << " id=" << id << " type=" << obj->getType()
2564 << " SerializationError in processMessage(): " << e.what()
2570 Callbacks for activeobjects
2573 void ClientEnvironment::damageLocalPlayer(u8 damage, bool handle_hp)
2575 LocalPlayer *lplayer = getLocalPlayer();
2579 if (lplayer->hp > damage)
2580 lplayer->hp -= damage;
2585 ClientEnvEvent event;
2586 event.type = CEE_PLAYER_DAMAGE;
2587 event.player_damage.amount = damage;
2588 event.player_damage.send_to_server = handle_hp;
2589 m_client_event_queue.push_back(event);
2592 void ClientEnvironment::updateLocalPlayerBreath(u16 breath)
2594 ClientEnvEvent event;
2595 event.type = CEE_PLAYER_BREATH;
2596 event.player_breath.amount = breath;
2597 m_client_event_queue.push_back(event);
2601 Client likes to call these
2604 void ClientEnvironment::getActiveObjects(v3f origin, f32 max_d,
2605 std::vector<DistanceSortedActiveObject> &dest)
2607 for(std::map<u16, ClientActiveObject*>::iterator
2608 i = m_active_objects.begin();
2609 i != m_active_objects.end(); ++i)
2611 ClientActiveObject* obj = i->second;
2613 f32 d = (obj->getPosition() - origin).getLength();
2618 DistanceSortedActiveObject dso(obj, d);
2620 dest.push_back(dso);
2624 ClientEnvEvent ClientEnvironment::getClientEvent()
2626 ClientEnvEvent event;
2627 if(m_client_event_queue.empty())
2628 event.type = CEE_NONE;
2630 event = m_client_event_queue.front();
2631 m_client_event_queue.pop_front();
2636 #endif // #ifndef SERVER