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():
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(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(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 MutexAutoLock(this->m_time_lock);
192 m_time_of_day_speed = speed;
195 float Environment::getTimeOfDaySpeed()
197 MutexAutoLock(this->m_time_lock);
198 float retval = m_time_of_day_speed;
202 void Environment::setTimeOfDay(u32 time)
204 MutexAutoLock(this->m_time_lock);
205 m_time_of_day = time;
206 m_time_of_day_f = (float)time / 24000.0;
209 u32 Environment::getTimeOfDay()
211 MutexAutoLock(this->m_time_lock);
212 u32 retval = m_time_of_day;
216 float Environment::getTimeOfDayF()
218 MutexAutoLock(this->m_time_lock);
219 float retval = m_time_of_day_f;
223 void Environment::stepTimeOfDay(float dtime)
225 MutexAutoLock(this->m_time_lock);
227 m_time_counter += dtime;
228 f32 speed = m_time_of_day_speed * 24000. / (24. * 3600);
229 u32 units = (u32)(m_time_counter * speed);
233 if (m_time_of_day + units >= 24000)
235 m_time_of_day = (m_time_of_day + units) % 24000;
237 m_time_of_day_f = (float)m_time_of_day / 24000.0;
240 m_time_counter -= (f32)units / speed;
243 m_time_of_day_f += m_time_of_day_speed / 24 / 3600 * dtime;
244 if (m_time_of_day_f > 1.0)
245 m_time_of_day_f -= 1.0;
246 if (m_time_of_day_f < 0.0)
247 m_time_of_day_f += 1.0;
255 ABMWithState::ABMWithState(ActiveBlockModifier *abm_):
259 // Initialize timer to random value to spread processing
260 float itv = abm->getTriggerInterval();
261 itv = MYMAX(0.001, itv); // No less than 1ms
262 int minval = MYMAX(-0.51*itv, -60); // Clamp to
263 int maxval = MYMIN(0.51*itv, 60); // +-60 seconds
264 timer = myrand_range(minval, maxval);
271 void fillRadiusBlock(v3s16 p0, s16 r, std::set<v3s16> &list)
274 for(p.X=p0.X-r; p.X<=p0.X+r; p.X++)
275 for(p.Y=p0.Y-r; p.Y<=p0.Y+r; p.Y++)
276 for(p.Z=p0.Z-r; p.Z<=p0.Z+r; p.Z++)
283 void ActiveBlockList::update(std::vector<v3s16> &active_positions,
285 std::set<v3s16> &blocks_removed,
286 std::set<v3s16> &blocks_added)
291 std::set<v3s16> newlist = m_forceloaded_list;
292 for(std::vector<v3s16>::iterator i = active_positions.begin();
293 i != active_positions.end(); ++i)
295 fillRadiusBlock(*i, radius, newlist);
299 Find out which blocks on the old list are not on the new list
301 // Go through old list
302 for(std::set<v3s16>::iterator i = m_list.begin();
303 i != m_list.end(); ++i)
306 // If not on new list, it's been removed
307 if(newlist.find(p) == newlist.end())
308 blocks_removed.insert(p);
312 Find out which blocks on the new list are not on the old list
314 // Go through new list
315 for(std::set<v3s16>::iterator i = newlist.begin();
316 i != newlist.end(); ++i)
319 // If not on old list, it's been added
320 if(m_list.find(p) == m_list.end())
321 blocks_added.insert(p);
328 for(std::set<v3s16>::iterator i = newlist.begin();
329 i != newlist.end(); ++i)
340 ServerEnvironment::ServerEnvironment(ServerMap *map,
341 GameScripting *scriptIface, IGameDef *gamedef,
342 const std::string &path_world) :
344 m_script(scriptIface),
346 m_path_world(path_world),
347 m_send_recommended_timer(0),
348 m_active_block_interval_overload_skip(0),
350 m_game_time_fraction_counter(0),
351 m_recommended_send_interval(0.1),
352 m_max_lag_estimate(0.1)
356 ServerEnvironment::~ServerEnvironment()
358 // Clear active block list.
359 // This makes the next one delete all active objects.
360 m_active_blocks.clear();
362 // Convert all objects to static and delete the active objects
363 deactivateFarObjects(true);
368 // Delete ActiveBlockModifiers
369 for(std::vector<ABMWithState>::iterator
370 i = m_abms.begin(); i != m_abms.end(); ++i){
375 Map & ServerEnvironment::getMap()
380 ServerMap & ServerEnvironment::getServerMap()
385 bool ServerEnvironment::line_of_sight(v3f pos1, v3f pos2, float stepsize, v3s16 *p)
387 float distance = pos1.getDistanceFrom(pos2);
389 //calculate normalized direction vector
390 v3f normalized_vector = v3f((pos2.X - pos1.X)/distance,
391 (pos2.Y - pos1.Y)/distance,
392 (pos2.Z - pos1.Z)/distance);
394 //find out if there's a node on path between pos1 and pos2
395 for (float i = 1; i < distance; i += stepsize) {
396 v3s16 pos = floatToInt(v3f(normalized_vector.X * i,
397 normalized_vector.Y * i,
398 normalized_vector.Z * i) +pos1,BS);
400 MapNode n = getMap().getNodeNoEx(pos);
402 if(n.param0 != CONTENT_AIR) {
412 void ServerEnvironment::kickAllPlayers(AccessDeniedCode reason,
413 const std::string &str_reason, bool reconnect)
415 for (std::vector<Player*>::iterator it = m_players.begin();
416 it != m_players.end();
418 ((Server*)m_gamedef)->DenyAccessVerCompliant((*it)->peer_id,
419 (*it)->protocol_version, (AccessDeniedCode)reason,
420 str_reason, reconnect);
424 void ServerEnvironment::saveLoadedPlayers()
426 std::string players_path = m_path_world + DIR_DELIM "players";
427 fs::CreateDir(players_path);
429 for (std::vector<Player*>::iterator it = m_players.begin();
430 it != m_players.end();
432 RemotePlayer *player = static_cast<RemotePlayer*>(*it);
433 if (player->checkModified()) {
434 player->save(players_path);
439 void ServerEnvironment::savePlayer(RemotePlayer *player)
441 std::string players_path = m_path_world + DIR_DELIM "players";
442 fs::CreateDir(players_path);
444 player->save(players_path);
447 Player *ServerEnvironment::loadPlayer(const std::string &playername)
449 bool newplayer = false;
451 std::string players_path = m_path_world + DIR_DELIM "players" DIR_DELIM;
452 std::string path = players_path + playername;
454 RemotePlayer *player = static_cast<RemotePlayer *>(getPlayer(playername.c_str()));
456 player = new RemotePlayer(m_gamedef, "");
460 for (u32 i = 0; i < PLAYER_FILE_ALTERNATE_TRIES; i++) {
461 //// Open file and deserialize
462 std::ifstream is(path.c_str(), std::ios_base::binary);
465 player->deSerialize(is, path);
468 if (player->getName() == playername) {
473 path = players_path + playername + itos(i);
477 infostream << "Player file for player " << playername
478 << " not found" << std::endl;
486 player->setModified(false);
490 void ServerEnvironment::saveMeta()
492 std::string path = m_path_world + DIR_DELIM "env_meta.txt";
494 // Open file and serialize
495 std::ostringstream ss(std::ios_base::binary);
498 args.setU64("game_time", m_game_time);
499 args.setU64("time_of_day", getTimeOfDay());
503 if(!fs::safeWriteToFile(path, ss.str()))
505 infostream<<"ServerEnvironment::saveMeta(): Failed to write "
507 throw SerializationError("Couldn't save env meta");
511 void ServerEnvironment::loadMeta()
513 std::string path = m_path_world + DIR_DELIM "env_meta.txt";
515 // Open file and deserialize
516 std::ifstream is(path.c_str(), std::ios_base::binary);
518 infostream << "ServerEnvironment::loadMeta(): Failed to open "
519 << path << std::endl;
520 throw SerializationError("Couldn't load env meta");
525 if (!args.parseConfigLines(is, "EnvArgsEnd")) {
526 throw SerializationError("ServerEnvironment::loadMeta(): "
527 "EnvArgsEnd not found!");
531 m_game_time = args.getU64("game_time");
532 } catch (SettingNotFoundException &e) {
533 // Getting this is crucial, otherwise timestamps are useless
534 throw SerializationError("Couldn't load env meta game_time");
538 m_time_of_day = args.getU64("time_of_day");
539 } catch (SettingNotFoundException &e) {
540 // This is not as important
541 m_time_of_day = 9000;
547 ActiveBlockModifier *abm;
549 std::set<content_t> required_neighbors;
555 ServerEnvironment *m_env;
556 std::map<content_t, std::vector<ActiveABM> > m_aabms;
558 ABMHandler(std::vector<ABMWithState> &abms,
559 float dtime_s, ServerEnvironment *env,
565 INodeDefManager *ndef = env->getGameDef()->ndef();
566 for(std::vector<ABMWithState>::iterator
567 i = abms.begin(); i != abms.end(); ++i) {
568 ActiveBlockModifier *abm = i->abm;
569 float trigger_interval = abm->getTriggerInterval();
570 if(trigger_interval < 0.001)
571 trigger_interval = 0.001;
572 float actual_interval = dtime_s;
575 if(i->timer < trigger_interval)
577 i->timer -= trigger_interval;
578 actual_interval = trigger_interval;
580 float chance = abm->getTriggerChance();
585 if(abm->getSimpleCatchUp()) {
586 float intervals = actual_interval / trigger_interval;
589 aabm.chance = chance / intervals;
593 aabm.chance = chance;
596 std::set<std::string> required_neighbors_s
597 = abm->getRequiredNeighbors();
598 for(std::set<std::string>::iterator
599 i = required_neighbors_s.begin();
600 i != required_neighbors_s.end(); ++i)
602 ndef->getIds(*i, aabm.required_neighbors);
605 std::set<std::string> contents_s = abm->getTriggerContents();
606 for(std::set<std::string>::iterator
607 i = contents_s.begin(); i != contents_s.end(); ++i)
609 std::set<content_t> ids;
610 ndef->getIds(*i, ids);
611 for(std::set<content_t>::const_iterator k = ids.begin();
615 std::map<content_t, std::vector<ActiveABM> >::iterator j;
617 if(j == m_aabms.end()){
618 std::vector<ActiveABM> aabmlist;
619 m_aabms[c] = aabmlist;
622 j->second.push_back(aabm);
627 // Find out how many objects the given block and its neighbours contain.
628 // Returns the number of objects in the block, and also in 'wider' the
629 // number of objects in the block and all its neighbours. The latter
630 // may an estimate if any neighbours are unloaded.
631 u32 countObjects(MapBlock *block, ServerMap * map, u32 &wider)
634 u32 wider_unknown_count = 0;
635 for(s16 x=-1; x<=1; x++)
636 for(s16 y=-1; y<=1; y++)
637 for(s16 z=-1; z<=1; z++)
639 MapBlock *block2 = map->getBlockNoCreateNoEx(
640 block->getPos() + v3s16(x,y,z));
642 wider_unknown_count++;
645 wider += block2->m_static_objects.m_active.size()
646 + block2->m_static_objects.m_stored.size();
649 u32 active_object_count = block->m_static_objects.m_active.size();
650 u32 wider_known_count = 3*3*3 - wider_unknown_count;
651 wider += wider_unknown_count * wider / wider_known_count;
652 return active_object_count;
655 void apply(MapBlock *block)
660 ServerMap *map = &m_env->getServerMap();
662 u32 active_object_count_wider;
663 u32 active_object_count = this->countObjects(block, map, active_object_count_wider);
664 m_env->m_added_objects = 0;
667 for(p0.X=0; p0.X<MAP_BLOCKSIZE; p0.X++)
668 for(p0.Y=0; p0.Y<MAP_BLOCKSIZE; p0.Y++)
669 for(p0.Z=0; p0.Z<MAP_BLOCKSIZE; p0.Z++)
671 MapNode n = block->getNodeNoEx(p0);
672 content_t c = n.getContent();
673 v3s16 p = p0 + block->getPosRelative();
675 std::map<content_t, std::vector<ActiveABM> >::iterator j;
677 if(j == m_aabms.end())
680 for(std::vector<ActiveABM>::iterator
681 i = j->second.begin(); i != j->second.end(); ++i) {
682 if(myrand() % i->chance != 0)
686 if(!i->required_neighbors.empty())
689 for(p1.X = p.X-1; p1.X <= p.X+1; p1.X++)
690 for(p1.Y = p.Y-1; p1.Y <= p.Y+1; p1.Y++)
691 for(p1.Z = p.Z-1; p1.Z <= p.Z+1; p1.Z++)
695 MapNode n = map->getNodeNoEx(p1);
696 content_t c = n.getContent();
697 std::set<content_t>::const_iterator k;
698 k = i->required_neighbors.find(c);
699 if(k != i->required_neighbors.end()){
703 // No required neighbor found
708 // Call all the trigger variations
709 i->abm->trigger(m_env, p, n);
710 i->abm->trigger(m_env, p, n,
711 active_object_count, active_object_count_wider);
713 // Count surrounding objects again if the abms added any
714 if(m_env->m_added_objects > 0) {
715 active_object_count = countObjects(block, map, active_object_count_wider);
716 m_env->m_added_objects = 0;
723 void ServerEnvironment::activateBlock(MapBlock *block, u32 additional_dtime)
725 // Reset usage timer immediately, otherwise a block that becomes active
726 // again at around the same time as it would normally be unloaded will
727 // get unloaded incorrectly. (I think this still leaves a small possibility
728 // of a race condition between this and server::AsyncRunStep, which only
729 // some kind of synchronisation will fix, but it at least reduces the window
730 // of opportunity for it to break from seconds to nanoseconds)
731 block->resetUsageTimer();
733 // Get time difference
735 u32 stamp = block->getTimestamp();
736 if(m_game_time > stamp && stamp != BLOCK_TIMESTAMP_UNDEFINED)
737 dtime_s = m_game_time - block->getTimestamp();
738 dtime_s += additional_dtime;
740 /*infostream<<"ServerEnvironment::activateBlock(): block timestamp: "
741 <<stamp<<", game time: "<<m_game_time<<std::endl;*/
743 // Set current time as timestamp
744 block->setTimestampNoChangedFlag(m_game_time);
746 /*infostream<<"ServerEnvironment::activateBlock(): block is "
747 <<dtime_s<<" seconds old."<<std::endl;*/
749 // Activate stored objects
750 activateObjects(block, dtime_s);
753 std::map<v3s16, NodeTimer> elapsed_timers =
754 block->m_node_timers.step((float)dtime_s);
755 if(!elapsed_timers.empty()){
757 for(std::map<v3s16, NodeTimer>::iterator
758 i = elapsed_timers.begin();
759 i != elapsed_timers.end(); ++i){
760 n = block->getNodeNoEx(i->first);
761 v3s16 p = i->first + block->getPosRelative();
762 if(m_script->node_on_timer(p,n,i->second.elapsed))
763 block->setNodeTimer(i->first,NodeTimer(i->second.timeout,0));
767 /* Handle ActiveBlockModifiers */
768 ABMHandler abmhandler(m_abms, dtime_s, this, false);
769 abmhandler.apply(block);
772 void ServerEnvironment::addActiveBlockModifier(ActiveBlockModifier *abm)
774 m_abms.push_back(ABMWithState(abm));
777 bool ServerEnvironment::setNode(v3s16 p, const MapNode &n)
779 INodeDefManager *ndef = m_gamedef->ndef();
780 MapNode n_old = m_map->getNodeNoEx(p);
783 if (ndef->get(n_old).has_on_destruct)
784 m_script->node_on_destruct(p, n_old);
787 if (!m_map->addNodeWithEvent(p, n))
790 // Update active VoxelManipulator if a mapgen thread
791 m_map->updateVManip(p);
793 // Call post-destructor
794 if (ndef->get(n_old).has_after_destruct)
795 m_script->node_after_destruct(p, n_old);
798 if (ndef->get(n).has_on_construct)
799 m_script->node_on_construct(p, n);
804 bool ServerEnvironment::removeNode(v3s16 p)
806 INodeDefManager *ndef = m_gamedef->ndef();
807 MapNode n_old = m_map->getNodeNoEx(p);
810 if (ndef->get(n_old).has_on_destruct)
811 m_script->node_on_destruct(p, n_old);
814 // This is slightly optimized compared to addNodeWithEvent(air)
815 if (!m_map->removeNodeWithEvent(p))
818 // Update active VoxelManipulator if a mapgen thread
819 m_map->updateVManip(p);
821 // Call post-destructor
822 if (ndef->get(n_old).has_after_destruct)
823 m_script->node_after_destruct(p, n_old);
825 // Air doesn't require constructor
829 bool ServerEnvironment::swapNode(v3s16 p, const MapNode &n)
831 if (!m_map->addNodeWithEvent(p, n, false))
834 // Update active VoxelManipulator if a mapgen thread
835 m_map->updateVManip(p);
840 void ServerEnvironment::getObjectsInsideRadius(std::vector<u16> &objects, v3f pos, float radius)
842 for(std::map<u16, ServerActiveObject*>::iterator
843 i = m_active_objects.begin();
844 i != m_active_objects.end(); ++i)
846 ServerActiveObject* obj = i->second;
848 v3f objectpos = obj->getBasePosition();
849 if(objectpos.getDistanceFrom(pos) > radius)
851 objects.push_back(id);
855 void ServerEnvironment::clearAllObjects()
857 infostream<<"ServerEnvironment::clearAllObjects(): "
858 <<"Removing all active objects"<<std::endl;
859 std::vector<u16> objects_to_remove;
860 for(std::map<u16, ServerActiveObject*>::iterator
861 i = m_active_objects.begin();
862 i != m_active_objects.end(); ++i) {
863 ServerActiveObject* obj = i->second;
864 if(obj->getType() == ACTIVEOBJECT_TYPE_PLAYER)
867 // Delete static object if block is loaded
868 if(obj->m_static_exists){
869 MapBlock *block = m_map->getBlockNoCreateNoEx(obj->m_static_block);
871 block->m_static_objects.remove(id);
872 block->raiseModified(MOD_STATE_WRITE_NEEDED,
873 MOD_REASON_CLEAR_ALL_OBJECTS);
874 obj->m_static_exists = false;
877 // If known by some client, don't delete immediately
878 if(obj->m_known_by_count > 0){
879 obj->m_pending_deactivation = true;
880 obj->m_removed = true;
884 // Tell the object about removal
885 obj->removingFromEnvironment();
886 // Deregister in scripting api
887 m_script->removeObjectReference(obj);
889 // Delete active object
890 if(obj->environmentDeletes())
892 // Id to be removed from m_active_objects
893 objects_to_remove.push_back(id);
896 // Remove references from m_active_objects
897 for(std::vector<u16>::iterator i = objects_to_remove.begin();
898 i != objects_to_remove.end(); ++i) {
899 m_active_objects.erase(*i);
902 // Get list of loaded blocks
903 std::vector<v3s16> loaded_blocks;
904 infostream<<"ServerEnvironment::clearAllObjects(): "
905 <<"Listing all loaded blocks"<<std::endl;
906 m_map->listAllLoadedBlocks(loaded_blocks);
907 infostream<<"ServerEnvironment::clearAllObjects(): "
908 <<"Done listing all loaded blocks: "
909 <<loaded_blocks.size()<<std::endl;
911 // Get list of loadable blocks
912 std::vector<v3s16> loadable_blocks;
913 infostream<<"ServerEnvironment::clearAllObjects(): "
914 <<"Listing all loadable blocks"<<std::endl;
915 m_map->listAllLoadableBlocks(loadable_blocks);
916 infostream<<"ServerEnvironment::clearAllObjects(): "
917 <<"Done listing all loadable blocks: "
918 <<loadable_blocks.size()
919 <<", now clearing"<<std::endl;
921 // Grab a reference on each loaded block to avoid unloading it
922 for(std::vector<v3s16>::iterator i = loaded_blocks.begin();
923 i != loaded_blocks.end(); ++i) {
925 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
926 assert(block != NULL);
930 // Remove objects in all loadable blocks
931 u32 unload_interval = g_settings->getS32("max_clearobjects_extra_loaded_blocks");
932 unload_interval = MYMAX(unload_interval, 1);
933 u32 report_interval = loadable_blocks.size() / 10;
934 u32 num_blocks_checked = 0;
935 u32 num_blocks_cleared = 0;
936 u32 num_objs_cleared = 0;
937 for(std::vector<v3s16>::iterator i = loadable_blocks.begin();
938 i != loadable_blocks.end(); ++i) {
940 MapBlock *block = m_map->emergeBlock(p, false);
942 errorstream<<"ServerEnvironment::clearAllObjects(): "
943 <<"Failed to emerge block "<<PP(p)<<std::endl;
946 u32 num_stored = block->m_static_objects.m_stored.size();
947 u32 num_active = block->m_static_objects.m_active.size();
948 if(num_stored != 0 || num_active != 0){
949 block->m_static_objects.m_stored.clear();
950 block->m_static_objects.m_active.clear();
951 block->raiseModified(MOD_STATE_WRITE_NEEDED,
952 MOD_REASON_CLEAR_ALL_OBJECTS);
953 num_objs_cleared += num_stored + num_active;
954 num_blocks_cleared++;
956 num_blocks_checked++;
958 if(report_interval != 0 &&
959 num_blocks_checked % report_interval == 0){
960 float percent = 100.0 * (float)num_blocks_checked /
961 loadable_blocks.size();
962 infostream<<"ServerEnvironment::clearAllObjects(): "
963 <<"Cleared "<<num_objs_cleared<<" objects"
964 <<" in "<<num_blocks_cleared<<" blocks ("
965 <<percent<<"%)"<<std::endl;
967 if(num_blocks_checked % unload_interval == 0){
968 m_map->unloadUnreferencedBlocks();
971 m_map->unloadUnreferencedBlocks();
973 // Drop references that were added above
974 for(std::vector<v3s16>::iterator i = loaded_blocks.begin();
975 i != loaded_blocks.end(); ++i) {
977 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
982 infostream<<"ServerEnvironment::clearAllObjects(): "
983 <<"Finished: Cleared "<<num_objs_cleared<<" objects"
984 <<" in "<<num_blocks_cleared<<" blocks"<<std::endl;
987 void ServerEnvironment::step(float dtime)
989 DSTACK(FUNCTION_NAME);
991 //TimeTaker timer("ServerEnv step");
993 /* Step time of day */
994 stepTimeOfDay(dtime);
997 // NOTE: This is kind of funny on a singleplayer game, but doesn't
998 // really matter that much.
999 m_recommended_send_interval = g_settings->getFloat("dedicated_server_step");
1005 m_game_time_fraction_counter += dtime;
1006 u32 inc_i = (u32)m_game_time_fraction_counter;
1007 m_game_time += inc_i;
1008 m_game_time_fraction_counter -= (float)inc_i;
1015 ScopeProfiler sp(g_profiler, "SEnv: handle players avg", SPT_AVG);
1016 for(std::vector<Player*>::iterator i = m_players.begin();
1017 i != m_players.end(); ++i)
1019 Player *player = *i;
1021 // Ignore disconnected players
1022 if(player->peer_id == 0)
1026 player->move(dtime, this, 100*BS);
1031 Manage active block list
1033 if(m_active_blocks_management_interval.step(dtime, 2.0))
1035 ScopeProfiler sp(g_profiler, "SEnv: manage act. block list avg /2s", SPT_AVG);
1037 Get player block positions
1039 std::vector<v3s16> players_blockpos;
1040 for(std::vector<Player*>::iterator
1041 i = m_players.begin();
1042 i != m_players.end(); ++i) {
1043 Player *player = *i;
1044 // Ignore disconnected players
1045 if(player->peer_id == 0)
1048 v3s16 blockpos = getNodeBlockPos(
1049 floatToInt(player->getPosition(), BS));
1050 players_blockpos.push_back(blockpos);
1054 Update list of active blocks, collecting changes
1056 const s16 active_block_range = g_settings->getS16("active_block_range");
1057 std::set<v3s16> blocks_removed;
1058 std::set<v3s16> blocks_added;
1059 m_active_blocks.update(players_blockpos, active_block_range,
1060 blocks_removed, blocks_added);
1063 Handle removed blocks
1066 // Convert active objects that are no more in active blocks to static
1067 deactivateFarObjects(false);
1069 for(std::set<v3s16>::iterator
1070 i = blocks_removed.begin();
1071 i != blocks_removed.end(); ++i)
1075 /* infostream<<"Server: Block " << PP(p)
1076 << " became inactive"<<std::endl; */
1078 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1082 // Set current time as timestamp (and let it set ChangedFlag)
1083 block->setTimestamp(m_game_time);
1090 for(std::set<v3s16>::iterator
1091 i = blocks_added.begin();
1092 i != blocks_added.end(); ++i)
1096 MapBlock *block = m_map->getBlockOrEmerge(p);
1098 m_active_blocks.m_list.erase(p);
1102 activateBlock(block);
1103 /* infostream<<"Server: Block " << PP(p)
1104 << " became active"<<std::endl; */
1109 Mess around in active blocks
1111 if(m_active_blocks_nodemetadata_interval.step(dtime, 1.0))
1113 ScopeProfiler sp(g_profiler, "SEnv: mess in act. blocks avg /1s", SPT_AVG);
1117 for(std::set<v3s16>::iterator
1118 i = m_active_blocks.m_list.begin();
1119 i != m_active_blocks.m_list.end(); ++i)
1123 /*infostream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
1124 <<") being handled"<<std::endl;*/
1126 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1130 // Reset block usage timer
1131 block->resetUsageTimer();
1133 // Set current time as timestamp
1134 block->setTimestampNoChangedFlag(m_game_time);
1135 // If time has changed much from the one on disk,
1136 // set block to be saved when it is unloaded
1137 if(block->getTimestamp() > block->getDiskTimestamp() + 60)
1138 block->raiseModified(MOD_STATE_WRITE_AT_UNLOAD,
1139 MOD_REASON_BLOCK_EXPIRED);
1142 std::map<v3s16, NodeTimer> elapsed_timers =
1143 block->m_node_timers.step((float)dtime);
1144 if(!elapsed_timers.empty()){
1146 for(std::map<v3s16, NodeTimer>::iterator
1147 i = elapsed_timers.begin();
1148 i != elapsed_timers.end(); ++i){
1149 n = block->getNodeNoEx(i->first);
1150 p = i->first + block->getPosRelative();
1151 if(m_script->node_on_timer(p,n,i->second.elapsed))
1152 block->setNodeTimer(i->first,NodeTimer(i->second.timeout,0));
1158 const float abm_interval = 1.0;
1159 if(m_active_block_modifier_interval.step(dtime, abm_interval))
1161 if(m_active_block_interval_overload_skip > 0){
1162 ScopeProfiler sp(g_profiler, "SEnv: ABM overload skips");
1163 m_active_block_interval_overload_skip--;
1166 ScopeProfiler sp(g_profiler, "SEnv: modify in blocks avg /1s", SPT_AVG);
1167 TimeTaker timer("modify in active blocks");
1169 // Initialize handling of ActiveBlockModifiers
1170 ABMHandler abmhandler(m_abms, abm_interval, this, true);
1172 for(std::set<v3s16>::iterator
1173 i = m_active_blocks.m_list.begin();
1174 i != m_active_blocks.m_list.end(); ++i)
1178 /*infostream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
1179 <<") being handled"<<std::endl;*/
1181 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1185 // Set current time as timestamp
1186 block->setTimestampNoChangedFlag(m_game_time);
1188 /* Handle ActiveBlockModifiers */
1189 abmhandler.apply(block);
1192 u32 time_ms = timer.stop(true);
1193 u32 max_time_ms = 200;
1194 if(time_ms > max_time_ms){
1195 warningstream<<"active block modifiers took "
1196 <<time_ms<<"ms (longer than "
1197 <<max_time_ms<<"ms)"<<std::endl;
1198 m_active_block_interval_overload_skip = (time_ms / max_time_ms) + 1;
1203 Step script environment (run global on_step())
1205 m_script->environment_Step(dtime);
1211 ScopeProfiler sp(g_profiler, "SEnv: step act. objs avg", SPT_AVG);
1212 //TimeTaker timer("Step active objects");
1214 g_profiler->avg("SEnv: num of objects", m_active_objects.size());
1216 // This helps the objects to send data at the same time
1217 bool send_recommended = false;
1218 m_send_recommended_timer += dtime;
1219 if(m_send_recommended_timer > getSendRecommendedInterval())
1221 m_send_recommended_timer -= getSendRecommendedInterval();
1222 send_recommended = true;
1225 for(std::map<u16, ServerActiveObject*>::iterator
1226 i = m_active_objects.begin();
1227 i != m_active_objects.end(); ++i)
1229 ServerActiveObject* obj = i->second;
1230 // Don't step if is to be removed or stored statically
1231 if(obj->m_removed || obj->m_pending_deactivation)
1234 obj->step(dtime, send_recommended);
1235 // Read messages from object
1236 while(!obj->m_messages_out.empty())
1238 m_active_object_messages.push(
1239 obj->m_messages_out.front());
1240 obj->m_messages_out.pop();
1246 Manage active objects
1248 if(m_object_management_interval.step(dtime, 0.5))
1250 ScopeProfiler sp(g_profiler, "SEnv: remove removed objs avg /.5s", SPT_AVG);
1252 Remove objects that satisfy (m_removed && m_known_by_count==0)
1254 removeRemovedObjects();
1258 ServerActiveObject* ServerEnvironment::getActiveObject(u16 id)
1260 std::map<u16, ServerActiveObject*>::iterator n;
1261 n = m_active_objects.find(id);
1262 if(n == m_active_objects.end())
1267 bool isFreeServerActiveObjectId(u16 id,
1268 std::map<u16, ServerActiveObject*> &objects)
1273 return objects.find(id) == objects.end();
1276 u16 getFreeServerActiveObjectId(
1277 std::map<u16, ServerActiveObject*> &objects)
1279 //try to reuse id's as late as possible
1280 static u16 last_used_id = 0;
1281 u16 startid = last_used_id;
1285 if(isFreeServerActiveObjectId(last_used_id, objects))
1286 return last_used_id;
1288 if(last_used_id == startid)
1293 u16 ServerEnvironment::addActiveObject(ServerActiveObject *object)
1295 assert(object); // Pre-condition
1297 u16 id = addActiveObjectRaw(object, true, 0);
1302 Finds out what new objects have been added to
1303 inside a radius around a position
1305 void ServerEnvironment::getAddedActiveObjects(Player *player, s16 radius,
1307 std::set<u16> ¤t_objects,
1308 std::queue<u16> &added_objects)
1310 f32 radius_f = radius * BS;
1311 f32 player_radius_f = player_radius * BS;
1313 if (player_radius_f < 0)
1314 player_radius_f = 0;
1317 Go through the object list,
1318 - discard m_removed objects,
1319 - discard objects that are too far away,
1320 - discard objects that are found in current_objects.
1321 - add remaining objects to added_objects
1323 for(std::map<u16, ServerActiveObject*>::iterator
1324 i = m_active_objects.begin();
1325 i != m_active_objects.end(); ++i) {
1329 ServerActiveObject *object = i->second;
1333 // Discard if removed or deactivating
1334 if(object->m_removed || object->m_pending_deactivation)
1337 f32 distance_f = object->getBasePosition().getDistanceFrom(player->getPosition());
1338 if (object->getType() == ACTIVEOBJECT_TYPE_PLAYER) {
1339 // Discard if too far
1340 if (distance_f > player_radius_f && player_radius_f != 0)
1342 } else if (distance_f > radius_f)
1345 // Discard if already on current_objects
1346 std::set<u16>::iterator n;
1347 n = current_objects.find(id);
1348 if(n != current_objects.end())
1350 // Add to added_objects
1351 added_objects.push(id);
1356 Finds out what objects have been removed from
1357 inside a radius around a position
1359 void ServerEnvironment::getRemovedActiveObjects(Player *player, s16 radius,
1361 std::set<u16> ¤t_objects,
1362 std::queue<u16> &removed_objects)
1364 f32 radius_f = radius * BS;
1365 f32 player_radius_f = player_radius * BS;
1367 if (player_radius_f < 0)
1368 player_radius_f = 0;
1371 Go through current_objects; object is removed if:
1372 - object is not found in m_active_objects (this is actually an
1373 error condition; objects should be set m_removed=true and removed
1374 only after all clients have been informed about removal), or
1375 - object has m_removed=true, or
1376 - object is too far away
1378 for(std::set<u16>::iterator
1379 i = current_objects.begin();
1380 i != current_objects.end(); ++i)
1383 ServerActiveObject *object = getActiveObject(id);
1385 if (object == NULL) {
1386 infostream << "ServerEnvironment::getRemovedActiveObjects():"
1387 << " object in current_objects is NULL" << std::endl;
1388 removed_objects.push(id);
1392 if (object->m_removed || object->m_pending_deactivation) {
1393 removed_objects.push(id);
1397 f32 distance_f = object->getBasePosition().getDistanceFrom(player->getPosition());
1398 if (object->getType() == ACTIVEOBJECT_TYPE_PLAYER) {
1399 if (distance_f <= player_radius_f || player_radius_f == 0)
1401 } else if (distance_f <= radius_f)
1404 // Object is no longer visible
1405 removed_objects.push(id);
1409 void ServerEnvironment::setStaticForActiveObjectsInBlock(
1410 v3s16 blockpos, bool static_exists, v3s16 static_block)
1412 MapBlock *block = m_map->getBlockNoCreateNoEx(blockpos);
1416 for (std::map<u16, StaticObject>::iterator
1417 so_it = block->m_static_objects.m_active.begin();
1418 so_it != block->m_static_objects.m_active.end(); ++so_it) {
1419 // Get the ServerActiveObject counterpart to this StaticObject
1420 std::map<u16, ServerActiveObject *>::iterator ao_it;
1421 ao_it = m_active_objects.find(so_it->first);
1422 if (ao_it == m_active_objects.end()) {
1423 // If this ever happens, there must be some kind of nasty bug.
1424 errorstream << "ServerEnvironment::setStaticForObjectsInBlock(): "
1425 "Object from MapBlock::m_static_objects::m_active not found "
1426 "in m_active_objects";
1430 ServerActiveObject *sao = ao_it->second;
1431 sao->m_static_exists = static_exists;
1432 sao->m_static_block = static_block;
1436 ActiveObjectMessage ServerEnvironment::getActiveObjectMessage()
1438 if(m_active_object_messages.empty())
1439 return ActiveObjectMessage(0);
1441 ActiveObjectMessage message = m_active_object_messages.front();
1442 m_active_object_messages.pop();
1447 ************ Private methods *************
1450 u16 ServerEnvironment::addActiveObjectRaw(ServerActiveObject *object,
1451 bool set_changed, u32 dtime_s)
1453 assert(object); // Pre-condition
1454 if(object->getId() == 0){
1455 u16 new_id = getFreeServerActiveObjectId(m_active_objects);
1458 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1459 <<"no free ids available"<<std::endl;
1460 if(object->environmentDeletes())
1464 object->setId(new_id);
1467 verbosestream<<"ServerEnvironment::addActiveObjectRaw(): "
1468 <<"supplied with id "<<object->getId()<<std::endl;
1470 if(isFreeServerActiveObjectId(object->getId(), m_active_objects) == false)
1472 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1473 <<"id is not free ("<<object->getId()<<")"<<std::endl;
1474 if(object->environmentDeletes())
1479 if (objectpos_over_limit(object->getBasePosition())) {
1480 v3f p = object->getBasePosition();
1481 errorstream << "ServerEnvironment::addActiveObjectRaw(): "
1482 << "object position (" << p.X << "," << p.Y << "," << p.Z
1483 << ") outside maximum range" << std::endl;
1484 if (object->environmentDeletes())
1489 /*infostream<<"ServerEnvironment::addActiveObjectRaw(): "
1490 <<"added (id="<<object->getId()<<")"<<std::endl;*/
1492 m_active_objects[object->getId()] = object;
1494 verbosestream<<"ServerEnvironment::addActiveObjectRaw(): "
1495 <<"Added id="<<object->getId()<<"; there are now "
1496 <<m_active_objects.size()<<" active objects."
1499 // Register reference in scripting api (must be done before post-init)
1500 m_script->addObjectReference(object);
1501 // Post-initialize object
1502 object->addedToEnvironment(dtime_s);
1504 // Add static data to block
1505 if(object->isStaticAllowed())
1507 // Add static object to active static list of the block
1508 v3f objectpos = object->getBasePosition();
1509 std::string staticdata = object->getStaticData();
1510 StaticObject s_obj(object->getType(), objectpos, staticdata);
1511 // Add to the block where the object is located in
1512 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1513 MapBlock *block = m_map->emergeBlock(blockpos);
1515 block->m_static_objects.m_active[object->getId()] = s_obj;
1516 object->m_static_exists = true;
1517 object->m_static_block = blockpos;
1520 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1521 MOD_REASON_ADD_ACTIVE_OBJECT_RAW);
1523 v3s16 p = floatToInt(objectpos, BS);
1524 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1525 <<"could not emerge block for storing id="<<object->getId()
1526 <<" statically (pos="<<PP(p)<<")"<<std::endl;
1530 return object->getId();
1534 Remove objects that satisfy (m_removed && m_known_by_count==0)
1536 void ServerEnvironment::removeRemovedObjects()
1538 std::vector<u16> objects_to_remove;
1539 for(std::map<u16, ServerActiveObject*>::iterator
1540 i = m_active_objects.begin();
1541 i != m_active_objects.end(); ++i) {
1543 ServerActiveObject* obj = i->second;
1544 // This shouldn't happen but check it
1547 infostream<<"NULL object found in ServerEnvironment"
1548 <<" while finding removed objects. id="<<id<<std::endl;
1549 // Id to be removed from m_active_objects
1550 objects_to_remove.push_back(id);
1555 We will delete objects that are marked as removed or thatare
1556 waiting for deletion after deactivation
1558 if(obj->m_removed == false && obj->m_pending_deactivation == false)
1562 Delete static data from block if is marked as removed
1564 if(obj->m_static_exists && obj->m_removed)
1566 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
1568 block->m_static_objects.remove(id);
1569 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1570 MOD_REASON_REMOVE_OBJECTS_REMOVE);
1571 obj->m_static_exists = false;
1573 infostream<<"Failed to emerge block from which an object to "
1574 <<"be removed was loaded from. id="<<id<<std::endl;
1578 // If m_known_by_count > 0, don't actually remove. On some future
1579 // invocation this will be 0, which is when removal will continue.
1580 if(obj->m_known_by_count > 0)
1584 Move static data from active to stored if not marked as removed
1586 if(obj->m_static_exists && !obj->m_removed){
1587 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
1589 std::map<u16, StaticObject>::iterator i =
1590 block->m_static_objects.m_active.find(id);
1591 if(i != block->m_static_objects.m_active.end()){
1592 block->m_static_objects.m_stored.push_back(i->second);
1593 block->m_static_objects.m_active.erase(id);
1594 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1595 MOD_REASON_REMOVE_OBJECTS_DEACTIVATE);
1598 infostream<<"Failed to emerge block from which an object to "
1599 <<"be deactivated was loaded from. id="<<id<<std::endl;
1603 // Tell the object about removal
1604 obj->removingFromEnvironment();
1605 // Deregister in scripting api
1606 m_script->removeObjectReference(obj);
1609 if(obj->environmentDeletes())
1612 // Id to be removed from m_active_objects
1613 objects_to_remove.push_back(id);
1615 // Remove references from m_active_objects
1616 for(std::vector<u16>::iterator i = objects_to_remove.begin();
1617 i != objects_to_remove.end(); ++i) {
1618 m_active_objects.erase(*i);
1622 static void print_hexdump(std::ostream &o, const std::string &data)
1624 const int linelength = 16;
1625 for(int l=0; ; l++){
1626 int i0 = linelength * l;
1627 bool at_end = false;
1628 int thislinelength = linelength;
1629 if(i0 + thislinelength > (int)data.size()){
1630 thislinelength = data.size() - i0;
1633 for(int di=0; di<linelength; di++){
1636 if(di<thislinelength)
1637 snprintf(buf, 4, "%.2x ", data[i]);
1639 snprintf(buf, 4, " ");
1643 for(int di=0; di<thislinelength; di++){
1657 Convert stored objects from blocks near the players to active.
1659 void ServerEnvironment::activateObjects(MapBlock *block, u32 dtime_s)
1664 // Ignore if no stored objects (to not set changed flag)
1665 if(block->m_static_objects.m_stored.empty())
1668 verbosestream<<"ServerEnvironment::activateObjects(): "
1669 <<"activating objects of block "<<PP(block->getPos())
1670 <<" ("<<block->m_static_objects.m_stored.size()
1671 <<" objects)"<<std::endl;
1672 bool large_amount = (block->m_static_objects.m_stored.size() > g_settings->getU16("max_objects_per_block"));
1674 errorstream<<"suspiciously large amount of objects detected: "
1675 <<block->m_static_objects.m_stored.size()<<" in "
1676 <<PP(block->getPos())
1677 <<"; removing all of them."<<std::endl;
1678 // Clear stored list
1679 block->m_static_objects.m_stored.clear();
1680 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1681 MOD_REASON_TOO_MANY_OBJECTS);
1685 // Activate stored objects
1686 std::vector<StaticObject> new_stored;
1687 for (std::vector<StaticObject>::iterator
1688 i = block->m_static_objects.m_stored.begin();
1689 i != block->m_static_objects.m_stored.end(); ++i) {
1690 StaticObject &s_obj = *i;
1692 // Create an active object from the data
1693 ServerActiveObject *obj = ServerActiveObject::create
1694 ((ActiveObjectType) s_obj.type, this, 0, s_obj.pos, s_obj.data);
1695 // If couldn't create object, store static data back.
1697 errorstream<<"ServerEnvironment::activateObjects(): "
1698 <<"failed to create active object from static object "
1699 <<"in block "<<PP(s_obj.pos/BS)
1700 <<" type="<<(int)s_obj.type<<" data:"<<std::endl;
1701 print_hexdump(verbosestream, s_obj.data);
1703 new_stored.push_back(s_obj);
1706 verbosestream<<"ServerEnvironment::activateObjects(): "
1707 <<"activated static object pos="<<PP(s_obj.pos/BS)
1708 <<" type="<<(int)s_obj.type<<std::endl;
1709 // This will also add the object to the active static list
1710 addActiveObjectRaw(obj, false, dtime_s);
1712 // Clear stored list
1713 block->m_static_objects.m_stored.clear();
1714 // Add leftover failed stuff to stored list
1715 for(std::vector<StaticObject>::iterator
1716 i = new_stored.begin();
1717 i != new_stored.end(); ++i) {
1718 StaticObject &s_obj = *i;
1719 block->m_static_objects.m_stored.push_back(s_obj);
1722 // Turn the active counterparts of activated objects not pending for
1724 for(std::map<u16, StaticObject>::iterator
1725 i = block->m_static_objects.m_active.begin();
1726 i != block->m_static_objects.m_active.end(); ++i)
1729 ServerActiveObject *object = getActiveObject(id);
1731 object->m_pending_deactivation = false;
1735 Note: Block hasn't really been modified here.
1736 The objects have just been activated and moved from the stored
1737 static list to the active static list.
1738 As such, the block is essentially the same.
1739 Thus, do not call block->raiseModified(MOD_STATE_WRITE_NEEDED).
1740 Otherwise there would be a huge amount of unnecessary I/O.
1745 Convert objects that are not standing inside active blocks to static.
1747 If m_known_by_count != 0, active object is not deleted, but static
1748 data is still updated.
1750 If force_delete is set, active object is deleted nevertheless. It
1751 shall only be set so in the destructor of the environment.
1753 If block wasn't generated (not in memory or on disk),
1755 void ServerEnvironment::deactivateFarObjects(bool force_delete)
1757 std::vector<u16> objects_to_remove;
1758 for(std::map<u16, ServerActiveObject*>::iterator
1759 i = m_active_objects.begin();
1760 i != m_active_objects.end(); ++i) {
1761 ServerActiveObject* obj = i->second;
1764 // Do not deactivate if static data creation not allowed
1765 if(!force_delete && !obj->isStaticAllowed())
1768 // If pending deactivation, let removeRemovedObjects() do it
1769 if(!force_delete && obj->m_pending_deactivation)
1773 v3f objectpos = obj->getBasePosition();
1775 // The block in which the object resides in
1776 v3s16 blockpos_o = getNodeBlockPos(floatToInt(objectpos, BS));
1778 // If object's static data is stored in a deactivated block and object
1779 // is actually located in an active block, re-save to the block in
1780 // which the object is actually located in.
1782 obj->m_static_exists &&
1783 !m_active_blocks.contains(obj->m_static_block) &&
1784 m_active_blocks.contains(blockpos_o))
1786 v3s16 old_static_block = obj->m_static_block;
1788 // Save to block where object is located
1789 MapBlock *block = m_map->emergeBlock(blockpos_o, false);
1791 errorstream<<"ServerEnvironment::deactivateFarObjects(): "
1792 <<"Could not save object id="<<id
1793 <<" to it's current block "<<PP(blockpos_o)
1797 std::string staticdata_new = obj->getStaticData();
1798 StaticObject s_obj(obj->getType(), objectpos, staticdata_new);
1799 block->m_static_objects.insert(id, s_obj);
1800 obj->m_static_block = blockpos_o;
1801 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1802 MOD_REASON_STATIC_DATA_ADDED);
1804 // Delete from block where object was located
1805 block = m_map->emergeBlock(old_static_block, false);
1807 errorstream<<"ServerEnvironment::deactivateFarObjects(): "
1808 <<"Could not delete object id="<<id
1809 <<" from it's previous block "<<PP(old_static_block)
1813 block->m_static_objects.remove(id);
1814 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1815 MOD_REASON_STATIC_DATA_REMOVED);
1819 // If block is active, don't remove
1820 if(!force_delete && m_active_blocks.contains(blockpos_o))
1823 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
1824 <<"deactivating object id="<<id<<" on inactive block "
1825 <<PP(blockpos_o)<<std::endl;
1827 // If known by some client, don't immediately delete.
1828 bool pending_delete = (obj->m_known_by_count > 0 && !force_delete);
1831 Update the static data
1834 if(obj->isStaticAllowed())
1836 // Create new static object
1837 std::string staticdata_new = obj->getStaticData();
1838 StaticObject s_obj(obj->getType(), objectpos, staticdata_new);
1840 bool stays_in_same_block = false;
1841 bool data_changed = true;
1843 if (obj->m_static_exists) {
1844 if (obj->m_static_block == blockpos_o)
1845 stays_in_same_block = true;
1847 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
1850 std::map<u16, StaticObject>::iterator n =
1851 block->m_static_objects.m_active.find(id);
1852 if (n != block->m_static_objects.m_active.end()) {
1853 StaticObject static_old = n->second;
1855 float save_movem = obj->getMinimumSavedMovement();
1857 if (static_old.data == staticdata_new &&
1858 (static_old.pos - objectpos).getLength() < save_movem)
1859 data_changed = false;
1861 errorstream<<"ServerEnvironment::deactivateFarObjects(): "
1862 <<"id="<<id<<" m_static_exists=true but "
1863 <<"static data doesn't actually exist in "
1864 <<PP(obj->m_static_block)<<std::endl;
1869 bool shall_be_written = (!stays_in_same_block || data_changed);
1871 // Delete old static object
1872 if(obj->m_static_exists)
1874 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
1877 block->m_static_objects.remove(id);
1878 obj->m_static_exists = false;
1879 // Only mark block as modified if data changed considerably
1880 if(shall_be_written)
1881 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1882 MOD_REASON_STATIC_DATA_CHANGED);
1886 // Add to the block where the object is located in
1887 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1888 // Get or generate the block
1889 MapBlock *block = NULL;
1891 block = m_map->emergeBlock(blockpos);
1892 } catch(InvalidPositionException &e){
1893 // Handled via NULL pointer
1894 // NOTE: emergeBlock's failure is usually determined by it
1895 // actually returning NULL
1900 if(block->m_static_objects.m_stored.size() >= g_settings->getU16("max_objects_per_block")){
1901 errorstream<<"ServerEnv: Trying to store id="<<obj->getId()
1902 <<" statically but block "<<PP(blockpos)
1903 <<" already contains "
1904 <<block->m_static_objects.m_stored.size()
1906 <<" Forcing delete."<<std::endl;
1907 force_delete = true;
1909 // If static counterpart already exists in target block,
1911 // This shouldn't happen because the object is removed from
1912 // the previous block before this according to
1913 // obj->m_static_block, but happens rarely for some unknown
1914 // reason. Unsuccessful attempts have been made to find
1916 if(id && block->m_static_objects.m_active.find(id) != block->m_static_objects.m_active.end()){
1917 warningstream<<"ServerEnv: Performing hack #83274"
1919 block->m_static_objects.remove(id);
1921 // Store static data
1922 u16 store_id = pending_delete ? id : 0;
1923 block->m_static_objects.insert(store_id, s_obj);
1925 // Only mark block as modified if data changed considerably
1926 if(shall_be_written)
1927 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1928 MOD_REASON_STATIC_DATA_CHANGED);
1930 obj->m_static_exists = true;
1931 obj->m_static_block = block->getPos();
1936 v3s16 p = floatToInt(objectpos, BS);
1937 errorstream<<"ServerEnv: Could not find or generate "
1938 <<"a block for storing id="<<obj->getId()
1939 <<" statically (pos="<<PP(p)<<")"<<std::endl;
1946 If known by some client, set pending deactivation.
1947 Otherwise delete it immediately.
1950 if(pending_delete && !force_delete)
1952 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
1953 <<"object id="<<id<<" is known by clients"
1954 <<"; not deleting yet"<<std::endl;
1956 obj->m_pending_deactivation = true;
1960 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
1961 <<"object id="<<id<<" is not known by clients"
1962 <<"; deleting"<<std::endl;
1964 // Tell the object about removal
1965 obj->removingFromEnvironment();
1966 // Deregister in scripting api
1967 m_script->removeObjectReference(obj);
1969 // Delete active object
1970 if(obj->environmentDeletes())
1972 // Id to be removed from m_active_objects
1973 objects_to_remove.push_back(id);
1976 // Remove references from m_active_objects
1977 for(std::vector<u16>::iterator i = objects_to_remove.begin();
1978 i != objects_to_remove.end(); ++i) {
1979 m_active_objects.erase(*i);
1985 #include "clientsimpleobject.h"
1991 ClientEnvironment::ClientEnvironment(ClientMap *map, scene::ISceneManager *smgr,
1992 ITextureSource *texturesource, IGameDef *gamedef,
1993 IrrlichtDevice *irr):
1996 m_texturesource(texturesource),
2001 memset(attachement_parent_ids, zero, sizeof(attachement_parent_ids));
2004 ClientEnvironment::~ClientEnvironment()
2006 // delete active objects
2007 for(std::map<u16, ClientActiveObject*>::iterator
2008 i = m_active_objects.begin();
2009 i != m_active_objects.end(); ++i)
2014 for(std::vector<ClientSimpleObject*>::iterator
2015 i = m_simple_objects.begin(); i != m_simple_objects.end(); ++i) {
2023 Map & ClientEnvironment::getMap()
2028 ClientMap & ClientEnvironment::getClientMap()
2033 void ClientEnvironment::addPlayer(Player *player)
2035 DSTACK(FUNCTION_NAME);
2037 It is a failure if player is local and there already is a local
2040 FATAL_ERROR_IF(player->isLocal() == true && getLocalPlayer() != NULL,
2041 "Player is local but there is already a local player");
2043 Environment::addPlayer(player);
2046 LocalPlayer * ClientEnvironment::getLocalPlayer()
2048 for(std::vector<Player*>::iterator i = m_players.begin();
2049 i != m_players.end(); ++i) {
2050 Player *player = *i;
2051 if(player->isLocal())
2052 return (LocalPlayer*)player;
2057 void ClientEnvironment::step(float dtime)
2059 DSTACK(FUNCTION_NAME);
2061 /* Step time of day */
2062 stepTimeOfDay(dtime);
2064 // Get some settings
2065 bool fly_allowed = m_gamedef->checkLocalPrivilege("fly");
2066 bool free_move = fly_allowed && g_settings->getBool("free_move");
2069 LocalPlayer *lplayer = getLocalPlayer();
2071 // collision info queue
2072 std::vector<CollisionInfo> player_collisions;
2075 Get the speed the player is going
2077 bool is_climbing = lplayer->is_climbing;
2079 f32 player_speed = lplayer->getSpeed().getLength();
2082 Maximum position increment
2084 //f32 position_max_increment = 0.05*BS;
2085 f32 position_max_increment = 0.1*BS;
2087 // Maximum time increment (for collision detection etc)
2088 // time = distance / speed
2089 f32 dtime_max_increment = 1;
2090 if(player_speed > 0.001)
2091 dtime_max_increment = position_max_increment / player_speed;
2093 // Maximum time increment is 10ms or lower
2094 if(dtime_max_increment > 0.01)
2095 dtime_max_increment = 0.01;
2097 // Don't allow overly huge dtime
2101 f32 dtime_downcount = dtime;
2104 Stuff that has a maximum time increment
2113 if(dtime_downcount > dtime_max_increment)
2115 dtime_part = dtime_max_increment;
2116 dtime_downcount -= dtime_part;
2120 dtime_part = dtime_downcount;
2122 Setting this to 0 (no -=dtime_part) disables an infinite loop
2123 when dtime_part is so small that dtime_downcount -= dtime_part
2126 dtime_downcount = 0;
2135 if(free_move == false && is_climbing == false)
2138 v3f speed = lplayer->getSpeed();
2139 if(lplayer->in_liquid == false)
2140 speed.Y -= lplayer->movement_gravity * lplayer->physics_override_gravity * dtime_part * 2;
2142 // Liquid floating / sinking
2143 if(lplayer->in_liquid && !lplayer->swimming_vertical)
2144 speed.Y -= lplayer->movement_liquid_sink * dtime_part * 2;
2146 // Liquid resistance
2147 if(lplayer->in_liquid_stable || lplayer->in_liquid)
2149 // How much the node's viscosity blocks movement, ranges between 0 and 1
2150 // Should match the scale at which viscosity increase affects other liquid attributes
2151 const f32 viscosity_factor = 0.3;
2153 v3f d_wanted = -speed / lplayer->movement_liquid_fluidity;
2154 f32 dl = d_wanted.getLength();
2155 if(dl > lplayer->movement_liquid_fluidity_smooth)
2156 dl = lplayer->movement_liquid_fluidity_smooth;
2157 dl *= (lplayer->liquid_viscosity * viscosity_factor) + (1 - viscosity_factor);
2159 v3f d = d_wanted.normalize() * dl;
2163 lplayer->setSpeed(speed);
2168 This also does collision detection.
2170 lplayer->move(dtime_part, this, position_max_increment,
2171 &player_collisions);
2174 while(dtime_downcount > 0.001);
2176 //std::cout<<"Looped "<<loopcount<<" times."<<std::endl;
2178 for(std::vector<CollisionInfo>::iterator i = player_collisions.begin();
2179 i != player_collisions.end(); ++i) {
2180 CollisionInfo &info = *i;
2181 v3f speed_diff = info.new_speed - info.old_speed;;
2182 // Handle only fall damage
2183 // (because otherwise walking against something in fast_move kills you)
2184 if(speed_diff.Y < 0 || info.old_speed.Y >= 0)
2186 // Get rid of other components
2189 f32 pre_factor = 1; // 1 hp per node/s
2190 f32 tolerance = BS*14; // 5 without damage
2191 f32 post_factor = 1; // 1 hp per node/s
2192 if(info.type == COLLISION_NODE)
2194 const ContentFeatures &f = m_gamedef->ndef()->
2195 get(m_map->getNodeNoEx(info.node_p));
2196 // Determine fall damage multiplier
2197 int addp = itemgroup_get(f.groups, "fall_damage_add_percent");
2198 pre_factor = 1.0 + (float)addp/100.0;
2200 float speed = pre_factor * speed_diff.getLength();
2201 if(speed > tolerance)
2203 f32 damage_f = (speed - tolerance)/BS * post_factor;
2204 u16 damage = (u16)(damage_f+0.5);
2206 damageLocalPlayer(damage, true);
2207 MtEvent *e = new SimpleTriggerEvent("PlayerFallingDamage");
2208 m_gamedef->event()->put(e);
2214 A quick draft of lava damage
2216 if(m_lava_hurt_interval.step(dtime, 1.0))
2218 v3f pf = lplayer->getPosition();
2220 // Feet, middle and head
2221 v3s16 p1 = floatToInt(pf + v3f(0, BS*0.1, 0), BS);
2222 MapNode n1 = m_map->getNodeNoEx(p1);
2223 v3s16 p2 = floatToInt(pf + v3f(0, BS*0.8, 0), BS);
2224 MapNode n2 = m_map->getNodeNoEx(p2);
2225 v3s16 p3 = floatToInt(pf + v3f(0, BS*1.6, 0), BS);
2226 MapNode n3 = m_map->getNodeNoEx(p3);
2228 u32 damage_per_second = 0;
2229 damage_per_second = MYMAX(damage_per_second,
2230 m_gamedef->ndef()->get(n1).damage_per_second);
2231 damage_per_second = MYMAX(damage_per_second,
2232 m_gamedef->ndef()->get(n2).damage_per_second);
2233 damage_per_second = MYMAX(damage_per_second,
2234 m_gamedef->ndef()->get(n3).damage_per_second);
2236 if(damage_per_second != 0)
2238 damageLocalPlayer(damage_per_second, true);
2245 if(m_drowning_interval.step(dtime, 2.0))
2247 v3f pf = lplayer->getPosition();
2250 v3s16 p = floatToInt(pf + v3f(0, BS*1.6, 0), BS);
2251 MapNode n = m_map->getNodeNoEx(p);
2252 ContentFeatures c = m_gamedef->ndef()->get(n);
2253 u8 drowning_damage = c.drowning;
2254 if(drowning_damage > 0 && lplayer->hp > 0){
2255 u16 breath = lplayer->getBreath();
2262 lplayer->setBreath(breath);
2263 updateLocalPlayerBreath(breath);
2266 if(lplayer->getBreath() == 0 && drowning_damage > 0){
2267 damageLocalPlayer(drowning_damage, true);
2270 if(m_breathing_interval.step(dtime, 0.5))
2272 v3f pf = lplayer->getPosition();
2275 v3s16 p = floatToInt(pf + v3f(0, BS*1.6, 0), BS);
2276 MapNode n = m_map->getNodeNoEx(p);
2277 ContentFeatures c = m_gamedef->ndef()->get(n);
2279 lplayer->setBreath(11);
2281 else if(c.drowning == 0){
2282 u16 breath = lplayer->getBreath();
2285 lplayer->setBreath(breath);
2286 updateLocalPlayerBreath(breath);
2292 Stuff that can be done in an arbitarily large dtime
2294 for(std::vector<Player*>::iterator i = m_players.begin();
2295 i != m_players.end(); ++i) {
2296 Player *player = *i;
2299 Handle non-local players
2301 if(player->isLocal() == false) {
2303 player->move(dtime, this, 100*BS);
2308 // Update lighting on local player (used for wield item)
2309 u32 day_night_ratio = getDayNightRatio();
2313 // On InvalidPositionException, use this as default
2314 // (day: LIGHT_SUN, night: 0)
2315 MapNode node_at_lplayer(CONTENT_AIR, 0x0f, 0);
2317 v3s16 p = lplayer->getLightPosition();
2318 node_at_lplayer = m_map->getNodeNoEx(p);
2320 u16 light = getInteriorLight(node_at_lplayer, 0, m_gamedef->ndef());
2321 u8 day = light & 0xff;
2322 u8 night = (light >> 8) & 0xff;
2323 finalColorBlend(lplayer->light_color, day, night, day_night_ratio);
2327 Step active objects and update lighting of them
2330 g_profiler->avg("CEnv: num of objects", m_active_objects.size());
2331 bool update_lighting = m_active_object_light_update_interval.step(dtime, 0.21);
2332 for(std::map<u16, ClientActiveObject*>::iterator
2333 i = m_active_objects.begin();
2334 i != m_active_objects.end(); ++i)
2336 ClientActiveObject* obj = i->second;
2338 obj->step(dtime, this);
2347 v3s16 p = obj->getLightPosition();
2348 MapNode n = m_map->getNodeNoEx(p, &pos_ok);
2350 light = n.getLightBlend(day_night_ratio, m_gamedef->ndef());
2352 light = blend_light(day_night_ratio, LIGHT_SUN, 0);
2354 obj->updateLight(light);
2359 Step and handle simple objects
2361 g_profiler->avg("CEnv: num of simple objects", m_simple_objects.size());
2362 for(std::vector<ClientSimpleObject*>::iterator
2363 i = m_simple_objects.begin(); i != m_simple_objects.end();) {
2364 std::vector<ClientSimpleObject*>::iterator cur = i;
2365 ClientSimpleObject *simple = *cur;
2367 simple->step(dtime);
2368 if(simple->m_to_be_removed) {
2370 i = m_simple_objects.erase(cur);
2378 void ClientEnvironment::addSimpleObject(ClientSimpleObject *simple)
2380 m_simple_objects.push_back(simple);
2383 GenericCAO* ClientEnvironment::getGenericCAO(u16 id)
2385 ClientActiveObject *obj = getActiveObject(id);
2386 if (obj && obj->getType() == ACTIVEOBJECT_TYPE_GENERIC)
2387 return (GenericCAO*) obj;
2392 ClientActiveObject* ClientEnvironment::getActiveObject(u16 id)
2394 std::map<u16, ClientActiveObject*>::iterator n;
2395 n = m_active_objects.find(id);
2396 if(n == m_active_objects.end())
2401 bool isFreeClientActiveObjectId(u16 id,
2402 std::map<u16, ClientActiveObject*> &objects)
2407 return objects.find(id) == objects.end();
2410 u16 getFreeClientActiveObjectId(
2411 std::map<u16, ClientActiveObject*> &objects)
2413 //try to reuse id's as late as possible
2414 static u16 last_used_id = 0;
2415 u16 startid = last_used_id;
2419 if(isFreeClientActiveObjectId(last_used_id, objects))
2420 return last_used_id;
2422 if(last_used_id == startid)
2427 u16 ClientEnvironment::addActiveObject(ClientActiveObject *object)
2429 assert(object); // Pre-condition
2430 if(object->getId() == 0)
2432 u16 new_id = getFreeClientActiveObjectId(m_active_objects);
2435 infostream<<"ClientEnvironment::addActiveObject(): "
2436 <<"no free ids available"<<std::endl;
2440 object->setId(new_id);
2442 if(isFreeClientActiveObjectId(object->getId(), m_active_objects) == false)
2444 infostream<<"ClientEnvironment::addActiveObject(): "
2445 <<"id is not free ("<<object->getId()<<")"<<std::endl;
2449 infostream<<"ClientEnvironment::addActiveObject(): "
2450 <<"added (id="<<object->getId()<<")"<<std::endl;
2451 m_active_objects[object->getId()] = object;
2452 object->addToScene(m_smgr, m_texturesource, m_irr);
2453 { // Update lighting immediately
2458 v3s16 p = object->getLightPosition();
2459 MapNode n = m_map->getNodeNoEx(p, &pos_ok);
2461 light = n.getLightBlend(getDayNightRatio(), m_gamedef->ndef());
2463 light = blend_light(getDayNightRatio(), LIGHT_SUN, 0);
2465 object->updateLight(light);
2467 return object->getId();
2470 void ClientEnvironment::addActiveObject(u16 id, u8 type,
2471 const std::string &init_data)
2473 ClientActiveObject* obj =
2474 ClientActiveObject::create((ActiveObjectType) type, m_gamedef, this);
2477 infostream<<"ClientEnvironment::addActiveObject(): "
2478 <<"id="<<id<<" type="<<type<<": Couldn't create object"
2487 obj->initialize(init_data);
2489 catch(SerializationError &e)
2491 errorstream<<"ClientEnvironment::addActiveObject():"
2492 <<" id="<<id<<" type="<<type
2493 <<": SerializationError in initialize(): "
2495 <<": init_data="<<serializeJsonString(init_data)
2499 addActiveObject(obj);
2502 void ClientEnvironment::removeActiveObject(u16 id)
2504 verbosestream<<"ClientEnvironment::removeActiveObject(): "
2505 <<"id="<<id<<std::endl;
2506 ClientActiveObject* obj = getActiveObject(id);
2509 infostream<<"ClientEnvironment::removeActiveObject(): "
2510 <<"id="<<id<<" not found"<<std::endl;
2513 obj->removeFromScene(true);
2515 m_active_objects.erase(id);
2518 void ClientEnvironment::processActiveObjectMessage(u16 id, const std::string &data)
2520 ClientActiveObject *obj = getActiveObject(id);
2522 infostream << "ClientEnvironment::processActiveObjectMessage():"
2523 << " got message for id=" << id << ", which doesn't exist."
2529 obj->processMessage(data);
2530 } catch (SerializationError &e) {
2531 errorstream<<"ClientEnvironment::processActiveObjectMessage():"
2532 << " id=" << id << " type=" << obj->getType()
2533 << " SerializationError in processMessage(): " << e.what()
2539 Callbacks for activeobjects
2542 void ClientEnvironment::damageLocalPlayer(u8 damage, bool handle_hp)
2544 LocalPlayer *lplayer = getLocalPlayer();
2548 if (lplayer->hp > damage)
2549 lplayer->hp -= damage;
2554 ClientEnvEvent event;
2555 event.type = CEE_PLAYER_DAMAGE;
2556 event.player_damage.amount = damage;
2557 event.player_damage.send_to_server = handle_hp;
2558 m_client_event_queue.push(event);
2561 void ClientEnvironment::updateLocalPlayerBreath(u16 breath)
2563 ClientEnvEvent event;
2564 event.type = CEE_PLAYER_BREATH;
2565 event.player_breath.amount = breath;
2566 m_client_event_queue.push(event);
2570 Client likes to call these
2573 void ClientEnvironment::getActiveObjects(v3f origin, f32 max_d,
2574 std::vector<DistanceSortedActiveObject> &dest)
2576 for(std::map<u16, ClientActiveObject*>::iterator
2577 i = m_active_objects.begin();
2578 i != m_active_objects.end(); ++i)
2580 ClientActiveObject* obj = i->second;
2582 f32 d = (obj->getPosition() - origin).getLength();
2587 DistanceSortedActiveObject dso(obj, d);
2589 dest.push_back(dso);
2593 ClientEnvEvent ClientEnvironment::getClientEvent()
2595 ClientEnvEvent event;
2596 if(m_client_event_queue.empty())
2597 event.type = CEE_NONE;
2599 event = m_client_event_queue.front();
2600 m_client_event_queue.pop();
2605 #endif // #ifndef SERVER