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"
42 #include "daynightratio.h"
45 #include "util/serialize.h"
46 #include "jthread/jmutexautolock.h"
48 #define PP(x) "("<<(x).X<<","<<(x).Y<<","<<(x).Z<<")"
50 Environment::Environment():
52 m_time_of_day_f(9000./24000),
53 m_time_of_day_speed(0),
55 m_enable_day_night_ratio_override(false),
56 m_day_night_ratio_override(0.0f)
58 m_cache_enable_shaders = g_settings->getBool("enable_shaders");
61 Environment::~Environment()
64 for(std::vector<Player*>::iterator i = m_players.begin();
65 i != m_players.end(); ++i) {
70 void Environment::addPlayer(Player *player)
72 DSTACK(__FUNCTION_NAME);
74 Check that peer_ids are unique.
75 Also check that names are unique.
76 Exception: there can be multiple players with peer_id=0
78 // If peer id is non-zero, it has to be unique.
79 if(player->peer_id != 0)
80 FATAL_ERROR_IF(getPlayer(player->peer_id) != NULL, "Peer id not unique");
81 // Name has to be unique.
82 FATAL_ERROR_IF(getPlayer(player->getName()) != NULL, "Player name not unique");
84 m_players.push_back(player);
87 void Environment::removePlayer(u16 peer_id)
89 DSTACK(__FUNCTION_NAME);
91 for(std::vector<Player*>::iterator i = m_players.begin();
92 i != m_players.end();)
95 if(player->peer_id == peer_id) {
97 i = m_players.erase(i);
104 void Environment::removePlayer(const char *name)
106 for (std::vector<Player*>::iterator it = m_players.begin();
107 it != m_players.end(); ++it) {
108 if (strcmp((*it)->getName(), name) == 0) {
116 Player * Environment::getPlayer(u16 peer_id)
118 for(std::vector<Player*>::iterator i = m_players.begin();
119 i != m_players.end(); ++i) {
121 if(player->peer_id == peer_id)
127 Player * Environment::getPlayer(const char *name)
129 for(std::vector<Player*>::iterator i = m_players.begin();
130 i != m_players.end(); ++i) {
132 if(strcmp(player->getName(), name) == 0)
138 Player * Environment::getRandomConnectedPlayer()
140 std::vector<Player*> connected_players = getPlayers(true);
141 u32 chosen_one = myrand() % connected_players.size();
143 for(std::vector<Player*>::iterator
144 i = connected_players.begin();
145 i != connected_players.end(); ++i) {
146 if(j == chosen_one) {
155 Player * Environment::getNearestConnectedPlayer(v3f pos)
157 std::vector<Player*> connected_players = getPlayers(true);
159 Player *nearest_player = NULL;
160 for(std::vector<Player*>::iterator
161 i = connected_players.begin();
162 i != connected_players.end(); ++i) {
164 f32 d = player->getPosition().getDistanceFrom(pos);
165 if(d < nearest_d || nearest_player == NULL) {
167 nearest_player = player;
170 return nearest_player;
173 std::vector<Player*> Environment::getPlayers()
178 std::vector<Player*> Environment::getPlayers(bool ignore_disconnected)
180 std::vector<Player*> newlist;
181 for(std::vector<Player*>::iterator
182 i = m_players.begin();
183 i != m_players.end(); ++i) {
186 if(ignore_disconnected) {
187 // Ignore disconnected players
188 if(player->peer_id == 0)
192 newlist.push_back(player);
197 u32 Environment::getDayNightRatio()
199 if(m_enable_day_night_ratio_override)
200 return m_day_night_ratio_override;
201 return time_to_daynight_ratio(m_time_of_day_f*24000, m_cache_enable_shaders);
204 void Environment::setTimeOfDaySpeed(float speed)
206 JMutexAutoLock(this->m_timeofday_lock);
207 m_time_of_day_speed = speed;
210 float Environment::getTimeOfDaySpeed()
212 JMutexAutoLock(this->m_timeofday_lock);
213 float retval = m_time_of_day_speed;
217 void Environment::setTimeOfDay(u32 time)
219 JMutexAutoLock(this->m_time_lock);
220 m_time_of_day = time;
221 m_time_of_day_f = (float)time / 24000.0;
224 u32 Environment::getTimeOfDay()
226 JMutexAutoLock(this->m_time_lock);
227 u32 retval = m_time_of_day;
231 float Environment::getTimeOfDayF()
233 JMutexAutoLock(this->m_time_lock);
234 float retval = m_time_of_day_f;
238 void Environment::stepTimeOfDay(float dtime)
240 // getTimeOfDaySpeed lock the value we need to prevent MT problems
241 float day_speed = getTimeOfDaySpeed();
243 m_time_counter += dtime;
244 f32 speed = day_speed * 24000./(24.*3600);
245 u32 units = (u32)(m_time_counter*speed);
249 if(m_time_of_day + units >= 24000)
251 m_time_of_day = (m_time_of_day + units) % 24000;
253 m_time_of_day_f = (float)m_time_of_day / 24000.0;
256 m_time_counter -= (f32)units / speed;
259 m_time_of_day_f += day_speed/24/3600*dtime;
260 if(m_time_of_day_f > 1.0)
261 m_time_of_day_f -= 1.0;
262 if(m_time_of_day_f < 0.0)
263 m_time_of_day_f += 1.0;
271 ABMWithState::ABMWithState(ActiveBlockModifier *abm_):
275 // Initialize timer to random value to spread processing
276 float itv = abm->getTriggerInterval();
277 itv = MYMAX(0.001, itv); // No less than 1ms
278 int minval = MYMAX(-0.51*itv, -60); // Clamp to
279 int maxval = MYMIN(0.51*itv, 60); // +-60 seconds
280 timer = myrand_range(minval, maxval);
287 void fillRadiusBlock(v3s16 p0, s16 r, std::set<v3s16> &list)
290 for(p.X=p0.X-r; p.X<=p0.X+r; p.X++)
291 for(p.Y=p0.Y-r; p.Y<=p0.Y+r; p.Y++)
292 for(p.Z=p0.Z-r; p.Z<=p0.Z+r; p.Z++)
299 void ActiveBlockList::update(std::vector<v3s16> &active_positions,
301 std::set<v3s16> &blocks_removed,
302 std::set<v3s16> &blocks_added)
307 std::set<v3s16> newlist = m_forceloaded_list;
308 for(std::vector<v3s16>::iterator i = active_positions.begin();
309 i != active_positions.end(); ++i)
311 fillRadiusBlock(*i, radius, newlist);
315 Find out which blocks on the old list are not on the new list
317 // Go through old list
318 for(std::set<v3s16>::iterator i = m_list.begin();
319 i != m_list.end(); ++i)
322 // If not on new list, it's been removed
323 if(newlist.find(p) == newlist.end())
324 blocks_removed.insert(p);
328 Find out which blocks on the new list are not on the old list
330 // Go through new list
331 for(std::set<v3s16>::iterator i = newlist.begin();
332 i != newlist.end(); ++i)
335 // If not on old list, it's been added
336 if(m_list.find(p) == m_list.end())
337 blocks_added.insert(p);
344 for(std::set<v3s16>::iterator i = newlist.begin();
345 i != newlist.end(); ++i)
356 ServerEnvironment::ServerEnvironment(ServerMap *map,
357 GameScripting *scriptIface, IGameDef *gamedef,
358 const std::string &path_world) :
360 m_script(scriptIface),
362 m_path_world(path_world),
363 m_send_recommended_timer(0),
364 m_active_block_interval_overload_skip(0),
366 m_game_time_fraction_counter(0),
367 m_recommended_send_interval(0.1),
368 m_max_lag_estimate(0.1)
372 ServerEnvironment::~ServerEnvironment()
374 // Clear active block list.
375 // This makes the next one delete all active objects.
376 m_active_blocks.clear();
378 // Convert all objects to static and delete the active objects
379 deactivateFarObjects(true);
384 // Delete ActiveBlockModifiers
385 for(std::vector<ABMWithState>::iterator
386 i = m_abms.begin(); i != m_abms.end(); ++i){
391 Map & ServerEnvironment::getMap()
396 ServerMap & ServerEnvironment::getServerMap()
401 bool ServerEnvironment::line_of_sight(v3f pos1, v3f pos2, float stepsize, v3s16 *p)
403 float distance = pos1.getDistanceFrom(pos2);
405 //calculate normalized direction vector
406 v3f normalized_vector = v3f((pos2.X - pos1.X)/distance,
407 (pos2.Y - pos1.Y)/distance,
408 (pos2.Z - pos1.Z)/distance);
410 //find out if there's a node on path between pos1 and pos2
411 for (float i = 1; i < distance; i += stepsize) {
412 v3s16 pos = floatToInt(v3f(normalized_vector.X * i,
413 normalized_vector.Y * i,
414 normalized_vector.Z * i) +pos1,BS);
416 MapNode n = getMap().getNodeNoEx(pos);
418 if(n.param0 != CONTENT_AIR) {
428 void ServerEnvironment::saveLoadedPlayers()
430 std::string players_path = m_path_world + DIR_DELIM "players";
431 fs::CreateDir(players_path);
433 for (std::vector<Player*>::iterator it = m_players.begin();
434 it != m_players.end();
436 RemotePlayer *player = static_cast<RemotePlayer*>(*it);
437 if (player->checkModified()) {
438 player->save(players_path);
443 void ServerEnvironment::savePlayer(const std::string &playername)
445 std::string players_path = m_path_world + DIR_DELIM "players";
446 fs::CreateDir(players_path);
448 RemotePlayer *player = static_cast<RemotePlayer*>(getPlayer(playername.c_str()));
450 player->save(players_path);
454 Player *ServerEnvironment::loadPlayer(const std::string &playername)
456 bool newplayer = false;
458 std::string players_path = m_path_world + DIR_DELIM "players" DIR_DELIM;
459 std::string path = players_path + playername;
461 RemotePlayer *player = static_cast<RemotePlayer *>(getPlayer(playername.c_str()));
463 player = new RemotePlayer(m_gamedef, "");
467 for (u32 i = 0; i < PLAYER_FILE_ALTERNATE_TRIES; i++) {
468 //// Open file and deserialize
469 std::ifstream is(path.c_str(), std::ios_base::binary);
472 player->deSerialize(is, path);
475 if (player->getName() == playername) {
480 path = players_path + playername + itos(i);
484 infostream << "Player file for player " << playername
485 << " not found" << std::endl;
493 player->setModified(false);
497 void ServerEnvironment::saveMeta()
499 std::string path = m_path_world + DIR_DELIM "env_meta.txt";
501 // Open file and serialize
502 std::ostringstream ss(std::ios_base::binary);
505 args.setU64("game_time", m_game_time);
506 args.setU64("time_of_day", getTimeOfDay());
510 if(!fs::safeWriteToFile(path, ss.str()))
512 infostream<<"ServerEnvironment::saveMeta(): Failed to write "
514 throw SerializationError("Couldn't save env meta");
518 void ServerEnvironment::loadMeta()
520 std::string path = m_path_world + DIR_DELIM "env_meta.txt";
522 // Open file and deserialize
523 std::ifstream is(path.c_str(), std::ios_base::binary);
525 infostream << "ServerEnvironment::loadMeta(): Failed to open "
526 << path << std::endl;
527 throw SerializationError("Couldn't load env meta");
532 if (!args.parseConfigLines(is, "EnvArgsEnd")) {
533 throw SerializationError("ServerEnvironment::loadMeta(): "
534 "EnvArgsEnd not found!");
538 m_game_time = args.getU64("game_time");
539 } catch (SettingNotFoundException &e) {
540 // Getting this is crucial, otherwise timestamps are useless
541 throw SerializationError("Couldn't load env meta game_time");
545 m_time_of_day = args.getU64("time_of_day");
546 } catch (SettingNotFoundException &e) {
547 // This is not as important
548 m_time_of_day = 9000;
554 ActiveBlockModifier *abm;
556 std::set<content_t> required_neighbors;
562 ServerEnvironment *m_env;
563 std::map<content_t, std::vector<ActiveABM> > m_aabms;
565 ABMHandler(std::vector<ABMWithState> &abms,
566 float dtime_s, ServerEnvironment *env,
572 INodeDefManager *ndef = env->getGameDef()->ndef();
573 for(std::vector<ABMWithState>::iterator
574 i = abms.begin(); i != abms.end(); ++i) {
575 ActiveBlockModifier *abm = i->abm;
576 float trigger_interval = abm->getTriggerInterval();
577 if(trigger_interval < 0.001)
578 trigger_interval = 0.001;
579 float actual_interval = dtime_s;
582 if(i->timer < trigger_interval)
584 i->timer -= trigger_interval;
585 actual_interval = trigger_interval;
587 float intervals = actual_interval / trigger_interval;
590 float chance = abm->getTriggerChance();
595 aabm.chance = chance / intervals;
599 std::set<std::string> required_neighbors_s
600 = abm->getRequiredNeighbors();
601 for(std::set<std::string>::iterator
602 i = required_neighbors_s.begin();
603 i != required_neighbors_s.end(); i++)
605 ndef->getIds(*i, aabm.required_neighbors);
608 std::set<std::string> contents_s = abm->getTriggerContents();
609 for(std::set<std::string>::iterator
610 i = contents_s.begin(); i != contents_s.end(); i++)
612 std::set<content_t> ids;
613 ndef->getIds(*i, ids);
614 for(std::set<content_t>::const_iterator k = ids.begin();
618 std::map<content_t, std::vector<ActiveABM> >::iterator j;
620 if(j == m_aabms.end()){
621 std::vector<ActiveABM> aabmlist;
622 m_aabms[c] = aabmlist;
625 j->second.push_back(aabm);
630 // Find out how many objects the given block and its neighbours contain.
631 // Returns the number of objects in the block, and also in 'wider' the
632 // number of objects in the block and all its neighbours. The latter
633 // may an estimate if any neighbours are unloaded.
634 u32 countObjects(MapBlock *block, ServerMap * map, u32 &wider)
637 u32 wider_unknown_count = 0;
638 for(s16 x=-1; x<=1; x++)
639 for(s16 y=-1; y<=1; y++)
640 for(s16 z=-1; z<=1; z++)
642 MapBlock *block2 = map->getBlockNoCreateNoEx(
643 block->getPos() + v3s16(x,y,z));
645 wider_unknown_count++;
648 wider += block2->m_static_objects.m_active.size()
649 + block2->m_static_objects.m_stored.size();
652 u32 active_object_count = block->m_static_objects.m_active.size();
653 u32 wider_known_count = 3*3*3 - wider_unknown_count;
654 wider += wider_unknown_count * wider / wider_known_count;
655 return active_object_count;
658 void apply(MapBlock *block)
663 ServerMap *map = &m_env->getServerMap();
665 u32 active_object_count_wider;
666 u32 active_object_count = this->countObjects(block, map, active_object_count_wider);
667 m_env->m_added_objects = 0;
670 for(p0.X=0; p0.X<MAP_BLOCKSIZE; p0.X++)
671 for(p0.Y=0; p0.Y<MAP_BLOCKSIZE; p0.Y++)
672 for(p0.Z=0; p0.Z<MAP_BLOCKSIZE; p0.Z++)
674 MapNode n = block->getNodeNoEx(p0);
675 content_t c = n.getContent();
676 v3s16 p = p0 + block->getPosRelative();
678 std::map<content_t, std::vector<ActiveABM> >::iterator j;
680 if(j == m_aabms.end())
683 for(std::vector<ActiveABM>::iterator
684 i = j->second.begin(); i != j->second.end(); i++) {
685 if(myrand() % i->chance != 0)
689 if(!i->required_neighbors.empty())
692 for(p1.X = p.X-1; p1.X <= p.X+1; p1.X++)
693 for(p1.Y = p.Y-1; p1.Y <= p.Y+1; p1.Y++)
694 for(p1.Z = p.Z-1; p1.Z <= p.Z+1; p1.Z++)
698 MapNode n = map->getNodeNoEx(p1);
699 content_t c = n.getContent();
700 std::set<content_t>::const_iterator k;
701 k = i->required_neighbors.find(c);
702 if(k != i->required_neighbors.end()){
706 // No required neighbor found
711 // Call all the trigger variations
712 i->abm->trigger(m_env, p, n);
713 i->abm->trigger(m_env, p, n,
714 active_object_count, active_object_count_wider);
716 // Count surrounding objects again if the abms added any
717 if(m_env->m_added_objects > 0) {
718 active_object_count = countObjects(block, map, active_object_count_wider);
719 m_env->m_added_objects = 0;
726 void ServerEnvironment::activateBlock(MapBlock *block, u32 additional_dtime)
728 // Reset usage timer immediately, otherwise a block that becomes active
729 // again at around the same time as it would normally be unloaded will
730 // get unloaded incorrectly. (I think this still leaves a small possibility
731 // of a race condition between this and server::AsyncRunStep, which only
732 // some kind of synchronisation will fix, but it at least reduces the window
733 // of opportunity for it to break from seconds to nanoseconds)
734 block->resetUsageTimer();
736 // Get time difference
738 u32 stamp = block->getTimestamp();
739 if(m_game_time > stamp && stamp != BLOCK_TIMESTAMP_UNDEFINED)
740 dtime_s = m_game_time - block->getTimestamp();
741 dtime_s += additional_dtime;
743 /*infostream<<"ServerEnvironment::activateBlock(): block timestamp: "
744 <<stamp<<", game time: "<<m_game_time<<std::endl;*/
746 // Set current time as timestamp
747 block->setTimestampNoChangedFlag(m_game_time);
749 /*infostream<<"ServerEnvironment::activateBlock(): block is "
750 <<dtime_s<<" seconds old."<<std::endl;*/
752 // Activate stored objects
753 activateObjects(block, dtime_s);
756 std::map<v3s16, NodeTimer> elapsed_timers =
757 block->m_node_timers.step((float)dtime_s);
758 if(!elapsed_timers.empty()){
760 for(std::map<v3s16, NodeTimer>::iterator
761 i = elapsed_timers.begin();
762 i != elapsed_timers.end(); i++){
763 n = block->getNodeNoEx(i->first);
764 v3s16 p = i->first + block->getPosRelative();
765 if(m_script->node_on_timer(p,n,i->second.elapsed))
766 block->setNodeTimer(i->first,NodeTimer(i->second.timeout,0));
770 /* Handle ActiveBlockModifiers */
771 ABMHandler abmhandler(m_abms, dtime_s, this, false);
772 abmhandler.apply(block);
775 void ServerEnvironment::addActiveBlockModifier(ActiveBlockModifier *abm)
777 m_abms.push_back(ABMWithState(abm));
780 bool ServerEnvironment::setNode(v3s16 p, const MapNode &n)
782 INodeDefManager *ndef = m_gamedef->ndef();
783 MapNode n_old = m_map->getNodeNoEx(p);
786 if (ndef->get(n_old).has_on_destruct)
787 m_script->node_on_destruct(p, n_old);
790 if (!m_map->addNodeWithEvent(p, n))
793 // Update active VoxelManipulator if a mapgen thread
794 m_map->updateVManip(p);
796 // Call post-destructor
797 if (ndef->get(n_old).has_after_destruct)
798 m_script->node_after_destruct(p, n_old);
801 if (ndef->get(n).has_on_construct)
802 m_script->node_on_construct(p, n);
807 bool ServerEnvironment::removeNode(v3s16 p)
809 INodeDefManager *ndef = m_gamedef->ndef();
810 MapNode n_old = m_map->getNodeNoEx(p);
813 if (ndef->get(n_old).has_on_destruct)
814 m_script->node_on_destruct(p, n_old);
817 // This is slightly optimized compared to addNodeWithEvent(air)
818 if (!m_map->removeNodeWithEvent(p))
821 // Update active VoxelManipulator if a mapgen thread
822 m_map->updateVManip(p);
824 // Call post-destructor
825 if (ndef->get(n_old).has_after_destruct)
826 m_script->node_after_destruct(p, n_old);
828 // Air doesn't require constructor
832 bool ServerEnvironment::swapNode(v3s16 p, const MapNode &n)
834 if (!m_map->addNodeWithEvent(p, n, false))
837 // Update active VoxelManipulator if a mapgen thread
838 m_map->updateVManip(p);
843 void ServerEnvironment::getObjectsInsideRadius(std::vector<u16> &objects, v3f pos, float radius)
845 for(std::map<u16, ServerActiveObject*>::iterator
846 i = m_active_objects.begin();
847 i != m_active_objects.end(); ++i)
849 ServerActiveObject* obj = i->second;
851 v3f objectpos = obj->getBasePosition();
852 if(objectpos.getDistanceFrom(pos) > radius)
854 objects.push_back(id);
858 void ServerEnvironment::clearAllObjects()
860 infostream<<"ServerEnvironment::clearAllObjects(): "
861 <<"Removing all active objects"<<std::endl;
862 std::vector<u16> objects_to_remove;
863 for(std::map<u16, ServerActiveObject*>::iterator
864 i = m_active_objects.begin();
865 i != m_active_objects.end(); ++i) {
866 ServerActiveObject* obj = i->second;
867 if(obj->getType() == ACTIVEOBJECT_TYPE_PLAYER)
870 // Delete static object if block is loaded
871 if(obj->m_static_exists){
872 MapBlock *block = m_map->getBlockNoCreateNoEx(obj->m_static_block);
874 block->m_static_objects.remove(id);
875 block->raiseModified(MOD_STATE_WRITE_NEEDED,
876 MOD_REASON_CLEAR_ALL_OBJECTS);
877 obj->m_static_exists = false;
880 // If known by some client, don't delete immediately
881 if(obj->m_known_by_count > 0){
882 obj->m_pending_deactivation = true;
883 obj->m_removed = true;
887 // Tell the object about removal
888 obj->removingFromEnvironment();
889 // Deregister in scripting api
890 m_script->removeObjectReference(obj);
892 // Delete active object
893 if(obj->environmentDeletes())
895 // Id to be removed from m_active_objects
896 objects_to_remove.push_back(id);
899 // Remove references from m_active_objects
900 for(std::vector<u16>::iterator i = objects_to_remove.begin();
901 i != objects_to_remove.end(); ++i) {
902 m_active_objects.erase(*i);
905 // Get list of loaded blocks
906 std::vector<v3s16> loaded_blocks;
907 infostream<<"ServerEnvironment::clearAllObjects(): "
908 <<"Listing all loaded blocks"<<std::endl;
909 m_map->listAllLoadedBlocks(loaded_blocks);
910 infostream<<"ServerEnvironment::clearAllObjects(): "
911 <<"Done listing all loaded blocks: "
912 <<loaded_blocks.size()<<std::endl;
914 // Get list of loadable blocks
915 std::vector<v3s16> loadable_blocks;
916 infostream<<"ServerEnvironment::clearAllObjects(): "
917 <<"Listing all loadable blocks"<<std::endl;
918 m_map->listAllLoadableBlocks(loadable_blocks);
919 infostream<<"ServerEnvironment::clearAllObjects(): "
920 <<"Done listing all loadable blocks: "
921 <<loadable_blocks.size()
922 <<", now clearing"<<std::endl;
924 // Grab a reference on each loaded block to avoid unloading it
925 for(std::vector<v3s16>::iterator i = loaded_blocks.begin();
926 i != loaded_blocks.end(); ++i) {
928 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
929 assert(block != NULL);
933 // Remove objects in all loadable blocks
934 u32 unload_interval = g_settings->getS32("max_clearobjects_extra_loaded_blocks");
935 unload_interval = MYMAX(unload_interval, 1);
936 u32 report_interval = loadable_blocks.size() / 10;
937 u32 num_blocks_checked = 0;
938 u32 num_blocks_cleared = 0;
939 u32 num_objs_cleared = 0;
940 for(std::vector<v3s16>::iterator i = loadable_blocks.begin();
941 i != loadable_blocks.end(); ++i) {
943 MapBlock *block = m_map->emergeBlock(p, false);
945 errorstream<<"ServerEnvironment::clearAllObjects(): "
946 <<"Failed to emerge block "<<PP(p)<<std::endl;
949 u32 num_stored = block->m_static_objects.m_stored.size();
950 u32 num_active = block->m_static_objects.m_active.size();
951 if(num_stored != 0 || num_active != 0){
952 block->m_static_objects.m_stored.clear();
953 block->m_static_objects.m_active.clear();
954 block->raiseModified(MOD_STATE_WRITE_NEEDED,
955 MOD_REASON_CLEAR_ALL_OBJECTS);
956 num_objs_cleared += num_stored + num_active;
957 num_blocks_cleared++;
959 num_blocks_checked++;
961 if(report_interval != 0 &&
962 num_blocks_checked % report_interval == 0){
963 float percent = 100.0 * (float)num_blocks_checked /
964 loadable_blocks.size();
965 infostream<<"ServerEnvironment::clearAllObjects(): "
966 <<"Cleared "<<num_objs_cleared<<" objects"
967 <<" in "<<num_blocks_cleared<<" blocks ("
968 <<percent<<"%)"<<std::endl;
970 if(num_blocks_checked % unload_interval == 0){
971 m_map->unloadUnreferencedBlocks();
974 m_map->unloadUnreferencedBlocks();
976 // Drop references that were added above
977 for(std::vector<v3s16>::iterator i = loaded_blocks.begin();
978 i != loaded_blocks.end(); ++i) {
980 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
985 infostream<<"ServerEnvironment::clearAllObjects(): "
986 <<"Finished: Cleared "<<num_objs_cleared<<" objects"
987 <<" in "<<num_blocks_cleared<<" blocks"<<std::endl;
990 void ServerEnvironment::step(float dtime)
992 DSTACK(__FUNCTION_NAME);
994 //TimeTaker timer("ServerEnv step");
996 /* Step time of day */
997 stepTimeOfDay(dtime);
1000 // NOTE: This is kind of funny on a singleplayer game, but doesn't
1001 // really matter that much.
1002 m_recommended_send_interval = g_settings->getFloat("dedicated_server_step");
1008 m_game_time_fraction_counter += dtime;
1009 u32 inc_i = (u32)m_game_time_fraction_counter;
1010 m_game_time += inc_i;
1011 m_game_time_fraction_counter -= (float)inc_i;
1018 ScopeProfiler sp(g_profiler, "SEnv: handle players avg", SPT_AVG);
1019 for(std::vector<Player*>::iterator i = m_players.begin();
1020 i != m_players.end(); ++i)
1022 Player *player = *i;
1024 // Ignore disconnected players
1025 if(player->peer_id == 0)
1029 player->move(dtime, this, 100*BS);
1034 Manage active block list
1036 if(m_active_blocks_management_interval.step(dtime, 2.0))
1038 ScopeProfiler sp(g_profiler, "SEnv: manage act. block list avg /2s", SPT_AVG);
1040 Get player block positions
1042 std::vector<v3s16> players_blockpos;
1043 for(std::vector<Player*>::iterator
1044 i = m_players.begin();
1045 i != m_players.end(); ++i) {
1046 Player *player = *i;
1047 // Ignore disconnected players
1048 if(player->peer_id == 0)
1051 v3s16 blockpos = getNodeBlockPos(
1052 floatToInt(player->getPosition(), BS));
1053 players_blockpos.push_back(blockpos);
1057 Update list of active blocks, collecting changes
1059 const s16 active_block_range = g_settings->getS16("active_block_range");
1060 std::set<v3s16> blocks_removed;
1061 std::set<v3s16> blocks_added;
1062 m_active_blocks.update(players_blockpos, active_block_range,
1063 blocks_removed, blocks_added);
1066 Handle removed blocks
1069 // Convert active objects that are no more in active blocks to static
1070 deactivateFarObjects(false);
1072 for(std::set<v3s16>::iterator
1073 i = blocks_removed.begin();
1074 i != blocks_removed.end(); ++i)
1078 /* infostream<<"Server: Block " << PP(p)
1079 << " became inactive"<<std::endl; */
1081 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1085 // Set current time as timestamp (and let it set ChangedFlag)
1086 block->setTimestamp(m_game_time);
1093 for(std::set<v3s16>::iterator
1094 i = blocks_added.begin();
1095 i != blocks_added.end(); ++i)
1099 MapBlock *block = m_map->getBlockOrEmerge(p);
1101 m_active_blocks.m_list.erase(p);
1105 activateBlock(block);
1106 /* infostream<<"Server: Block " << PP(p)
1107 << " became active"<<std::endl; */
1112 Mess around in active blocks
1114 if(m_active_blocks_nodemetadata_interval.step(dtime, 1.0))
1116 ScopeProfiler sp(g_profiler, "SEnv: mess in act. blocks avg /1s", SPT_AVG);
1120 for(std::set<v3s16>::iterator
1121 i = m_active_blocks.m_list.begin();
1122 i != m_active_blocks.m_list.end(); ++i)
1126 /*infostream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
1127 <<") being handled"<<std::endl;*/
1129 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1133 // Reset block usage timer
1134 block->resetUsageTimer();
1136 // Set current time as timestamp
1137 block->setTimestampNoChangedFlag(m_game_time);
1138 // If time has changed much from the one on disk,
1139 // set block to be saved when it is unloaded
1140 if(block->getTimestamp() > block->getDiskTimestamp() + 60)
1141 block->raiseModified(MOD_STATE_WRITE_AT_UNLOAD,
1142 MOD_REASON_BLOCK_EXPIRED);
1145 std::map<v3s16, NodeTimer> elapsed_timers =
1146 block->m_node_timers.step((float)dtime);
1147 if(!elapsed_timers.empty()){
1149 for(std::map<v3s16, NodeTimer>::iterator
1150 i = elapsed_timers.begin();
1151 i != elapsed_timers.end(); i++){
1152 n = block->getNodeNoEx(i->first);
1153 p = i->first + block->getPosRelative();
1154 if(m_script->node_on_timer(p,n,i->second.elapsed))
1155 block->setNodeTimer(i->first,NodeTimer(i->second.timeout,0));
1161 const float abm_interval = 1.0;
1162 if(m_active_block_modifier_interval.step(dtime, abm_interval))
1164 if(m_active_block_interval_overload_skip > 0){
1165 ScopeProfiler sp(g_profiler, "SEnv: ABM overload skips");
1166 m_active_block_interval_overload_skip--;
1169 ScopeProfiler sp(g_profiler, "SEnv: modify in blocks avg /1s", SPT_AVG);
1170 TimeTaker timer("modify in active blocks");
1172 // Initialize handling of ActiveBlockModifiers
1173 ABMHandler abmhandler(m_abms, abm_interval, this, true);
1175 for(std::set<v3s16>::iterator
1176 i = m_active_blocks.m_list.begin();
1177 i != m_active_blocks.m_list.end(); ++i)
1181 /*infostream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
1182 <<") being handled"<<std::endl;*/
1184 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1188 // Set current time as timestamp
1189 block->setTimestampNoChangedFlag(m_game_time);
1191 /* Handle ActiveBlockModifiers */
1192 abmhandler.apply(block);
1195 u32 time_ms = timer.stop(true);
1196 u32 max_time_ms = 200;
1197 if(time_ms > max_time_ms){
1198 infostream<<"WARNING: active block modifiers took "
1199 <<time_ms<<"ms (longer than "
1200 <<max_time_ms<<"ms)"<<std::endl;
1201 m_active_block_interval_overload_skip = (time_ms / max_time_ms) + 1;
1206 Step script environment (run global on_step())
1208 m_script->environment_Step(dtime);
1214 ScopeProfiler sp(g_profiler, "SEnv: step act. objs avg", SPT_AVG);
1215 //TimeTaker timer("Step active objects");
1217 g_profiler->avg("SEnv: num of objects", m_active_objects.size());
1219 // This helps the objects to send data at the same time
1220 bool send_recommended = false;
1221 m_send_recommended_timer += dtime;
1222 if(m_send_recommended_timer > getSendRecommendedInterval())
1224 m_send_recommended_timer -= getSendRecommendedInterval();
1225 send_recommended = true;
1228 for(std::map<u16, ServerActiveObject*>::iterator
1229 i = m_active_objects.begin();
1230 i != m_active_objects.end(); ++i)
1232 ServerActiveObject* obj = i->second;
1233 // Don't step if is to be removed or stored statically
1234 if(obj->m_removed || obj->m_pending_deactivation)
1237 obj->step(dtime, send_recommended);
1238 // Read messages from object
1239 while(!obj->m_messages_out.empty())
1241 m_active_object_messages.push(
1242 obj->m_messages_out.front());
1243 obj->m_messages_out.pop();
1249 Manage active objects
1251 if(m_object_management_interval.step(dtime, 0.5))
1253 ScopeProfiler sp(g_profiler, "SEnv: remove removed objs avg /.5s", SPT_AVG);
1255 Remove objects that satisfy (m_removed && m_known_by_count==0)
1257 removeRemovedObjects();
1261 ServerActiveObject* ServerEnvironment::getActiveObject(u16 id)
1263 std::map<u16, ServerActiveObject*>::iterator n;
1264 n = m_active_objects.find(id);
1265 if(n == m_active_objects.end())
1270 bool isFreeServerActiveObjectId(u16 id,
1271 std::map<u16, ServerActiveObject*> &objects)
1276 return objects.find(id) == objects.end();
1279 u16 getFreeServerActiveObjectId(
1280 std::map<u16, ServerActiveObject*> &objects)
1282 //try to reuse id's as late as possible
1283 static u16 last_used_id = 0;
1284 u16 startid = last_used_id;
1288 if(isFreeServerActiveObjectId(last_used_id, objects))
1289 return last_used_id;
1291 if(last_used_id == startid)
1296 u16 ServerEnvironment::addActiveObject(ServerActiveObject *object)
1298 assert(object); // Pre-condition
1300 u16 id = addActiveObjectRaw(object, true, 0);
1305 bool ServerEnvironment::addActiveObjectAsStatic(ServerActiveObject *obj)
1309 v3f objectpos = obj->getBasePosition();
1311 // The block in which the object resides in
1312 v3s16 blockpos_o = getNodeBlockPos(floatToInt(objectpos, BS));
1315 Update the static data
1318 // Create new static object
1319 std::string staticdata = obj->getStaticData();
1320 StaticObject s_obj(obj->getType(), objectpos, staticdata);
1321 // Add to the block where the object is located in
1322 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1323 // Get or generate the block
1324 MapBlock *block = m_map->emergeBlock(blockpos);
1326 bool succeeded = false;
1330 block->m_static_objects.insert(0, s_obj);
1331 block->raiseModified(MOD_STATE_WRITE_AT_UNLOAD,
1332 "addActiveObjectAsStatic");
1336 infostream<<"ServerEnvironment::addActiveObjectAsStatic: "
1337 <<"Could not find or generate "
1338 <<"a block for storing static object"<<std::endl;
1342 if(obj->environmentDeletes())
1350 Finds out what new objects have been added to
1351 inside a radius around a position
1353 void ServerEnvironment::getAddedActiveObjects(v3s16 pos, s16 radius,
1355 std::set<u16> ¤t_objects,
1356 std::set<u16> &added_objects)
1358 v3f pos_f = intToFloat(pos, BS);
1359 f32 radius_f = radius * BS;
1360 f32 player_radius_f = player_radius * BS;
1362 if (player_radius_f < 0)
1363 player_radius_f = 0;
1366 Go through the object list,
1367 - discard m_removed objects,
1368 - discard objects that are too far away,
1369 - discard objects that are found in current_objects.
1370 - add remaining objects to added_objects
1372 for(std::map<u16, ServerActiveObject*>::iterator
1373 i = m_active_objects.begin();
1374 i != m_active_objects.end(); ++i)
1378 ServerActiveObject *object = i->second;
1381 // Discard if removed or deactivating
1382 if(object->m_removed || object->m_pending_deactivation)
1385 f32 distance_f = object->getBasePosition().getDistanceFrom(pos_f);
1386 if (object->getType() == ACTIVEOBJECT_TYPE_PLAYER) {
1387 // Discard if too far
1388 if (distance_f > player_radius_f && player_radius_f != 0)
1390 } else if (distance_f > radius_f)
1393 // Discard if already on current_objects
1394 std::set<u16>::iterator n;
1395 n = current_objects.find(id);
1396 if(n != current_objects.end())
1398 // Add to added_objects
1399 added_objects.insert(id);
1404 Finds out what objects have been removed from
1405 inside a radius around a position
1407 void ServerEnvironment::getRemovedActiveObjects(v3s16 pos, s16 radius,
1409 std::set<u16> ¤t_objects,
1410 std::set<u16> &removed_objects)
1412 v3f pos_f = intToFloat(pos, BS);
1413 f32 radius_f = radius * BS;
1414 f32 player_radius_f = player_radius * BS;
1416 if (player_radius_f < 0)
1417 player_radius_f = 0;
1420 Go through current_objects; object is removed if:
1421 - object is not found in m_active_objects (this is actually an
1422 error condition; objects should be set m_removed=true and removed
1423 only after all clients have been informed about removal), or
1424 - object has m_removed=true, or
1425 - object is too far away
1427 for(std::set<u16>::iterator
1428 i = current_objects.begin();
1429 i != current_objects.end(); ++i)
1432 ServerActiveObject *object = getActiveObject(id);
1435 infostream<<"ServerEnvironment::getRemovedActiveObjects():"
1436 <<" object in current_objects is NULL"<<std::endl;
1437 removed_objects.insert(id);
1441 if(object->m_removed || object->m_pending_deactivation)
1443 removed_objects.insert(id);
1447 f32 distance_f = object->getBasePosition().getDistanceFrom(pos_f);
1448 if (object->getType() == ACTIVEOBJECT_TYPE_PLAYER) {
1449 if (distance_f <= player_radius_f || player_radius_f == 0)
1451 } else if (distance_f <= radius_f)
1454 // Object is no longer visible
1455 removed_objects.insert(id);
1459 ActiveObjectMessage ServerEnvironment::getActiveObjectMessage()
1461 if(m_active_object_messages.empty())
1462 return ActiveObjectMessage(0);
1464 ActiveObjectMessage message = m_active_object_messages.front();
1465 m_active_object_messages.pop();
1470 ************ Private methods *************
1473 u16 ServerEnvironment::addActiveObjectRaw(ServerActiveObject *object,
1474 bool set_changed, u32 dtime_s)
1476 assert(object); // Pre-condition
1477 if(object->getId() == 0){
1478 u16 new_id = getFreeServerActiveObjectId(m_active_objects);
1481 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1482 <<"no free ids available"<<std::endl;
1483 if(object->environmentDeletes())
1487 object->setId(new_id);
1490 verbosestream<<"ServerEnvironment::addActiveObjectRaw(): "
1491 <<"supplied with id "<<object->getId()<<std::endl;
1493 if(isFreeServerActiveObjectId(object->getId(), m_active_objects) == false)
1495 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1496 <<"id is not free ("<<object->getId()<<")"<<std::endl;
1497 if(object->environmentDeletes())
1501 /*infostream<<"ServerEnvironment::addActiveObjectRaw(): "
1502 <<"added (id="<<object->getId()<<")"<<std::endl;*/
1504 m_active_objects[object->getId()] = object;
1506 verbosestream<<"ServerEnvironment::addActiveObjectRaw(): "
1507 <<"Added id="<<object->getId()<<"; there are now "
1508 <<m_active_objects.size()<<" active objects."
1511 // Register reference in scripting api (must be done before post-init)
1512 m_script->addObjectReference(object);
1513 // Post-initialize object
1514 object->addedToEnvironment(dtime_s);
1516 // Add static data to block
1517 if(object->isStaticAllowed())
1519 // Add static object to active static list of the block
1520 v3f objectpos = object->getBasePosition();
1521 std::string staticdata = object->getStaticData();
1522 StaticObject s_obj(object->getType(), objectpos, staticdata);
1523 // Add to the block where the object is located in
1524 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1525 MapBlock *block = m_map->emergeBlock(blockpos);
1527 block->m_static_objects.m_active[object->getId()] = s_obj;
1528 object->m_static_exists = true;
1529 object->m_static_block = blockpos;
1532 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1533 MOD_REASON_ADD_ACTIVE_OBJECT_RAW);
1535 v3s16 p = floatToInt(objectpos, BS);
1536 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1537 <<"could not emerge block for storing id="<<object->getId()
1538 <<" statically (pos="<<PP(p)<<")"<<std::endl;
1542 return object->getId();
1546 Remove objects that satisfy (m_removed && m_known_by_count==0)
1548 void ServerEnvironment::removeRemovedObjects()
1550 std::vector<u16> objects_to_remove;
1551 for(std::map<u16, ServerActiveObject*>::iterator
1552 i = m_active_objects.begin();
1553 i != m_active_objects.end(); ++i) {
1555 ServerActiveObject* obj = i->second;
1556 // This shouldn't happen but check it
1559 infostream<<"NULL object found in ServerEnvironment"
1560 <<" while finding removed objects. id="<<id<<std::endl;
1561 // Id to be removed from m_active_objects
1562 objects_to_remove.push_back(id);
1567 We will delete objects that are marked as removed or thatare
1568 waiting for deletion after deactivation
1570 if(obj->m_removed == false && obj->m_pending_deactivation == false)
1574 Delete static data from block if is marked as removed
1576 if(obj->m_static_exists && obj->m_removed)
1578 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
1580 block->m_static_objects.remove(id);
1581 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1582 MOD_REASON_REMOVE_OBJECTS_REMOVE);
1583 obj->m_static_exists = false;
1585 infostream<<"Failed to emerge block from which an object to "
1586 <<"be removed was loaded from. id="<<id<<std::endl;
1590 // If m_known_by_count > 0, don't actually remove. On some future
1591 // invocation this will be 0, which is when removal will continue.
1592 if(obj->m_known_by_count > 0)
1596 Move static data from active to stored if not marked as removed
1598 if(obj->m_static_exists && !obj->m_removed){
1599 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
1601 std::map<u16, StaticObject>::iterator i =
1602 block->m_static_objects.m_active.find(id);
1603 if(i != block->m_static_objects.m_active.end()){
1604 block->m_static_objects.m_stored.push_back(i->second);
1605 block->m_static_objects.m_active.erase(id);
1606 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1607 MOD_REASON_REMOVE_OBJECTS_DEACTIVATE);
1610 infostream<<"Failed to emerge block from which an object to "
1611 <<"be deactivated was loaded from. id="<<id<<std::endl;
1615 // Tell the object about removal
1616 obj->removingFromEnvironment();
1617 // Deregister in scripting api
1618 m_script->removeObjectReference(obj);
1621 if(obj->environmentDeletes())
1624 // Id to be removed from m_active_objects
1625 objects_to_remove.push_back(id);
1627 // Remove references from m_active_objects
1628 for(std::vector<u16>::iterator i = objects_to_remove.begin();
1629 i != objects_to_remove.end(); ++i) {
1630 m_active_objects.erase(*i);
1634 static void print_hexdump(std::ostream &o, const std::string &data)
1636 const int linelength = 16;
1637 for(int l=0; ; l++){
1638 int i0 = linelength * l;
1639 bool at_end = false;
1640 int thislinelength = linelength;
1641 if(i0 + thislinelength > (int)data.size()){
1642 thislinelength = data.size() - i0;
1645 for(int di=0; di<linelength; di++){
1648 if(di<thislinelength)
1649 snprintf(buf, 4, "%.2x ", data[i]);
1651 snprintf(buf, 4, " ");
1655 for(int di=0; di<thislinelength; di++){
1669 Convert stored objects from blocks near the players to active.
1671 void ServerEnvironment::activateObjects(MapBlock *block, u32 dtime_s)
1676 // Ignore if no stored objects (to not set changed flag)
1677 if(block->m_static_objects.m_stored.empty())
1680 verbosestream<<"ServerEnvironment::activateObjects(): "
1681 <<"activating objects of block "<<PP(block->getPos())
1682 <<" ("<<block->m_static_objects.m_stored.size()
1683 <<" objects)"<<std::endl;
1684 bool large_amount = (block->m_static_objects.m_stored.size() > g_settings->getU16("max_objects_per_block"));
1686 errorstream<<"suspiciously large amount of objects detected: "
1687 <<block->m_static_objects.m_stored.size()<<" in "
1688 <<PP(block->getPos())
1689 <<"; removing all of them."<<std::endl;
1690 // Clear stored list
1691 block->m_static_objects.m_stored.clear();
1692 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1693 MOD_REASON_TOO_MANY_OBJECTS);
1697 // Activate stored objects
1698 std::vector<StaticObject> new_stored;
1699 for (std::vector<StaticObject>::iterator
1700 i = block->m_static_objects.m_stored.begin();
1701 i != block->m_static_objects.m_stored.end(); ++i) {
1702 StaticObject &s_obj = *i;
1704 // Create an active object from the data
1705 ServerActiveObject *obj = ServerActiveObject::create
1706 ((ActiveObjectType) s_obj.type, this, 0, s_obj.pos, s_obj.data);
1707 // If couldn't create object, store static data back.
1709 errorstream<<"ServerEnvironment::activateObjects(): "
1710 <<"failed to create active object from static object "
1711 <<"in block "<<PP(s_obj.pos/BS)
1712 <<" type="<<(int)s_obj.type<<" data:"<<std::endl;
1713 print_hexdump(verbosestream, s_obj.data);
1715 new_stored.push_back(s_obj);
1718 verbosestream<<"ServerEnvironment::activateObjects(): "
1719 <<"activated static object pos="<<PP(s_obj.pos/BS)
1720 <<" type="<<(int)s_obj.type<<std::endl;
1721 // This will also add the object to the active static list
1722 addActiveObjectRaw(obj, false, dtime_s);
1724 // Clear stored list
1725 block->m_static_objects.m_stored.clear();
1726 // Add leftover failed stuff to stored list
1727 for(std::vector<StaticObject>::iterator
1728 i = new_stored.begin();
1729 i != new_stored.end(); ++i) {
1730 StaticObject &s_obj = *i;
1731 block->m_static_objects.m_stored.push_back(s_obj);
1734 // Turn the active counterparts of activated objects not pending for
1736 for(std::map<u16, StaticObject>::iterator
1737 i = block->m_static_objects.m_active.begin();
1738 i != block->m_static_objects.m_active.end(); ++i)
1741 ServerActiveObject *object = getActiveObject(id);
1743 object->m_pending_deactivation = false;
1747 Note: Block hasn't really been modified here.
1748 The objects have just been activated and moved from the stored
1749 static list to the active static list.
1750 As such, the block is essentially the same.
1751 Thus, do not call block->raiseModified(MOD_STATE_WRITE_NEEDED).
1752 Otherwise there would be a huge amount of unnecessary I/O.
1757 Convert objects that are not standing inside active blocks to static.
1759 If m_known_by_count != 0, active object is not deleted, but static
1760 data is still updated.
1762 If force_delete is set, active object is deleted nevertheless. It
1763 shall only be set so in the destructor of the environment.
1765 If block wasn't generated (not in memory or on disk),
1767 void ServerEnvironment::deactivateFarObjects(bool force_delete)
1769 std::vector<u16> objects_to_remove;
1770 for(std::map<u16, ServerActiveObject*>::iterator
1771 i = m_active_objects.begin();
1772 i != m_active_objects.end(); ++i) {
1773 ServerActiveObject* obj = i->second;
1776 // Do not deactivate if static data creation not allowed
1777 if(!force_delete && !obj->isStaticAllowed())
1780 // If pending deactivation, let removeRemovedObjects() do it
1781 if(!force_delete && obj->m_pending_deactivation)
1785 v3f objectpos = obj->getBasePosition();
1787 // The block in which the object resides in
1788 v3s16 blockpos_o = getNodeBlockPos(floatToInt(objectpos, BS));
1790 // If object's static data is stored in a deactivated block and object
1791 // is actually located in an active block, re-save to the block in
1792 // which the object is actually located in.
1794 obj->m_static_exists &&
1795 !m_active_blocks.contains(obj->m_static_block) &&
1796 m_active_blocks.contains(blockpos_o))
1798 v3s16 old_static_block = obj->m_static_block;
1800 // Save to block where object is located
1801 MapBlock *block = m_map->emergeBlock(blockpos_o, false);
1803 errorstream<<"ServerEnvironment::deactivateFarObjects(): "
1804 <<"Could not save object id="<<id
1805 <<" to it's current block "<<PP(blockpos_o)
1809 std::string staticdata_new = obj->getStaticData();
1810 StaticObject s_obj(obj->getType(), objectpos, staticdata_new);
1811 block->m_static_objects.insert(id, s_obj);
1812 obj->m_static_block = blockpos_o;
1813 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1814 MOD_REASON_STATIC_DATA_ADDED);
1816 // Delete from block where object was located
1817 block = m_map->emergeBlock(old_static_block, false);
1819 errorstream<<"ServerEnvironment::deactivateFarObjects(): "
1820 <<"Could not delete object id="<<id
1821 <<" from it's previous block "<<PP(old_static_block)
1825 block->m_static_objects.remove(id);
1826 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1827 MOD_REASON_STATIC_DATA_REMOVED);
1831 // If block is active, don't remove
1832 if(!force_delete && m_active_blocks.contains(blockpos_o))
1835 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
1836 <<"deactivating object id="<<id<<" on inactive block "
1837 <<PP(blockpos_o)<<std::endl;
1839 // If known by some client, don't immediately delete.
1840 bool pending_delete = (obj->m_known_by_count > 0 && !force_delete);
1843 Update the static data
1846 if(obj->isStaticAllowed())
1848 // Create new static object
1849 std::string staticdata_new = obj->getStaticData();
1850 StaticObject s_obj(obj->getType(), objectpos, staticdata_new);
1852 bool stays_in_same_block = false;
1853 bool data_changed = true;
1855 if(obj->m_static_exists){
1856 if(obj->m_static_block == blockpos_o)
1857 stays_in_same_block = true;
1859 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
1861 std::map<u16, StaticObject>::iterator n =
1862 block->m_static_objects.m_active.find(id);
1863 if(n != block->m_static_objects.m_active.end()){
1864 StaticObject static_old = n->second;
1866 float save_movem = obj->getMinimumSavedMovement();
1868 if(static_old.data == staticdata_new &&
1869 (static_old.pos - objectpos).getLength() < save_movem)
1870 data_changed = false;
1872 errorstream<<"ServerEnvironment::deactivateFarObjects(): "
1873 <<"id="<<id<<" m_static_exists=true but "
1874 <<"static data doesn't actually exist in "
1875 <<PP(obj->m_static_block)<<std::endl;
1879 bool shall_be_written = (!stays_in_same_block || data_changed);
1881 // Delete old static object
1882 if(obj->m_static_exists)
1884 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
1887 block->m_static_objects.remove(id);
1888 obj->m_static_exists = false;
1889 // Only mark block as modified if data changed considerably
1890 if(shall_be_written)
1891 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1892 MOD_REASON_STATIC_DATA_CHANGED);
1896 // Add to the block where the object is located in
1897 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1898 // Get or generate the block
1899 MapBlock *block = NULL;
1901 block = m_map->emergeBlock(blockpos);
1902 } catch(InvalidPositionException &e){
1903 // Handled via NULL pointer
1904 // NOTE: emergeBlock's failure is usually determined by it
1905 // actually returning NULL
1910 if(block->m_static_objects.m_stored.size() >= g_settings->getU16("max_objects_per_block")){
1911 errorstream<<"ServerEnv: Trying to store id="<<obj->getId()
1912 <<" statically but block "<<PP(blockpos)
1913 <<" already contains "
1914 <<block->m_static_objects.m_stored.size()
1916 <<" Forcing delete."<<std::endl;
1917 force_delete = true;
1919 // If static counterpart already exists in target block,
1921 // This shouldn't happen because the object is removed from
1922 // the previous block before this according to
1923 // obj->m_static_block, but happens rarely for some unknown
1924 // reason. Unsuccessful attempts have been made to find
1926 if(id && block->m_static_objects.m_active.find(id) != block->m_static_objects.m_active.end()){
1927 infostream<<"ServerEnv: WARNING: Performing hack #83274"
1929 block->m_static_objects.remove(id);
1931 // Store static data
1932 u16 store_id = pending_delete ? id : 0;
1933 block->m_static_objects.insert(store_id, s_obj);
1935 // Only mark block as modified if data changed considerably
1936 if(shall_be_written)
1937 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1938 MOD_REASON_STATIC_DATA_CHANGED);
1940 obj->m_static_exists = true;
1941 obj->m_static_block = block->getPos();
1946 v3s16 p = floatToInt(objectpos, BS);
1947 errorstream<<"ServerEnv: Could not find or generate "
1948 <<"a block for storing id="<<obj->getId()
1949 <<" statically (pos="<<PP(p)<<")"<<std::endl;
1956 If known by some client, set pending deactivation.
1957 Otherwise delete it immediately.
1960 if(pending_delete && !force_delete)
1962 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
1963 <<"object id="<<id<<" is known by clients"
1964 <<"; not deleting yet"<<std::endl;
1966 obj->m_pending_deactivation = true;
1970 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
1971 <<"object id="<<id<<" is not known by clients"
1972 <<"; deleting"<<std::endl;
1974 // Tell the object about removal
1975 obj->removingFromEnvironment();
1976 // Deregister in scripting api
1977 m_script->removeObjectReference(obj);
1979 // Delete active object
1980 if(obj->environmentDeletes())
1982 // Id to be removed from m_active_objects
1983 objects_to_remove.push_back(id);
1986 // Remove references from m_active_objects
1987 for(std::vector<u16>::iterator i = objects_to_remove.begin();
1988 i != objects_to_remove.end(); ++i) {
1989 m_active_objects.erase(*i);
1996 #include "clientsimpleobject.h"
2002 ClientEnvironment::ClientEnvironment(ClientMap *map, scene::ISceneManager *smgr,
2003 ITextureSource *texturesource, IGameDef *gamedef,
2004 IrrlichtDevice *irr):
2007 m_texturesource(texturesource),
2012 memset(m_attachements, zero, sizeof(m_attachements));
2015 ClientEnvironment::~ClientEnvironment()
2017 // delete active objects
2018 for(std::map<u16, ClientActiveObject*>::iterator
2019 i = m_active_objects.begin();
2020 i != m_active_objects.end(); ++i)
2025 for(std::vector<ClientSimpleObject*>::iterator
2026 i = m_simple_objects.begin(); i != m_simple_objects.end(); ++i) {
2034 Map & ClientEnvironment::getMap()
2039 ClientMap & ClientEnvironment::getClientMap()
2044 void ClientEnvironment::addPlayer(Player *player)
2046 DSTACK(__FUNCTION_NAME);
2048 It is a failure if player is local and there already is a local
2051 FATAL_ERROR_IF(player->isLocal() == true && getLocalPlayer() != NULL,
2052 "Player is local but there is already a local player");
2054 Environment::addPlayer(player);
2057 LocalPlayer * ClientEnvironment::getLocalPlayer()
2059 for(std::vector<Player*>::iterator i = m_players.begin();
2060 i != m_players.end(); ++i) {
2061 Player *player = *i;
2062 if(player->isLocal())
2063 return (LocalPlayer*)player;
2068 void ClientEnvironment::step(float dtime)
2070 DSTACK(__FUNCTION_NAME);
2072 /* Step time of day */
2073 stepTimeOfDay(dtime);
2075 // Get some settings
2076 bool fly_allowed = m_gamedef->checkLocalPrivilege("fly");
2077 bool free_move = fly_allowed && g_settings->getBool("free_move");
2080 LocalPlayer *lplayer = getLocalPlayer();
2082 // collision info queue
2083 std::vector<CollisionInfo> player_collisions;
2086 Get the speed the player is going
2088 bool is_climbing = lplayer->is_climbing;
2090 f32 player_speed = lplayer->getSpeed().getLength();
2093 Maximum position increment
2095 //f32 position_max_increment = 0.05*BS;
2096 f32 position_max_increment = 0.1*BS;
2098 // Maximum time increment (for collision detection etc)
2099 // time = distance / speed
2100 f32 dtime_max_increment = 1;
2101 if(player_speed > 0.001)
2102 dtime_max_increment = position_max_increment / player_speed;
2104 // Maximum time increment is 10ms or lower
2105 if(dtime_max_increment > 0.01)
2106 dtime_max_increment = 0.01;
2108 // Don't allow overly huge dtime
2112 f32 dtime_downcount = dtime;
2115 Stuff that has a maximum time increment
2124 if(dtime_downcount > dtime_max_increment)
2126 dtime_part = dtime_max_increment;
2127 dtime_downcount -= dtime_part;
2131 dtime_part = dtime_downcount;
2133 Setting this to 0 (no -=dtime_part) disables an infinite loop
2134 when dtime_part is so small that dtime_downcount -= dtime_part
2137 dtime_downcount = 0;
2146 if(free_move == false && is_climbing == false)
2149 v3f speed = lplayer->getSpeed();
2150 if(lplayer->in_liquid == false)
2151 speed.Y -= lplayer->movement_gravity * lplayer->physics_override_gravity * dtime_part * 2;
2153 // Liquid floating / sinking
2154 if(lplayer->in_liquid && !lplayer->swimming_vertical)
2155 speed.Y -= lplayer->movement_liquid_sink * dtime_part * 2;
2157 // Liquid resistance
2158 if(lplayer->in_liquid_stable || lplayer->in_liquid)
2160 // How much the node's viscosity blocks movement, ranges between 0 and 1
2161 // Should match the scale at which viscosity increase affects other liquid attributes
2162 const f32 viscosity_factor = 0.3;
2164 v3f d_wanted = -speed / lplayer->movement_liquid_fluidity;
2165 f32 dl = d_wanted.getLength();
2166 if(dl > lplayer->movement_liquid_fluidity_smooth)
2167 dl = lplayer->movement_liquid_fluidity_smooth;
2168 dl *= (lplayer->liquid_viscosity * viscosity_factor) + (1 - viscosity_factor);
2170 v3f d = d_wanted.normalize() * dl;
2174 if(speed.X > lplayer->movement_liquid_fluidity + lplayer->movement_liquid_fluidity_smooth) speed.X -= lplayer->movement_liquid_fluidity_smooth;
2175 if(speed.X < -lplayer->movement_liquid_fluidity - lplayer->movement_liquid_fluidity_smooth) speed.X += lplayer->movement_liquid_fluidity_smooth;
2176 if(speed.Y > lplayer->movement_liquid_fluidity + lplayer->movement_liquid_fluidity_smooth) speed.Y -= lplayer->movement_liquid_fluidity_smooth;
2177 if(speed.Y < -lplayer->movement_liquid_fluidity - lplayer->movement_liquid_fluidity_smooth) speed.Y += lplayer->movement_liquid_fluidity_smooth;
2178 if(speed.Z > lplayer->movement_liquid_fluidity + lplayer->movement_liquid_fluidity_smooth) speed.Z -= lplayer->movement_liquid_fluidity_smooth;
2179 if(speed.Z < -lplayer->movement_liquid_fluidity - lplayer->movement_liquid_fluidity_smooth) speed.Z += lplayer->movement_liquid_fluidity_smooth;
2183 lplayer->setSpeed(speed);
2188 This also does collision detection.
2190 lplayer->move(dtime_part, this, position_max_increment,
2191 &player_collisions);
2194 while(dtime_downcount > 0.001);
2196 //std::cout<<"Looped "<<loopcount<<" times."<<std::endl;
2198 for(std::vector<CollisionInfo>::iterator i = player_collisions.begin();
2199 i != player_collisions.end(); ++i) {
2200 CollisionInfo &info = *i;
2201 v3f speed_diff = info.new_speed - info.old_speed;;
2202 // Handle only fall damage
2203 // (because otherwise walking against something in fast_move kills you)
2204 if(speed_diff.Y < 0 || info.old_speed.Y >= 0)
2206 // Get rid of other components
2209 f32 pre_factor = 1; // 1 hp per node/s
2210 f32 tolerance = BS*14; // 5 without damage
2211 f32 post_factor = 1; // 1 hp per node/s
2212 if(info.type == COLLISION_NODE)
2214 const ContentFeatures &f = m_gamedef->ndef()->
2215 get(m_map->getNodeNoEx(info.node_p));
2216 // Determine fall damage multiplier
2217 int addp = itemgroup_get(f.groups, "fall_damage_add_percent");
2218 pre_factor = 1.0 + (float)addp/100.0;
2220 float speed = pre_factor * speed_diff.getLength();
2221 if(speed > tolerance)
2223 f32 damage_f = (speed - tolerance)/BS * post_factor;
2224 u16 damage = (u16)(damage_f+0.5);
2226 damageLocalPlayer(damage, true);
2227 MtEvent *e = new SimpleTriggerEvent("PlayerFallingDamage");
2228 m_gamedef->event()->put(e);
2234 A quick draft of lava damage
2236 if(m_lava_hurt_interval.step(dtime, 1.0))
2238 v3f pf = lplayer->getPosition();
2240 // Feet, middle and head
2241 v3s16 p1 = floatToInt(pf + v3f(0, BS*0.1, 0), BS);
2242 MapNode n1 = m_map->getNodeNoEx(p1);
2243 v3s16 p2 = floatToInt(pf + v3f(0, BS*0.8, 0), BS);
2244 MapNode n2 = m_map->getNodeNoEx(p2);
2245 v3s16 p3 = floatToInt(pf + v3f(0, BS*1.6, 0), BS);
2246 MapNode n3 = m_map->getNodeNoEx(p3);
2248 u32 damage_per_second = 0;
2249 damage_per_second = MYMAX(damage_per_second,
2250 m_gamedef->ndef()->get(n1).damage_per_second);
2251 damage_per_second = MYMAX(damage_per_second,
2252 m_gamedef->ndef()->get(n2).damage_per_second);
2253 damage_per_second = MYMAX(damage_per_second,
2254 m_gamedef->ndef()->get(n3).damage_per_second);
2256 if(damage_per_second != 0)
2258 damageLocalPlayer(damage_per_second, true);
2265 if(m_drowning_interval.step(dtime, 2.0))
2267 v3f pf = lplayer->getPosition();
2270 v3s16 p = floatToInt(pf + v3f(0, BS*1.6, 0), BS);
2271 MapNode n = m_map->getNodeNoEx(p);
2272 ContentFeatures c = m_gamedef->ndef()->get(n);
2273 u8 drowning_damage = c.drowning;
2274 if(drowning_damage > 0 && lplayer->hp > 0){
2275 u16 breath = lplayer->getBreath();
2282 lplayer->setBreath(breath);
2283 updateLocalPlayerBreath(breath);
2286 if(lplayer->getBreath() == 0 && drowning_damage > 0){
2287 damageLocalPlayer(drowning_damage, true);
2290 if(m_breathing_interval.step(dtime, 0.5))
2292 v3f pf = lplayer->getPosition();
2295 v3s16 p = floatToInt(pf + v3f(0, BS*1.6, 0), BS);
2296 MapNode n = m_map->getNodeNoEx(p);
2297 ContentFeatures c = m_gamedef->ndef()->get(n);
2299 lplayer->setBreath(11);
2301 else if(c.drowning == 0){
2302 u16 breath = lplayer->getBreath();
2305 lplayer->setBreath(breath);
2306 updateLocalPlayerBreath(breath);
2312 Stuff that can be done in an arbitarily large dtime
2314 for(std::vector<Player*>::iterator i = m_players.begin();
2315 i != m_players.end(); ++i) {
2316 Player *player = *i;
2319 Handle non-local players
2321 if(player->isLocal() == false) {
2323 player->move(dtime, this, 100*BS);
2328 // Update lighting on local player (used for wield item)
2329 u32 day_night_ratio = getDayNightRatio();
2333 // On InvalidPositionException, use this as default
2334 // (day: LIGHT_SUN, night: 0)
2335 MapNode node_at_lplayer(CONTENT_AIR, 0x0f, 0);
2337 v3s16 p = lplayer->getLightPosition();
2338 node_at_lplayer = m_map->getNodeNoEx(p);
2340 u16 light = getInteriorLight(node_at_lplayer, 0, m_gamedef->ndef());
2341 u8 day = light & 0xff;
2342 u8 night = (light >> 8) & 0xff;
2343 finalColorBlend(lplayer->light_color, day, night, day_night_ratio);
2347 Step active objects and update lighting of them
2350 g_profiler->avg("CEnv: num of objects", m_active_objects.size());
2351 bool update_lighting = m_active_object_light_update_interval.step(dtime, 0.21);
2352 for(std::map<u16, ClientActiveObject*>::iterator
2353 i = m_active_objects.begin();
2354 i != m_active_objects.end(); ++i)
2356 ClientActiveObject* obj = i->second;
2358 obj->step(dtime, this);
2367 v3s16 p = obj->getLightPosition();
2368 MapNode n = m_map->getNodeNoEx(p, &pos_ok);
2370 light = n.getLightBlend(day_night_ratio, m_gamedef->ndef());
2372 light = blend_light(day_night_ratio, LIGHT_SUN, 0);
2374 obj->updateLight(light);
2379 Step and handle simple objects
2381 g_profiler->avg("CEnv: num of simple objects", m_simple_objects.size());
2382 for(std::vector<ClientSimpleObject*>::iterator
2383 i = m_simple_objects.begin(); i != m_simple_objects.end();) {
2384 std::vector<ClientSimpleObject*>::iterator cur = i;
2385 ClientSimpleObject *simple = *cur;
2387 simple->step(dtime);
2388 if(simple->m_to_be_removed) {
2390 i = m_simple_objects.erase(cur);
2398 void ClientEnvironment::addSimpleObject(ClientSimpleObject *simple)
2400 m_simple_objects.push_back(simple);
2403 ClientActiveObject* ClientEnvironment::getActiveObject(u16 id)
2405 std::map<u16, ClientActiveObject*>::iterator n;
2406 n = m_active_objects.find(id);
2407 if(n == m_active_objects.end())
2412 bool isFreeClientActiveObjectId(u16 id,
2413 std::map<u16, ClientActiveObject*> &objects)
2418 return objects.find(id) == objects.end();
2421 u16 getFreeClientActiveObjectId(
2422 std::map<u16, ClientActiveObject*> &objects)
2424 //try to reuse id's as late as possible
2425 static u16 last_used_id = 0;
2426 u16 startid = last_used_id;
2430 if(isFreeClientActiveObjectId(last_used_id, objects))
2431 return last_used_id;
2433 if(last_used_id == startid)
2438 u16 ClientEnvironment::addActiveObject(ClientActiveObject *object)
2440 assert(object); // Pre-condition
2441 if(object->getId() == 0)
2443 u16 new_id = getFreeClientActiveObjectId(m_active_objects);
2446 infostream<<"ClientEnvironment::addActiveObject(): "
2447 <<"no free ids available"<<std::endl;
2451 object->setId(new_id);
2453 if(isFreeClientActiveObjectId(object->getId(), m_active_objects) == false)
2455 infostream<<"ClientEnvironment::addActiveObject(): "
2456 <<"id is not free ("<<object->getId()<<")"<<std::endl;
2460 infostream<<"ClientEnvironment::addActiveObject(): "
2461 <<"added (id="<<object->getId()<<")"<<std::endl;
2462 m_active_objects[object->getId()] = object;
2463 object->addToScene(m_smgr, m_texturesource, m_irr);
2464 { // Update lighting immediately
2469 v3s16 p = object->getLightPosition();
2470 MapNode n = m_map->getNodeNoEx(p, &pos_ok);
2472 light = n.getLightBlend(getDayNightRatio(), m_gamedef->ndef());
2474 light = blend_light(getDayNightRatio(), LIGHT_SUN, 0);
2476 object->updateLight(light);
2478 return object->getId();
2481 void ClientEnvironment::addActiveObject(u16 id, u8 type,
2482 const std::string &init_data)
2484 ClientActiveObject* obj =
2485 ClientActiveObject::create((ActiveObjectType) type, m_gamedef, this);
2488 infostream<<"ClientEnvironment::addActiveObject(): "
2489 <<"id="<<id<<" type="<<type<<": Couldn't create object"
2498 obj->initialize(init_data);
2500 catch(SerializationError &e)
2502 errorstream<<"ClientEnvironment::addActiveObject():"
2503 <<" id="<<id<<" type="<<type
2504 <<": SerializationError in initialize(): "
2506 <<": init_data="<<serializeJsonString(init_data)
2510 addActiveObject(obj);
2513 void ClientEnvironment::removeActiveObject(u16 id)
2515 verbosestream<<"ClientEnvironment::removeActiveObject(): "
2516 <<"id="<<id<<std::endl;
2517 ClientActiveObject* obj = getActiveObject(id);
2520 infostream<<"ClientEnvironment::removeActiveObject(): "
2521 <<"id="<<id<<" not found"<<std::endl;
2524 obj->removeFromScene(true);
2526 m_active_objects.erase(id);
2529 void ClientEnvironment::processActiveObjectMessage(u16 id,
2530 const std::string &data)
2532 ClientActiveObject* obj = getActiveObject(id);
2535 infostream<<"ClientEnvironment::processActiveObjectMessage():"
2536 <<" got message for id="<<id<<", which doesn't exist."
2542 obj->processMessage(data);
2544 catch(SerializationError &e)
2546 errorstream<<"ClientEnvironment::processActiveObjectMessage():"
2547 <<" id="<<id<<" type="<<obj->getType()
2548 <<" SerializationError in processMessage(),"
2549 <<" message="<<serializeJsonString(data)
2555 Callbacks for activeobjects
2558 void ClientEnvironment::damageLocalPlayer(u8 damage, bool handle_hp)
2560 LocalPlayer *lplayer = getLocalPlayer();
2564 if (lplayer->hp > damage)
2565 lplayer->hp -= damage;
2570 ClientEnvEvent event;
2571 event.type = CEE_PLAYER_DAMAGE;
2572 event.player_damage.amount = damage;
2573 event.player_damage.send_to_server = handle_hp;
2574 m_client_event_queue.push_back(event);
2577 void ClientEnvironment::updateLocalPlayerBreath(u16 breath)
2579 ClientEnvEvent event;
2580 event.type = CEE_PLAYER_BREATH;
2581 event.player_breath.amount = breath;
2582 m_client_event_queue.push_back(event);
2586 Client likes to call these
2589 void ClientEnvironment::getActiveObjects(v3f origin, f32 max_d,
2590 std::vector<DistanceSortedActiveObject> &dest)
2592 for(std::map<u16, ClientActiveObject*>::iterator
2593 i = m_active_objects.begin();
2594 i != m_active_objects.end(); ++i)
2596 ClientActiveObject* obj = i->second;
2598 f32 d = (obj->getPosition() - origin).getLength();
2603 DistanceSortedActiveObject dso(obj, d);
2605 dest.push_back(dso);
2609 ClientEnvEvent ClientEnvironment::getClientEvent()
2611 ClientEnvEvent event;
2612 if(m_client_event_queue.empty())
2613 event.type = CEE_NONE;
2615 event = m_client_event_queue.front();
2616 m_client_event_queue.pop_front();
2621 #endif // #ifndef SERVER