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);
228 f32 speed = m_time_of_day_speed * 24000. / (24. * 3600);
229 m_time_conversion_skew += dtime;
230 u32 units = (u32)(m_time_conversion_skew * speed);
234 if (m_time_of_day + units >= 24000)
236 m_time_of_day = (m_time_of_day + units) % 24000;
238 m_time_of_day_f = (float)m_time_of_day / 24000.0;
241 m_time_conversion_skew -= (f32)units / speed;
244 m_time_of_day_f += speed * dtime;
245 if (m_time_of_day_f > 1.0)
246 m_time_of_day_f -= 1.0;
247 if (m_time_of_day_f < 0.0)
248 m_time_of_day_f += 1.0;
256 ABMWithState::ABMWithState(ActiveBlockModifier *abm_):
260 // Initialize timer to random value to spread processing
261 float itv = abm->getTriggerInterval();
262 itv = MYMAX(0.001, itv); // No less than 1ms
263 int minval = MYMAX(-0.51*itv, -60); // Clamp to
264 int maxval = MYMIN(0.51*itv, 60); // +-60 seconds
265 timer = myrand_range(minval, maxval);
272 void fillRadiusBlock(v3s16 p0, s16 r, std::set<v3s16> &list)
275 for(p.X=p0.X-r; p.X<=p0.X+r; p.X++)
276 for(p.Y=p0.Y-r; p.Y<=p0.Y+r; p.Y++)
277 for(p.Z=p0.Z-r; p.Z<=p0.Z+r; p.Z++)
284 void ActiveBlockList::update(std::vector<v3s16> &active_positions,
286 std::set<v3s16> &blocks_removed,
287 std::set<v3s16> &blocks_added)
292 std::set<v3s16> newlist = m_forceloaded_list;
293 for(std::vector<v3s16>::iterator i = active_positions.begin();
294 i != active_positions.end(); ++i)
296 fillRadiusBlock(*i, radius, newlist);
300 Find out which blocks on the old list are not on the new list
302 // Go through old list
303 for(std::set<v3s16>::iterator i = m_list.begin();
304 i != m_list.end(); ++i)
307 // If not on new list, it's been removed
308 if(newlist.find(p) == newlist.end())
309 blocks_removed.insert(p);
313 Find out which blocks on the new list are not on the old list
315 // Go through new list
316 for(std::set<v3s16>::iterator i = newlist.begin();
317 i != newlist.end(); ++i)
320 // If not on old list, it's been added
321 if(m_list.find(p) == m_list.end())
322 blocks_added.insert(p);
329 for(std::set<v3s16>::iterator i = newlist.begin();
330 i != newlist.end(); ++i)
341 ServerEnvironment::ServerEnvironment(ServerMap *map,
342 GameScripting *scriptIface, IGameDef *gamedef,
343 const std::string &path_world) :
345 m_script(scriptIface),
347 m_path_world(path_world),
348 m_send_recommended_timer(0),
349 m_active_block_interval_overload_skip(0),
351 m_game_time_fraction_counter(0),
352 m_recommended_send_interval(0.1),
353 m_max_lag_estimate(0.1)
357 ServerEnvironment::~ServerEnvironment()
359 // Clear active block list.
360 // This makes the next one delete all active objects.
361 m_active_blocks.clear();
363 // Convert all objects to static and delete the active objects
364 deactivateFarObjects(true);
369 // Delete ActiveBlockModifiers
370 for(std::vector<ABMWithState>::iterator
371 i = m_abms.begin(); i != m_abms.end(); ++i){
376 Map & ServerEnvironment::getMap()
381 ServerMap & ServerEnvironment::getServerMap()
386 bool ServerEnvironment::line_of_sight(v3f pos1, v3f pos2, float stepsize, v3s16 *p)
388 float distance = pos1.getDistanceFrom(pos2);
390 //calculate normalized direction vector
391 v3f normalized_vector = v3f((pos2.X - pos1.X)/distance,
392 (pos2.Y - pos1.Y)/distance,
393 (pos2.Z - pos1.Z)/distance);
395 //find out if there's a node on path between pos1 and pos2
396 for (float i = 1; i < distance; i += stepsize) {
397 v3s16 pos = floatToInt(v3f(normalized_vector.X * i,
398 normalized_vector.Y * i,
399 normalized_vector.Z * i) +pos1,BS);
401 MapNode n = getMap().getNodeNoEx(pos);
403 if(n.param0 != CONTENT_AIR) {
413 void ServerEnvironment::kickAllPlayers(AccessDeniedCode reason,
414 const std::string &str_reason, bool reconnect)
416 for (std::vector<Player*>::iterator it = m_players.begin();
417 it != m_players.end();
419 ((Server*)m_gamedef)->DenyAccessVerCompliant((*it)->peer_id,
420 (*it)->protocol_version, (AccessDeniedCode)reason,
421 str_reason, reconnect);
425 void ServerEnvironment::saveLoadedPlayers()
427 std::string players_path = m_path_world + DIR_DELIM "players";
428 fs::CreateDir(players_path);
430 for (std::vector<Player*>::iterator it = m_players.begin();
431 it != m_players.end();
433 RemotePlayer *player = static_cast<RemotePlayer*>(*it);
434 if (player->checkModified()) {
435 player->save(players_path);
440 void ServerEnvironment::savePlayer(RemotePlayer *player)
442 std::string players_path = m_path_world + DIR_DELIM "players";
443 fs::CreateDir(players_path);
445 player->save(players_path);
448 Player *ServerEnvironment::loadPlayer(const std::string &playername)
450 bool newplayer = false;
452 std::string players_path = m_path_world + DIR_DELIM "players" DIR_DELIM;
453 std::string path = players_path + playername;
455 RemotePlayer *player = static_cast<RemotePlayer *>(getPlayer(playername.c_str()));
457 player = new RemotePlayer(m_gamedef, "");
461 for (u32 i = 0; i < PLAYER_FILE_ALTERNATE_TRIES; i++) {
462 //// Open file and deserialize
463 std::ifstream is(path.c_str(), std::ios_base::binary);
466 player->deSerialize(is, path);
469 if (player->getName() == playername) {
474 path = players_path + playername + itos(i);
478 infostream << "Player file for player " << playername
479 << " not found" << std::endl;
487 player->setModified(false);
491 void ServerEnvironment::saveMeta()
493 std::string path = m_path_world + DIR_DELIM "env_meta.txt";
495 // Open file and serialize
496 std::ostringstream ss(std::ios_base::binary);
499 args.setU64("game_time", m_game_time);
500 args.setU64("time_of_day", getTimeOfDay());
504 if(!fs::safeWriteToFile(path, ss.str()))
506 infostream<<"ServerEnvironment::saveMeta(): Failed to write "
508 throw SerializationError("Couldn't save env meta");
512 void ServerEnvironment::loadMeta()
514 std::string path = m_path_world + DIR_DELIM "env_meta.txt";
516 // Open file and deserialize
517 std::ifstream is(path.c_str(), std::ios_base::binary);
519 infostream << "ServerEnvironment::loadMeta(): Failed to open "
520 << path << std::endl;
521 throw SerializationError("Couldn't load env meta");
526 if (!args.parseConfigLines(is, "EnvArgsEnd")) {
527 throw SerializationError("ServerEnvironment::loadMeta(): "
528 "EnvArgsEnd not found!");
532 m_game_time = args.getU64("game_time");
533 } catch (SettingNotFoundException &e) {
534 // Getting this is crucial, otherwise timestamps are useless
535 throw SerializationError("Couldn't load env meta game_time");
539 setTimeOfDay(args.getU64("time_of_day"));
540 } catch (SettingNotFoundException &e) {
541 // This is not as important
548 ActiveBlockModifier *abm;
550 std::set<content_t> required_neighbors;
556 ServerEnvironment *m_env;
557 std::map<content_t, std::vector<ActiveABM> > m_aabms;
559 ABMHandler(std::vector<ABMWithState> &abms,
560 float dtime_s, ServerEnvironment *env,
566 INodeDefManager *ndef = env->getGameDef()->ndef();
567 for(std::vector<ABMWithState>::iterator
568 i = abms.begin(); i != abms.end(); ++i) {
569 ActiveBlockModifier *abm = i->abm;
570 float trigger_interval = abm->getTriggerInterval();
571 if(trigger_interval < 0.001)
572 trigger_interval = 0.001;
573 float actual_interval = dtime_s;
576 if(i->timer < trigger_interval)
578 i->timer -= trigger_interval;
579 actual_interval = trigger_interval;
581 float chance = abm->getTriggerChance();
586 if(abm->getSimpleCatchUp()) {
587 float intervals = actual_interval / trigger_interval;
590 aabm.chance = chance / intervals;
594 aabm.chance = chance;
597 std::set<std::string> required_neighbors_s
598 = abm->getRequiredNeighbors();
599 for(std::set<std::string>::iterator
600 i = required_neighbors_s.begin();
601 i != required_neighbors_s.end(); ++i)
603 ndef->getIds(*i, aabm.required_neighbors);
606 std::set<std::string> contents_s = abm->getTriggerContents();
607 for(std::set<std::string>::iterator
608 i = contents_s.begin(); i != contents_s.end(); ++i)
610 std::set<content_t> ids;
611 ndef->getIds(*i, ids);
612 for(std::set<content_t>::const_iterator k = ids.begin();
616 std::map<content_t, std::vector<ActiveABM> >::iterator j;
618 if(j == m_aabms.end()){
619 std::vector<ActiveABM> aabmlist;
620 m_aabms[c] = aabmlist;
623 j->second.push_back(aabm);
628 // Find out how many objects the given block and its neighbours contain.
629 // Returns the number of objects in the block, and also in 'wider' the
630 // number of objects in the block and all its neighbours. The latter
631 // may an estimate if any neighbours are unloaded.
632 u32 countObjects(MapBlock *block, ServerMap * map, u32 &wider)
635 u32 wider_unknown_count = 0;
636 for(s16 x=-1; x<=1; x++)
637 for(s16 y=-1; y<=1; y++)
638 for(s16 z=-1; z<=1; z++)
640 MapBlock *block2 = map->getBlockNoCreateNoEx(
641 block->getPos() + v3s16(x,y,z));
643 wider_unknown_count++;
646 wider += block2->m_static_objects.m_active.size()
647 + block2->m_static_objects.m_stored.size();
650 u32 active_object_count = block->m_static_objects.m_active.size();
651 u32 wider_known_count = 3*3*3 - wider_unknown_count;
652 wider += wider_unknown_count * wider / wider_known_count;
653 return active_object_count;
656 void apply(MapBlock *block)
661 ServerMap *map = &m_env->getServerMap();
663 u32 active_object_count_wider;
664 u32 active_object_count = this->countObjects(block, map, active_object_count_wider);
665 m_env->m_added_objects = 0;
668 for(p0.X=0; p0.X<MAP_BLOCKSIZE; p0.X++)
669 for(p0.Y=0; p0.Y<MAP_BLOCKSIZE; p0.Y++)
670 for(p0.Z=0; p0.Z<MAP_BLOCKSIZE; p0.Z++)
672 MapNode n = block->getNodeNoEx(p0);
673 content_t c = n.getContent();
674 v3s16 p = p0 + block->getPosRelative();
676 std::map<content_t, std::vector<ActiveABM> >::iterator j;
678 if(j == m_aabms.end())
681 for(std::vector<ActiveABM>::iterator
682 i = j->second.begin(); i != j->second.end(); ++i) {
683 if(myrand() % i->chance != 0)
687 if(!i->required_neighbors.empty())
690 for(p1.X = p.X-1; p1.X <= p.X+1; p1.X++)
691 for(p1.Y = p.Y-1; p1.Y <= p.Y+1; p1.Y++)
692 for(p1.Z = p.Z-1; p1.Z <= p.Z+1; p1.Z++)
696 MapNode n = map->getNodeNoEx(p1);
697 content_t c = n.getContent();
698 std::set<content_t>::const_iterator k;
699 k = i->required_neighbors.find(c);
700 if(k != i->required_neighbors.end()){
704 // No required neighbor found
709 // Call all the trigger variations
710 i->abm->trigger(m_env, p, n);
711 i->abm->trigger(m_env, p, n,
712 active_object_count, active_object_count_wider);
714 // Count surrounding objects again if the abms added any
715 if(m_env->m_added_objects > 0) {
716 active_object_count = countObjects(block, map, active_object_count_wider);
717 m_env->m_added_objects = 0;
724 void ServerEnvironment::activateBlock(MapBlock *block, u32 additional_dtime)
726 // Reset usage timer immediately, otherwise a block that becomes active
727 // again at around the same time as it would normally be unloaded will
728 // get unloaded incorrectly. (I think this still leaves a small possibility
729 // of a race condition between this and server::AsyncRunStep, which only
730 // some kind of synchronisation will fix, but it at least reduces the window
731 // of opportunity for it to break from seconds to nanoseconds)
732 block->resetUsageTimer();
734 // Get time difference
736 u32 stamp = block->getTimestamp();
737 if(m_game_time > stamp && stamp != BLOCK_TIMESTAMP_UNDEFINED)
738 dtime_s = m_game_time - block->getTimestamp();
739 dtime_s += additional_dtime;
741 /*infostream<<"ServerEnvironment::activateBlock(): block timestamp: "
742 <<stamp<<", game time: "<<m_game_time<<std::endl;*/
744 // Set current time as timestamp
745 block->setTimestampNoChangedFlag(m_game_time);
747 /*infostream<<"ServerEnvironment::activateBlock(): block is "
748 <<dtime_s<<" seconds old."<<std::endl;*/
750 // Activate stored objects
751 activateObjects(block, dtime_s);
754 std::map<v3s16, NodeTimer> elapsed_timers =
755 block->m_node_timers.step((float)dtime_s);
756 if(!elapsed_timers.empty()){
758 for(std::map<v3s16, NodeTimer>::iterator
759 i = elapsed_timers.begin();
760 i != elapsed_timers.end(); ++i){
761 n = block->getNodeNoEx(i->first);
762 v3s16 p = i->first + block->getPosRelative();
763 if(m_script->node_on_timer(p,n,i->second.elapsed))
764 block->setNodeTimer(i->first,NodeTimer(i->second.timeout,0));
768 /* Handle ActiveBlockModifiers */
769 ABMHandler abmhandler(m_abms, dtime_s, this, false);
770 abmhandler.apply(block);
773 void ServerEnvironment::addActiveBlockModifier(ActiveBlockModifier *abm)
775 m_abms.push_back(ABMWithState(abm));
778 bool ServerEnvironment::setNode(v3s16 p, const MapNode &n)
780 INodeDefManager *ndef = m_gamedef->ndef();
781 MapNode n_old = m_map->getNodeNoEx(p);
784 if (ndef->get(n_old).has_on_destruct)
785 m_script->node_on_destruct(p, n_old);
788 if (!m_map->addNodeWithEvent(p, n))
791 // Update active VoxelManipulator if a mapgen thread
792 m_map->updateVManip(p);
794 // Call post-destructor
795 if (ndef->get(n_old).has_after_destruct)
796 m_script->node_after_destruct(p, n_old);
799 if (ndef->get(n).has_on_construct)
800 m_script->node_on_construct(p, n);
805 bool ServerEnvironment::removeNode(v3s16 p)
807 INodeDefManager *ndef = m_gamedef->ndef();
808 MapNode n_old = m_map->getNodeNoEx(p);
811 if (ndef->get(n_old).has_on_destruct)
812 m_script->node_on_destruct(p, n_old);
815 // This is slightly optimized compared to addNodeWithEvent(air)
816 if (!m_map->removeNodeWithEvent(p))
819 // Update active VoxelManipulator if a mapgen thread
820 m_map->updateVManip(p);
822 // Call post-destructor
823 if (ndef->get(n_old).has_after_destruct)
824 m_script->node_after_destruct(p, n_old);
826 // Air doesn't require constructor
830 bool ServerEnvironment::swapNode(v3s16 p, const MapNode &n)
832 if (!m_map->addNodeWithEvent(p, n, false))
835 // Update active VoxelManipulator if a mapgen thread
836 m_map->updateVManip(p);
841 void ServerEnvironment::getObjectsInsideRadius(std::vector<u16> &objects, v3f pos, float radius)
843 for(std::map<u16, ServerActiveObject*>::iterator
844 i = m_active_objects.begin();
845 i != m_active_objects.end(); ++i)
847 ServerActiveObject* obj = i->second;
849 v3f objectpos = obj->getBasePosition();
850 if(objectpos.getDistanceFrom(pos) > radius)
852 objects.push_back(id);
856 void ServerEnvironment::clearAllObjects()
858 infostream<<"ServerEnvironment::clearAllObjects(): "
859 <<"Removing all active objects"<<std::endl;
860 std::vector<u16> objects_to_remove;
861 for(std::map<u16, ServerActiveObject*>::iterator
862 i = m_active_objects.begin();
863 i != m_active_objects.end(); ++i) {
864 ServerActiveObject* obj = i->second;
865 if(obj->getType() == ACTIVEOBJECT_TYPE_PLAYER)
868 // Delete static object if block is loaded
869 if(obj->m_static_exists){
870 MapBlock *block = m_map->getBlockNoCreateNoEx(obj->m_static_block);
872 block->m_static_objects.remove(id);
873 block->raiseModified(MOD_STATE_WRITE_NEEDED,
874 MOD_REASON_CLEAR_ALL_OBJECTS);
875 obj->m_static_exists = false;
878 // If known by some client, don't delete immediately
879 if(obj->m_known_by_count > 0){
880 obj->m_pending_deactivation = true;
881 obj->m_removed = true;
885 // Tell the object about removal
886 obj->removingFromEnvironment();
887 // Deregister in scripting api
888 m_script->removeObjectReference(obj);
890 // Delete active object
891 if(obj->environmentDeletes())
893 // Id to be removed from m_active_objects
894 objects_to_remove.push_back(id);
897 // Remove references from m_active_objects
898 for(std::vector<u16>::iterator i = objects_to_remove.begin();
899 i != objects_to_remove.end(); ++i) {
900 m_active_objects.erase(*i);
903 // Get list of loaded blocks
904 std::vector<v3s16> loaded_blocks;
905 infostream<<"ServerEnvironment::clearAllObjects(): "
906 <<"Listing all loaded blocks"<<std::endl;
907 m_map->listAllLoadedBlocks(loaded_blocks);
908 infostream<<"ServerEnvironment::clearAllObjects(): "
909 <<"Done listing all loaded blocks: "
910 <<loaded_blocks.size()<<std::endl;
912 // Get list of loadable blocks
913 std::vector<v3s16> loadable_blocks;
914 infostream<<"ServerEnvironment::clearAllObjects(): "
915 <<"Listing all loadable blocks"<<std::endl;
916 m_map->listAllLoadableBlocks(loadable_blocks);
917 infostream<<"ServerEnvironment::clearAllObjects(): "
918 <<"Done listing all loadable blocks: "
919 <<loadable_blocks.size()
920 <<", now clearing"<<std::endl;
922 // Grab a reference on each loaded block to avoid unloading it
923 for(std::vector<v3s16>::iterator i = loaded_blocks.begin();
924 i != loaded_blocks.end(); ++i) {
926 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
927 assert(block != NULL);
931 // Remove objects in all loadable blocks
932 u32 unload_interval = g_settings->getS32("max_clearobjects_extra_loaded_blocks");
933 unload_interval = MYMAX(unload_interval, 1);
934 u32 report_interval = loadable_blocks.size() / 10;
935 u32 num_blocks_checked = 0;
936 u32 num_blocks_cleared = 0;
937 u32 num_objs_cleared = 0;
938 for(std::vector<v3s16>::iterator i = loadable_blocks.begin();
939 i != loadable_blocks.end(); ++i) {
941 MapBlock *block = m_map->emergeBlock(p, false);
943 errorstream<<"ServerEnvironment::clearAllObjects(): "
944 <<"Failed to emerge block "<<PP(p)<<std::endl;
947 u32 num_stored = block->m_static_objects.m_stored.size();
948 u32 num_active = block->m_static_objects.m_active.size();
949 if(num_stored != 0 || num_active != 0){
950 block->m_static_objects.m_stored.clear();
951 block->m_static_objects.m_active.clear();
952 block->raiseModified(MOD_STATE_WRITE_NEEDED,
953 MOD_REASON_CLEAR_ALL_OBJECTS);
954 num_objs_cleared += num_stored + num_active;
955 num_blocks_cleared++;
957 num_blocks_checked++;
959 if(report_interval != 0 &&
960 num_blocks_checked % report_interval == 0){
961 float percent = 100.0 * (float)num_blocks_checked /
962 loadable_blocks.size();
963 infostream<<"ServerEnvironment::clearAllObjects(): "
964 <<"Cleared "<<num_objs_cleared<<" objects"
965 <<" in "<<num_blocks_cleared<<" blocks ("
966 <<percent<<"%)"<<std::endl;
968 if(num_blocks_checked % unload_interval == 0){
969 m_map->unloadUnreferencedBlocks();
972 m_map->unloadUnreferencedBlocks();
974 // Drop references that were added above
975 for(std::vector<v3s16>::iterator i = loaded_blocks.begin();
976 i != loaded_blocks.end(); ++i) {
978 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
983 infostream<<"ServerEnvironment::clearAllObjects(): "
984 <<"Finished: Cleared "<<num_objs_cleared<<" objects"
985 <<" in "<<num_blocks_cleared<<" blocks"<<std::endl;
988 void ServerEnvironment::step(float dtime)
990 DSTACK(FUNCTION_NAME);
992 //TimeTaker timer("ServerEnv step");
994 /* Step time of day */
995 stepTimeOfDay(dtime);
998 // NOTE: This is kind of funny on a singleplayer game, but doesn't
999 // really matter that much.
1000 m_recommended_send_interval = g_settings->getFloat("dedicated_server_step");
1006 m_game_time_fraction_counter += dtime;
1007 u32 inc_i = (u32)m_game_time_fraction_counter;
1008 m_game_time += inc_i;
1009 m_game_time_fraction_counter -= (float)inc_i;
1016 ScopeProfiler sp(g_profiler, "SEnv: handle players avg", SPT_AVG);
1017 for(std::vector<Player*>::iterator i = m_players.begin();
1018 i != m_players.end(); ++i)
1020 Player *player = *i;
1022 // Ignore disconnected players
1023 if(player->peer_id == 0)
1027 player->move(dtime, this, 100*BS);
1032 Manage active block list
1034 if(m_active_blocks_management_interval.step(dtime, 2.0))
1036 ScopeProfiler sp(g_profiler, "SEnv: manage act. block list avg /2s", SPT_AVG);
1038 Get player block positions
1040 std::vector<v3s16> players_blockpos;
1041 for(std::vector<Player*>::iterator
1042 i = m_players.begin();
1043 i != m_players.end(); ++i) {
1044 Player *player = *i;
1045 // Ignore disconnected players
1046 if(player->peer_id == 0)
1049 v3s16 blockpos = getNodeBlockPos(
1050 floatToInt(player->getPosition(), BS));
1051 players_blockpos.push_back(blockpos);
1055 Update list of active blocks, collecting changes
1057 const s16 active_block_range = g_settings->getS16("active_block_range");
1058 std::set<v3s16> blocks_removed;
1059 std::set<v3s16> blocks_added;
1060 m_active_blocks.update(players_blockpos, active_block_range,
1061 blocks_removed, blocks_added);
1064 Handle removed blocks
1067 // Convert active objects that are no more in active blocks to static
1068 deactivateFarObjects(false);
1070 for(std::set<v3s16>::iterator
1071 i = blocks_removed.begin();
1072 i != blocks_removed.end(); ++i)
1076 /* infostream<<"Server: Block " << PP(p)
1077 << " became inactive"<<std::endl; */
1079 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1083 // Set current time as timestamp (and let it set ChangedFlag)
1084 block->setTimestamp(m_game_time);
1091 for(std::set<v3s16>::iterator
1092 i = blocks_added.begin();
1093 i != blocks_added.end(); ++i)
1097 MapBlock *block = m_map->getBlockOrEmerge(p);
1099 m_active_blocks.m_list.erase(p);
1103 activateBlock(block);
1104 /* infostream<<"Server: Block " << PP(p)
1105 << " became active"<<std::endl; */
1110 Mess around in active blocks
1112 if(m_active_blocks_nodemetadata_interval.step(dtime, 1.0))
1114 ScopeProfiler sp(g_profiler, "SEnv: mess in act. blocks avg /1s", SPT_AVG);
1118 for(std::set<v3s16>::iterator
1119 i = m_active_blocks.m_list.begin();
1120 i != m_active_blocks.m_list.end(); ++i)
1124 /*infostream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
1125 <<") being handled"<<std::endl;*/
1127 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1131 // Reset block usage timer
1132 block->resetUsageTimer();
1134 // Set current time as timestamp
1135 block->setTimestampNoChangedFlag(m_game_time);
1136 // If time has changed much from the one on disk,
1137 // set block to be saved when it is unloaded
1138 if(block->getTimestamp() > block->getDiskTimestamp() + 60)
1139 block->raiseModified(MOD_STATE_WRITE_AT_UNLOAD,
1140 MOD_REASON_BLOCK_EXPIRED);
1143 std::map<v3s16, NodeTimer> elapsed_timers =
1144 block->m_node_timers.step((float)dtime);
1145 if(!elapsed_timers.empty()){
1147 for(std::map<v3s16, NodeTimer>::iterator
1148 i = elapsed_timers.begin();
1149 i != elapsed_timers.end(); ++i){
1150 n = block->getNodeNoEx(i->first);
1151 p = i->first + block->getPosRelative();
1152 if(m_script->node_on_timer(p,n,i->second.elapsed))
1153 block->setNodeTimer(i->first,NodeTimer(i->second.timeout,0));
1159 const float abm_interval = 1.0;
1160 if(m_active_block_modifier_interval.step(dtime, abm_interval))
1162 if(m_active_block_interval_overload_skip > 0){
1163 ScopeProfiler sp(g_profiler, "SEnv: ABM overload skips");
1164 m_active_block_interval_overload_skip--;
1167 ScopeProfiler sp(g_profiler, "SEnv: modify in blocks avg /1s", SPT_AVG);
1168 TimeTaker timer("modify in active blocks");
1170 // Initialize handling of ActiveBlockModifiers
1171 ABMHandler abmhandler(m_abms, abm_interval, this, true);
1173 for(std::set<v3s16>::iterator
1174 i = m_active_blocks.m_list.begin();
1175 i != m_active_blocks.m_list.end(); ++i)
1179 /*infostream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
1180 <<") being handled"<<std::endl;*/
1182 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1186 // Set current time as timestamp
1187 block->setTimestampNoChangedFlag(m_game_time);
1189 /* Handle ActiveBlockModifiers */
1190 abmhandler.apply(block);
1193 u32 time_ms = timer.stop(true);
1194 u32 max_time_ms = 200;
1195 if(time_ms > max_time_ms){
1196 warningstream<<"active block modifiers took "
1197 <<time_ms<<"ms (longer than "
1198 <<max_time_ms<<"ms)"<<std::endl;
1199 m_active_block_interval_overload_skip = (time_ms / max_time_ms) + 1;
1204 Step script environment (run global on_step())
1206 m_script->environment_Step(dtime);
1212 ScopeProfiler sp(g_profiler, "SEnv: step act. objs avg", SPT_AVG);
1213 //TimeTaker timer("Step active objects");
1215 g_profiler->avg("SEnv: num of objects", m_active_objects.size());
1217 // This helps the objects to send data at the same time
1218 bool send_recommended = false;
1219 m_send_recommended_timer += dtime;
1220 if(m_send_recommended_timer > getSendRecommendedInterval())
1222 m_send_recommended_timer -= getSendRecommendedInterval();
1223 send_recommended = true;
1226 for(std::map<u16, ServerActiveObject*>::iterator
1227 i = m_active_objects.begin();
1228 i != m_active_objects.end(); ++i)
1230 ServerActiveObject* obj = i->second;
1231 // Don't step if is to be removed or stored statically
1232 if(obj->m_removed || obj->m_pending_deactivation)
1235 obj->step(dtime, send_recommended);
1236 // Read messages from object
1237 while(!obj->m_messages_out.empty())
1239 m_active_object_messages.push(
1240 obj->m_messages_out.front());
1241 obj->m_messages_out.pop();
1247 Manage active objects
1249 if(m_object_management_interval.step(dtime, 0.5))
1251 ScopeProfiler sp(g_profiler, "SEnv: remove removed objs avg /.5s", SPT_AVG);
1253 Remove objects that satisfy (m_removed && m_known_by_count==0)
1255 removeRemovedObjects();
1259 ServerActiveObject* ServerEnvironment::getActiveObject(u16 id)
1261 std::map<u16, ServerActiveObject*>::iterator n;
1262 n = m_active_objects.find(id);
1263 if(n == m_active_objects.end())
1268 bool isFreeServerActiveObjectId(u16 id,
1269 std::map<u16, ServerActiveObject*> &objects)
1274 return objects.find(id) == objects.end();
1277 u16 getFreeServerActiveObjectId(
1278 std::map<u16, ServerActiveObject*> &objects)
1280 //try to reuse id's as late as possible
1281 static u16 last_used_id = 0;
1282 u16 startid = last_used_id;
1286 if(isFreeServerActiveObjectId(last_used_id, objects))
1287 return last_used_id;
1289 if(last_used_id == startid)
1294 u16 ServerEnvironment::addActiveObject(ServerActiveObject *object)
1296 assert(object); // Pre-condition
1298 u16 id = addActiveObjectRaw(object, true, 0);
1303 Finds out what new objects have been added to
1304 inside a radius around a position
1306 void ServerEnvironment::getAddedActiveObjects(Player *player, s16 radius,
1308 std::set<u16> ¤t_objects,
1309 std::queue<u16> &added_objects)
1311 f32 radius_f = radius * BS;
1312 f32 player_radius_f = player_radius * BS;
1314 if (player_radius_f < 0)
1315 player_radius_f = 0;
1318 Go through the object list,
1319 - discard m_removed objects,
1320 - discard objects that are too far away,
1321 - discard objects that are found in current_objects.
1322 - add remaining objects to added_objects
1324 for(std::map<u16, ServerActiveObject*>::iterator
1325 i = m_active_objects.begin();
1326 i != m_active_objects.end(); ++i) {
1330 ServerActiveObject *object = i->second;
1334 // Discard if removed or deactivating
1335 if(object->m_removed || object->m_pending_deactivation)
1338 f32 distance_f = object->getBasePosition().getDistanceFrom(player->getPosition());
1339 if (object->getType() == ACTIVEOBJECT_TYPE_PLAYER) {
1340 // Discard if too far
1341 if (distance_f > player_radius_f && player_radius_f != 0)
1343 } else if (distance_f > radius_f)
1346 // Discard if already on current_objects
1347 std::set<u16>::iterator n;
1348 n = current_objects.find(id);
1349 if(n != current_objects.end())
1351 // Add to added_objects
1352 added_objects.push(id);
1357 Finds out what objects have been removed from
1358 inside a radius around a position
1360 void ServerEnvironment::getRemovedActiveObjects(Player *player, s16 radius,
1362 std::set<u16> ¤t_objects,
1363 std::queue<u16> &removed_objects)
1365 f32 radius_f = radius * BS;
1366 f32 player_radius_f = player_radius * BS;
1368 if (player_radius_f < 0)
1369 player_radius_f = 0;
1372 Go through current_objects; object is removed if:
1373 - object is not found in m_active_objects (this is actually an
1374 error condition; objects should be set m_removed=true and removed
1375 only after all clients have been informed about removal), or
1376 - object has m_removed=true, or
1377 - object is too far away
1379 for(std::set<u16>::iterator
1380 i = current_objects.begin();
1381 i != current_objects.end(); ++i)
1384 ServerActiveObject *object = getActiveObject(id);
1386 if (object == NULL) {
1387 infostream << "ServerEnvironment::getRemovedActiveObjects():"
1388 << " object in current_objects is NULL" << std::endl;
1389 removed_objects.push(id);
1393 if (object->m_removed || object->m_pending_deactivation) {
1394 removed_objects.push(id);
1398 f32 distance_f = object->getBasePosition().getDistanceFrom(player->getPosition());
1399 if (object->getType() == ACTIVEOBJECT_TYPE_PLAYER) {
1400 if (distance_f <= player_radius_f || player_radius_f == 0)
1402 } else if (distance_f <= radius_f)
1405 // Object is no longer visible
1406 removed_objects.push(id);
1410 void ServerEnvironment::setStaticForActiveObjectsInBlock(
1411 v3s16 blockpos, bool static_exists, v3s16 static_block)
1413 MapBlock *block = m_map->getBlockNoCreateNoEx(blockpos);
1417 for (std::map<u16, StaticObject>::iterator
1418 so_it = block->m_static_objects.m_active.begin();
1419 so_it != block->m_static_objects.m_active.end(); ++so_it) {
1420 // Get the ServerActiveObject counterpart to this StaticObject
1421 std::map<u16, ServerActiveObject *>::iterator ao_it;
1422 ao_it = m_active_objects.find(so_it->first);
1423 if (ao_it == m_active_objects.end()) {
1424 // If this ever happens, there must be some kind of nasty bug.
1425 errorstream << "ServerEnvironment::setStaticForObjectsInBlock(): "
1426 "Object from MapBlock::m_static_objects::m_active not found "
1427 "in m_active_objects";
1431 ServerActiveObject *sao = ao_it->second;
1432 sao->m_static_exists = static_exists;
1433 sao->m_static_block = static_block;
1437 ActiveObjectMessage ServerEnvironment::getActiveObjectMessage()
1439 if(m_active_object_messages.empty())
1440 return ActiveObjectMessage(0);
1442 ActiveObjectMessage message = m_active_object_messages.front();
1443 m_active_object_messages.pop();
1448 ************ Private methods *************
1451 u16 ServerEnvironment::addActiveObjectRaw(ServerActiveObject *object,
1452 bool set_changed, u32 dtime_s)
1454 assert(object); // Pre-condition
1455 if(object->getId() == 0){
1456 u16 new_id = getFreeServerActiveObjectId(m_active_objects);
1459 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1460 <<"no free ids available"<<std::endl;
1461 if(object->environmentDeletes())
1465 object->setId(new_id);
1468 verbosestream<<"ServerEnvironment::addActiveObjectRaw(): "
1469 <<"supplied with id "<<object->getId()<<std::endl;
1471 if(isFreeServerActiveObjectId(object->getId(), m_active_objects) == false)
1473 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1474 <<"id is not free ("<<object->getId()<<")"<<std::endl;
1475 if(object->environmentDeletes())
1480 if (objectpos_over_limit(object->getBasePosition())) {
1481 v3f p = object->getBasePosition();
1482 errorstream << "ServerEnvironment::addActiveObjectRaw(): "
1483 << "object position (" << p.X << "," << p.Y << "," << p.Z
1484 << ") outside maximum range" << std::endl;
1485 if (object->environmentDeletes())
1490 /*infostream<<"ServerEnvironment::addActiveObjectRaw(): "
1491 <<"added (id="<<object->getId()<<")"<<std::endl;*/
1493 m_active_objects[object->getId()] = object;
1495 verbosestream<<"ServerEnvironment::addActiveObjectRaw(): "
1496 <<"Added id="<<object->getId()<<"; there are now "
1497 <<m_active_objects.size()<<" active objects."
1500 // Register reference in scripting api (must be done before post-init)
1501 m_script->addObjectReference(object);
1502 // Post-initialize object
1503 object->addedToEnvironment(dtime_s);
1505 // Add static data to block
1506 if(object->isStaticAllowed())
1508 // Add static object to active static list of the block
1509 v3f objectpos = object->getBasePosition();
1510 std::string staticdata = object->getStaticData();
1511 StaticObject s_obj(object->getType(), objectpos, staticdata);
1512 // Add to the block where the object is located in
1513 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1514 MapBlock *block = m_map->emergeBlock(blockpos);
1516 block->m_static_objects.m_active[object->getId()] = s_obj;
1517 object->m_static_exists = true;
1518 object->m_static_block = blockpos;
1521 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1522 MOD_REASON_ADD_ACTIVE_OBJECT_RAW);
1524 v3s16 p = floatToInt(objectpos, BS);
1525 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1526 <<"could not emerge block for storing id="<<object->getId()
1527 <<" statically (pos="<<PP(p)<<")"<<std::endl;
1531 return object->getId();
1535 Remove objects that satisfy (m_removed && m_known_by_count==0)
1537 void ServerEnvironment::removeRemovedObjects()
1539 std::vector<u16> objects_to_remove;
1540 for(std::map<u16, ServerActiveObject*>::iterator
1541 i = m_active_objects.begin();
1542 i != m_active_objects.end(); ++i) {
1544 ServerActiveObject* obj = i->second;
1545 // This shouldn't happen but check it
1548 infostream<<"NULL object found in ServerEnvironment"
1549 <<" while finding removed objects. id="<<id<<std::endl;
1550 // Id to be removed from m_active_objects
1551 objects_to_remove.push_back(id);
1556 We will delete objects that are marked as removed or thatare
1557 waiting for deletion after deactivation
1559 if(obj->m_removed == false && obj->m_pending_deactivation == false)
1563 Delete static data from block if is marked as removed
1565 if(obj->m_static_exists && obj->m_removed)
1567 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
1569 block->m_static_objects.remove(id);
1570 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1571 MOD_REASON_REMOVE_OBJECTS_REMOVE);
1572 obj->m_static_exists = false;
1574 infostream<<"Failed to emerge block from which an object to "
1575 <<"be removed was loaded from. id="<<id<<std::endl;
1579 // If m_known_by_count > 0, don't actually remove. On some future
1580 // invocation this will be 0, which is when removal will continue.
1581 if(obj->m_known_by_count > 0)
1585 Move static data from active to stored if not marked as removed
1587 if(obj->m_static_exists && !obj->m_removed){
1588 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
1590 std::map<u16, StaticObject>::iterator i =
1591 block->m_static_objects.m_active.find(id);
1592 if(i != block->m_static_objects.m_active.end()){
1593 block->m_static_objects.m_stored.push_back(i->second);
1594 block->m_static_objects.m_active.erase(id);
1595 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1596 MOD_REASON_REMOVE_OBJECTS_DEACTIVATE);
1599 infostream<<"Failed to emerge block from which an object to "
1600 <<"be deactivated was loaded from. id="<<id<<std::endl;
1604 // Tell the object about removal
1605 obj->removingFromEnvironment();
1606 // Deregister in scripting api
1607 m_script->removeObjectReference(obj);
1610 if(obj->environmentDeletes())
1613 // Id to be removed from m_active_objects
1614 objects_to_remove.push_back(id);
1616 // Remove references from m_active_objects
1617 for(std::vector<u16>::iterator i = objects_to_remove.begin();
1618 i != objects_to_remove.end(); ++i) {
1619 m_active_objects.erase(*i);
1623 static void print_hexdump(std::ostream &o, const std::string &data)
1625 const int linelength = 16;
1626 for(int l=0; ; l++){
1627 int i0 = linelength * l;
1628 bool at_end = false;
1629 int thislinelength = linelength;
1630 if(i0 + thislinelength > (int)data.size()){
1631 thislinelength = data.size() - i0;
1634 for(int di=0; di<linelength; di++){
1637 if(di<thislinelength)
1638 snprintf(buf, 4, "%.2x ", data[i]);
1640 snprintf(buf, 4, " ");
1644 for(int di=0; di<thislinelength; di++){
1658 Convert stored objects from blocks near the players to active.
1660 void ServerEnvironment::activateObjects(MapBlock *block, u32 dtime_s)
1665 // Ignore if no stored objects (to not set changed flag)
1666 if(block->m_static_objects.m_stored.empty())
1669 verbosestream<<"ServerEnvironment::activateObjects(): "
1670 <<"activating objects of block "<<PP(block->getPos())
1671 <<" ("<<block->m_static_objects.m_stored.size()
1672 <<" objects)"<<std::endl;
1673 bool large_amount = (block->m_static_objects.m_stored.size() > g_settings->getU16("max_objects_per_block"));
1675 errorstream<<"suspiciously large amount of objects detected: "
1676 <<block->m_static_objects.m_stored.size()<<" in "
1677 <<PP(block->getPos())
1678 <<"; removing all of them."<<std::endl;
1679 // Clear stored list
1680 block->m_static_objects.m_stored.clear();
1681 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1682 MOD_REASON_TOO_MANY_OBJECTS);
1686 // Activate stored objects
1687 std::vector<StaticObject> new_stored;
1688 for (std::vector<StaticObject>::iterator
1689 i = block->m_static_objects.m_stored.begin();
1690 i != block->m_static_objects.m_stored.end(); ++i) {
1691 StaticObject &s_obj = *i;
1693 // Create an active object from the data
1694 ServerActiveObject *obj = ServerActiveObject::create
1695 ((ActiveObjectType) s_obj.type, this, 0, s_obj.pos, s_obj.data);
1696 // If couldn't create object, store static data back.
1698 errorstream<<"ServerEnvironment::activateObjects(): "
1699 <<"failed to create active object from static object "
1700 <<"in block "<<PP(s_obj.pos/BS)
1701 <<" type="<<(int)s_obj.type<<" data:"<<std::endl;
1702 print_hexdump(verbosestream, s_obj.data);
1704 new_stored.push_back(s_obj);
1707 verbosestream<<"ServerEnvironment::activateObjects(): "
1708 <<"activated static object pos="<<PP(s_obj.pos/BS)
1709 <<" type="<<(int)s_obj.type<<std::endl;
1710 // This will also add the object to the active static list
1711 addActiveObjectRaw(obj, false, dtime_s);
1713 // Clear stored list
1714 block->m_static_objects.m_stored.clear();
1715 // Add leftover failed stuff to stored list
1716 for(std::vector<StaticObject>::iterator
1717 i = new_stored.begin();
1718 i != new_stored.end(); ++i) {
1719 StaticObject &s_obj = *i;
1720 block->m_static_objects.m_stored.push_back(s_obj);
1723 // Turn the active counterparts of activated objects not pending for
1725 for(std::map<u16, StaticObject>::iterator
1726 i = block->m_static_objects.m_active.begin();
1727 i != block->m_static_objects.m_active.end(); ++i)
1730 ServerActiveObject *object = getActiveObject(id);
1732 object->m_pending_deactivation = false;
1736 Note: Block hasn't really been modified here.
1737 The objects have just been activated and moved from the stored
1738 static list to the active static list.
1739 As such, the block is essentially the same.
1740 Thus, do not call block->raiseModified(MOD_STATE_WRITE_NEEDED).
1741 Otherwise there would be a huge amount of unnecessary I/O.
1746 Convert objects that are not standing inside active blocks to static.
1748 If m_known_by_count != 0, active object is not deleted, but static
1749 data is still updated.
1751 If force_delete is set, active object is deleted nevertheless. It
1752 shall only be set so in the destructor of the environment.
1754 If block wasn't generated (not in memory or on disk),
1756 void ServerEnvironment::deactivateFarObjects(bool force_delete)
1758 std::vector<u16> objects_to_remove;
1759 for(std::map<u16, ServerActiveObject*>::iterator
1760 i = m_active_objects.begin();
1761 i != m_active_objects.end(); ++i) {
1762 ServerActiveObject* obj = i->second;
1765 // Do not deactivate if static data creation not allowed
1766 if(!force_delete && !obj->isStaticAllowed())
1769 // If pending deactivation, let removeRemovedObjects() do it
1770 if(!force_delete && obj->m_pending_deactivation)
1774 v3f objectpos = obj->getBasePosition();
1776 // The block in which the object resides in
1777 v3s16 blockpos_o = getNodeBlockPos(floatToInt(objectpos, BS));
1779 // If object's static data is stored in a deactivated block and object
1780 // is actually located in an active block, re-save to the block in
1781 // which the object is actually located in.
1783 obj->m_static_exists &&
1784 !m_active_blocks.contains(obj->m_static_block) &&
1785 m_active_blocks.contains(blockpos_o))
1787 v3s16 old_static_block = obj->m_static_block;
1789 // Save to block where object is located
1790 MapBlock *block = m_map->emergeBlock(blockpos_o, false);
1792 errorstream<<"ServerEnvironment::deactivateFarObjects(): "
1793 <<"Could not save object id="<<id
1794 <<" to it's current block "<<PP(blockpos_o)
1798 std::string staticdata_new = obj->getStaticData();
1799 StaticObject s_obj(obj->getType(), objectpos, staticdata_new);
1800 block->m_static_objects.insert(id, s_obj);
1801 obj->m_static_block = blockpos_o;
1802 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1803 MOD_REASON_STATIC_DATA_ADDED);
1805 // Delete from block where object was located
1806 block = m_map->emergeBlock(old_static_block, false);
1808 errorstream<<"ServerEnvironment::deactivateFarObjects(): "
1809 <<"Could not delete object id="<<id
1810 <<" from it's previous block "<<PP(old_static_block)
1814 block->m_static_objects.remove(id);
1815 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1816 MOD_REASON_STATIC_DATA_REMOVED);
1820 // If block is active, don't remove
1821 if(!force_delete && m_active_blocks.contains(blockpos_o))
1824 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
1825 <<"deactivating object id="<<id<<" on inactive block "
1826 <<PP(blockpos_o)<<std::endl;
1828 // If known by some client, don't immediately delete.
1829 bool pending_delete = (obj->m_known_by_count > 0 && !force_delete);
1832 Update the static data
1835 if(obj->isStaticAllowed())
1837 // Create new static object
1838 std::string staticdata_new = obj->getStaticData();
1839 StaticObject s_obj(obj->getType(), objectpos, staticdata_new);
1841 bool stays_in_same_block = false;
1842 bool data_changed = true;
1844 if (obj->m_static_exists) {
1845 if (obj->m_static_block == blockpos_o)
1846 stays_in_same_block = true;
1848 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
1851 std::map<u16, StaticObject>::iterator n =
1852 block->m_static_objects.m_active.find(id);
1853 if (n != block->m_static_objects.m_active.end()) {
1854 StaticObject static_old = n->second;
1856 float save_movem = obj->getMinimumSavedMovement();
1858 if (static_old.data == staticdata_new &&
1859 (static_old.pos - objectpos).getLength() < save_movem)
1860 data_changed = false;
1862 errorstream<<"ServerEnvironment::deactivateFarObjects(): "
1863 <<"id="<<id<<" m_static_exists=true but "
1864 <<"static data doesn't actually exist in "
1865 <<PP(obj->m_static_block)<<std::endl;
1870 bool shall_be_written = (!stays_in_same_block || data_changed);
1872 // Delete old static object
1873 if(obj->m_static_exists)
1875 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
1878 block->m_static_objects.remove(id);
1879 obj->m_static_exists = false;
1880 // Only mark block as modified if data changed considerably
1881 if(shall_be_written)
1882 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1883 MOD_REASON_STATIC_DATA_CHANGED);
1887 // Add to the block where the object is located in
1888 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1889 // Get or generate the block
1890 MapBlock *block = NULL;
1892 block = m_map->emergeBlock(blockpos);
1893 } catch(InvalidPositionException &e){
1894 // Handled via NULL pointer
1895 // NOTE: emergeBlock's failure is usually determined by it
1896 // actually returning NULL
1901 if(block->m_static_objects.m_stored.size() >= g_settings->getU16("max_objects_per_block")){
1902 errorstream<<"ServerEnv: Trying to store id="<<obj->getId()
1903 <<" statically but block "<<PP(blockpos)
1904 <<" already contains "
1905 <<block->m_static_objects.m_stored.size()
1907 <<" Forcing delete."<<std::endl;
1908 force_delete = true;
1910 // If static counterpart already exists in target block,
1912 // This shouldn't happen because the object is removed from
1913 // the previous block before this according to
1914 // obj->m_static_block, but happens rarely for some unknown
1915 // reason. Unsuccessful attempts have been made to find
1917 if(id && block->m_static_objects.m_active.find(id) != block->m_static_objects.m_active.end()){
1918 warningstream<<"ServerEnv: Performing hack #83274"
1920 block->m_static_objects.remove(id);
1922 // Store static data
1923 u16 store_id = pending_delete ? id : 0;
1924 block->m_static_objects.insert(store_id, s_obj);
1926 // Only mark block as modified if data changed considerably
1927 if(shall_be_written)
1928 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1929 MOD_REASON_STATIC_DATA_CHANGED);
1931 obj->m_static_exists = true;
1932 obj->m_static_block = block->getPos();
1937 v3s16 p = floatToInt(objectpos, BS);
1938 errorstream<<"ServerEnv: Could not find or generate "
1939 <<"a block for storing id="<<obj->getId()
1940 <<" statically (pos="<<PP(p)<<")"<<std::endl;
1947 If known by some client, set pending deactivation.
1948 Otherwise delete it immediately.
1951 if(pending_delete && !force_delete)
1953 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
1954 <<"object id="<<id<<" is known by clients"
1955 <<"; not deleting yet"<<std::endl;
1957 obj->m_pending_deactivation = true;
1961 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
1962 <<"object id="<<id<<" is not known by clients"
1963 <<"; deleting"<<std::endl;
1965 // Tell the object about removal
1966 obj->removingFromEnvironment();
1967 // Deregister in scripting api
1968 m_script->removeObjectReference(obj);
1970 // Delete active object
1971 if(obj->environmentDeletes())
1973 // Id to be removed from m_active_objects
1974 objects_to_remove.push_back(id);
1977 // Remove references from m_active_objects
1978 for(std::vector<u16>::iterator i = objects_to_remove.begin();
1979 i != objects_to_remove.end(); ++i) {
1980 m_active_objects.erase(*i);
1986 #include "clientsimpleobject.h"
1992 ClientEnvironment::ClientEnvironment(ClientMap *map, scene::ISceneManager *smgr,
1993 ITextureSource *texturesource, IGameDef *gamedef,
1994 IrrlichtDevice *irr):
1997 m_texturesource(texturesource),
2002 memset(attachement_parent_ids, zero, sizeof(attachement_parent_ids));
2005 ClientEnvironment::~ClientEnvironment()
2007 // delete active objects
2008 for(std::map<u16, ClientActiveObject*>::iterator
2009 i = m_active_objects.begin();
2010 i != m_active_objects.end(); ++i)
2015 for(std::vector<ClientSimpleObject*>::iterator
2016 i = m_simple_objects.begin(); i != m_simple_objects.end(); ++i) {
2024 Map & ClientEnvironment::getMap()
2029 ClientMap & ClientEnvironment::getClientMap()
2034 void ClientEnvironment::addPlayer(Player *player)
2036 DSTACK(FUNCTION_NAME);
2038 It is a failure if player is local and there already is a local
2041 FATAL_ERROR_IF(player->isLocal() == true && getLocalPlayer() != NULL,
2042 "Player is local but there is already a local player");
2044 Environment::addPlayer(player);
2047 LocalPlayer * ClientEnvironment::getLocalPlayer()
2049 for(std::vector<Player*>::iterator i = m_players.begin();
2050 i != m_players.end(); ++i) {
2051 Player *player = *i;
2052 if(player->isLocal())
2053 return (LocalPlayer*)player;
2058 void ClientEnvironment::step(float dtime)
2060 DSTACK(FUNCTION_NAME);
2062 /* Step time of day */
2063 stepTimeOfDay(dtime);
2065 // Get some settings
2066 bool fly_allowed = m_gamedef->checkLocalPrivilege("fly");
2067 bool free_move = fly_allowed && g_settings->getBool("free_move");
2070 LocalPlayer *lplayer = getLocalPlayer();
2072 // collision info queue
2073 std::vector<CollisionInfo> player_collisions;
2076 Get the speed the player is going
2078 bool is_climbing = lplayer->is_climbing;
2080 f32 player_speed = lplayer->getSpeed().getLength();
2083 Maximum position increment
2085 //f32 position_max_increment = 0.05*BS;
2086 f32 position_max_increment = 0.1*BS;
2088 // Maximum time increment (for collision detection etc)
2089 // time = distance / speed
2090 f32 dtime_max_increment = 1;
2091 if(player_speed > 0.001)
2092 dtime_max_increment = position_max_increment / player_speed;
2094 // Maximum time increment is 10ms or lower
2095 if(dtime_max_increment > 0.01)
2096 dtime_max_increment = 0.01;
2098 // Don't allow overly huge dtime
2102 f32 dtime_downcount = dtime;
2105 Stuff that has a maximum time increment
2114 if(dtime_downcount > dtime_max_increment)
2116 dtime_part = dtime_max_increment;
2117 dtime_downcount -= dtime_part;
2121 dtime_part = dtime_downcount;
2123 Setting this to 0 (no -=dtime_part) disables an infinite loop
2124 when dtime_part is so small that dtime_downcount -= dtime_part
2127 dtime_downcount = 0;
2136 if(free_move == false && is_climbing == false)
2139 v3f speed = lplayer->getSpeed();
2140 if(lplayer->in_liquid == false)
2141 speed.Y -= lplayer->movement_gravity * lplayer->physics_override_gravity * dtime_part * 2;
2143 // Liquid floating / sinking
2144 if(lplayer->in_liquid && !lplayer->swimming_vertical)
2145 speed.Y -= lplayer->movement_liquid_sink * dtime_part * 2;
2147 // Liquid resistance
2148 if(lplayer->in_liquid_stable || lplayer->in_liquid)
2150 // How much the node's viscosity blocks movement, ranges between 0 and 1
2151 // Should match the scale at which viscosity increase affects other liquid attributes
2152 const f32 viscosity_factor = 0.3;
2154 v3f d_wanted = -speed / lplayer->movement_liquid_fluidity;
2155 f32 dl = d_wanted.getLength();
2156 if(dl > lplayer->movement_liquid_fluidity_smooth)
2157 dl = lplayer->movement_liquid_fluidity_smooth;
2158 dl *= (lplayer->liquid_viscosity * viscosity_factor) + (1 - viscosity_factor);
2160 v3f d = d_wanted.normalize() * dl;
2164 lplayer->setSpeed(speed);
2169 This also does collision detection.
2171 lplayer->move(dtime_part, this, position_max_increment,
2172 &player_collisions);
2175 while(dtime_downcount > 0.001);
2177 //std::cout<<"Looped "<<loopcount<<" times."<<std::endl;
2179 for(std::vector<CollisionInfo>::iterator i = player_collisions.begin();
2180 i != player_collisions.end(); ++i) {
2181 CollisionInfo &info = *i;
2182 v3f speed_diff = info.new_speed - info.old_speed;;
2183 // Handle only fall damage
2184 // (because otherwise walking against something in fast_move kills you)
2185 if(speed_diff.Y < 0 || info.old_speed.Y >= 0)
2187 // Get rid of other components
2190 f32 pre_factor = 1; // 1 hp per node/s
2191 f32 tolerance = BS*14; // 5 without damage
2192 f32 post_factor = 1; // 1 hp per node/s
2193 if(info.type == COLLISION_NODE)
2195 const ContentFeatures &f = m_gamedef->ndef()->
2196 get(m_map->getNodeNoEx(info.node_p));
2197 // Determine fall damage multiplier
2198 int addp = itemgroup_get(f.groups, "fall_damage_add_percent");
2199 pre_factor = 1.0 + (float)addp/100.0;
2201 float speed = pre_factor * speed_diff.getLength();
2202 if(speed > tolerance)
2204 f32 damage_f = (speed - tolerance)/BS * post_factor;
2205 u16 damage = (u16)(damage_f+0.5);
2207 damageLocalPlayer(damage, true);
2208 MtEvent *e = new SimpleTriggerEvent("PlayerFallingDamage");
2209 m_gamedef->event()->put(e);
2215 A quick draft of lava damage
2217 if(m_lava_hurt_interval.step(dtime, 1.0))
2219 v3f pf = lplayer->getPosition();
2221 // Feet, middle and head
2222 v3s16 p1 = floatToInt(pf + v3f(0, BS*0.1, 0), BS);
2223 MapNode n1 = m_map->getNodeNoEx(p1);
2224 v3s16 p2 = floatToInt(pf + v3f(0, BS*0.8, 0), BS);
2225 MapNode n2 = m_map->getNodeNoEx(p2);
2226 v3s16 p3 = floatToInt(pf + v3f(0, BS*1.6, 0), BS);
2227 MapNode n3 = m_map->getNodeNoEx(p3);
2229 u32 damage_per_second = 0;
2230 damage_per_second = MYMAX(damage_per_second,
2231 m_gamedef->ndef()->get(n1).damage_per_second);
2232 damage_per_second = MYMAX(damage_per_second,
2233 m_gamedef->ndef()->get(n2).damage_per_second);
2234 damage_per_second = MYMAX(damage_per_second,
2235 m_gamedef->ndef()->get(n3).damage_per_second);
2237 if(damage_per_second != 0)
2239 damageLocalPlayer(damage_per_second, true);
2246 if(m_drowning_interval.step(dtime, 2.0))
2248 v3f pf = lplayer->getPosition();
2251 v3s16 p = floatToInt(pf + v3f(0, BS*1.6, 0), BS);
2252 MapNode n = m_map->getNodeNoEx(p);
2253 ContentFeatures c = m_gamedef->ndef()->get(n);
2254 u8 drowning_damage = c.drowning;
2255 if(drowning_damage > 0 && lplayer->hp > 0){
2256 u16 breath = lplayer->getBreath();
2263 lplayer->setBreath(breath);
2264 updateLocalPlayerBreath(breath);
2267 if(lplayer->getBreath() == 0 && drowning_damage > 0){
2268 damageLocalPlayer(drowning_damage, true);
2271 if(m_breathing_interval.step(dtime, 0.5))
2273 v3f pf = lplayer->getPosition();
2276 v3s16 p = floatToInt(pf + v3f(0, BS*1.6, 0), BS);
2277 MapNode n = m_map->getNodeNoEx(p);
2278 ContentFeatures c = m_gamedef->ndef()->get(n);
2280 lplayer->setBreath(11);
2282 else if(c.drowning == 0){
2283 u16 breath = lplayer->getBreath();
2286 lplayer->setBreath(breath);
2287 updateLocalPlayerBreath(breath);
2293 Stuff that can be done in an arbitarily large dtime
2295 for(std::vector<Player*>::iterator i = m_players.begin();
2296 i != m_players.end(); ++i) {
2297 Player *player = *i;
2300 Handle non-local players
2302 if(player->isLocal() == false) {
2304 player->move(dtime, this, 100*BS);
2309 // Update lighting on local player (used for wield item)
2310 u32 day_night_ratio = getDayNightRatio();
2314 // On InvalidPositionException, use this as default
2315 // (day: LIGHT_SUN, night: 0)
2316 MapNode node_at_lplayer(CONTENT_AIR, 0x0f, 0);
2318 v3s16 p = lplayer->getLightPosition();
2319 node_at_lplayer = m_map->getNodeNoEx(p);
2321 u16 light = getInteriorLight(node_at_lplayer, 0, m_gamedef->ndef());
2322 u8 day = light & 0xff;
2323 u8 night = (light >> 8) & 0xff;
2324 finalColorBlend(lplayer->light_color, day, night, day_night_ratio);
2328 Step active objects and update lighting of them
2331 g_profiler->avg("CEnv: num of objects", m_active_objects.size());
2332 bool update_lighting = m_active_object_light_update_interval.step(dtime, 0.21);
2333 for(std::map<u16, ClientActiveObject*>::iterator
2334 i = m_active_objects.begin();
2335 i != m_active_objects.end(); ++i)
2337 ClientActiveObject* obj = i->second;
2339 obj->step(dtime, this);
2348 v3s16 p = obj->getLightPosition();
2349 MapNode n = m_map->getNodeNoEx(p, &pos_ok);
2351 light = n.getLightBlend(day_night_ratio, m_gamedef->ndef());
2353 light = blend_light(day_night_ratio, LIGHT_SUN, 0);
2355 obj->updateLight(light);
2360 Step and handle simple objects
2362 g_profiler->avg("CEnv: num of simple objects", m_simple_objects.size());
2363 for(std::vector<ClientSimpleObject*>::iterator
2364 i = m_simple_objects.begin(); i != m_simple_objects.end();) {
2365 std::vector<ClientSimpleObject*>::iterator cur = i;
2366 ClientSimpleObject *simple = *cur;
2368 simple->step(dtime);
2369 if(simple->m_to_be_removed) {
2371 i = m_simple_objects.erase(cur);
2379 void ClientEnvironment::addSimpleObject(ClientSimpleObject *simple)
2381 m_simple_objects.push_back(simple);
2384 GenericCAO* ClientEnvironment::getGenericCAO(u16 id)
2386 ClientActiveObject *obj = getActiveObject(id);
2387 if (obj && obj->getType() == ACTIVEOBJECT_TYPE_GENERIC)
2388 return (GenericCAO*) obj;
2393 ClientActiveObject* ClientEnvironment::getActiveObject(u16 id)
2395 std::map<u16, ClientActiveObject*>::iterator n;
2396 n = m_active_objects.find(id);
2397 if(n == m_active_objects.end())
2402 bool isFreeClientActiveObjectId(u16 id,
2403 std::map<u16, ClientActiveObject*> &objects)
2408 return objects.find(id) == objects.end();
2411 u16 getFreeClientActiveObjectId(
2412 std::map<u16, ClientActiveObject*> &objects)
2414 //try to reuse id's as late as possible
2415 static u16 last_used_id = 0;
2416 u16 startid = last_used_id;
2420 if(isFreeClientActiveObjectId(last_used_id, objects))
2421 return last_used_id;
2423 if(last_used_id == startid)
2428 u16 ClientEnvironment::addActiveObject(ClientActiveObject *object)
2430 assert(object); // Pre-condition
2431 if(object->getId() == 0)
2433 u16 new_id = getFreeClientActiveObjectId(m_active_objects);
2436 infostream<<"ClientEnvironment::addActiveObject(): "
2437 <<"no free ids available"<<std::endl;
2441 object->setId(new_id);
2443 if(isFreeClientActiveObjectId(object->getId(), m_active_objects) == false)
2445 infostream<<"ClientEnvironment::addActiveObject(): "
2446 <<"id is not free ("<<object->getId()<<")"<<std::endl;
2450 infostream<<"ClientEnvironment::addActiveObject(): "
2451 <<"added (id="<<object->getId()<<")"<<std::endl;
2452 m_active_objects[object->getId()] = object;
2453 object->addToScene(m_smgr, m_texturesource, m_irr);
2454 { // Update lighting immediately
2459 v3s16 p = object->getLightPosition();
2460 MapNode n = m_map->getNodeNoEx(p, &pos_ok);
2462 light = n.getLightBlend(getDayNightRatio(), m_gamedef->ndef());
2464 light = blend_light(getDayNightRatio(), LIGHT_SUN, 0);
2466 object->updateLight(light);
2468 return object->getId();
2471 void ClientEnvironment::addActiveObject(u16 id, u8 type,
2472 const std::string &init_data)
2474 ClientActiveObject* obj =
2475 ClientActiveObject::create((ActiveObjectType) type, m_gamedef, this);
2478 infostream<<"ClientEnvironment::addActiveObject(): "
2479 <<"id="<<id<<" type="<<type<<": Couldn't create object"
2488 obj->initialize(init_data);
2490 catch(SerializationError &e)
2492 errorstream<<"ClientEnvironment::addActiveObject():"
2493 <<" id="<<id<<" type="<<type
2494 <<": SerializationError in initialize(): "
2496 <<": init_data="<<serializeJsonString(init_data)
2500 addActiveObject(obj);
2503 void ClientEnvironment::removeActiveObject(u16 id)
2505 verbosestream<<"ClientEnvironment::removeActiveObject(): "
2506 <<"id="<<id<<std::endl;
2507 ClientActiveObject* obj = getActiveObject(id);
2510 infostream<<"ClientEnvironment::removeActiveObject(): "
2511 <<"id="<<id<<" not found"<<std::endl;
2514 obj->removeFromScene(true);
2516 m_active_objects.erase(id);
2519 void ClientEnvironment::processActiveObjectMessage(u16 id, const std::string &data)
2521 ClientActiveObject *obj = getActiveObject(id);
2523 infostream << "ClientEnvironment::processActiveObjectMessage():"
2524 << " got message for id=" << id << ", which doesn't exist."
2530 obj->processMessage(data);
2531 } catch (SerializationError &e) {
2532 errorstream<<"ClientEnvironment::processActiveObjectMessage():"
2533 << " id=" << id << " type=" << obj->getType()
2534 << " SerializationError in processMessage(): " << e.what()
2540 Callbacks for activeobjects
2543 void ClientEnvironment::damageLocalPlayer(u8 damage, bool handle_hp)
2545 LocalPlayer *lplayer = getLocalPlayer();
2549 if (lplayer->hp > damage)
2550 lplayer->hp -= damage;
2555 ClientEnvEvent event;
2556 event.type = CEE_PLAYER_DAMAGE;
2557 event.player_damage.amount = damage;
2558 event.player_damage.send_to_server = handle_hp;
2559 m_client_event_queue.push(event);
2562 void ClientEnvironment::updateLocalPlayerBreath(u16 breath)
2564 ClientEnvEvent event;
2565 event.type = CEE_PLAYER_BREATH;
2566 event.player_breath.amount = breath;
2567 m_client_event_queue.push(event);
2571 Client likes to call these
2574 void ClientEnvironment::getActiveObjects(v3f origin, f32 max_d,
2575 std::vector<DistanceSortedActiveObject> &dest)
2577 for(std::map<u16, ClientActiveObject*>::iterator
2578 i = m_active_objects.begin();
2579 i != m_active_objects.end(); ++i)
2581 ClientActiveObject* obj = i->second;
2583 f32 d = (obj->getPosition() - origin).getLength();
2588 DistanceSortedActiveObject dso(obj, d);
2590 dest.push_back(dso);
2594 ClientEnvEvent ClientEnvironment::getClientEvent()
2596 ClientEnvEvent event;
2597 if(m_client_event_queue.empty())
2598 event.type = CEE_NONE;
2600 event = m_client_event_queue.front();
2601 m_client_event_queue.pop();
2606 #endif // #ifndef SERVER