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 std::set<u16> ServerEnvironment::getObjectsInsideRadius(v3f pos, float radius)
845 std::set<u16> objects;
846 for(std::map<u16, ServerActiveObject*>::iterator
847 i = m_active_objects.begin();
848 i != m_active_objects.end(); ++i)
850 ServerActiveObject* obj = i->second;
852 v3f objectpos = obj->getBasePosition();
853 if(objectpos.getDistanceFrom(pos) > radius)
860 void ServerEnvironment::clearAllObjects()
862 infostream<<"ServerEnvironment::clearAllObjects(): "
863 <<"Removing all active objects"<<std::endl;
864 std::vector<u16> objects_to_remove;
865 for(std::map<u16, ServerActiveObject*>::iterator
866 i = m_active_objects.begin();
867 i != m_active_objects.end(); ++i) {
868 ServerActiveObject* obj = i->second;
869 if(obj->getType() == ACTIVEOBJECT_TYPE_PLAYER)
872 // Delete static object if block is loaded
873 if(obj->m_static_exists){
874 MapBlock *block = m_map->getBlockNoCreateNoEx(obj->m_static_block);
876 block->m_static_objects.remove(id);
877 block->raiseModified(MOD_STATE_WRITE_NEEDED,
879 obj->m_static_exists = false;
882 // If known by some client, don't delete immediately
883 if(obj->m_known_by_count > 0){
884 obj->m_pending_deactivation = true;
885 obj->m_removed = true;
889 // Tell the object about removal
890 obj->removingFromEnvironment();
891 // Deregister in scripting api
892 m_script->removeObjectReference(obj);
894 // Delete active object
895 if(obj->environmentDeletes())
897 // Id to be removed from m_active_objects
898 objects_to_remove.push_back(id);
901 // Remove references from m_active_objects
902 for(std::vector<u16>::iterator i = objects_to_remove.begin();
903 i != objects_to_remove.end(); ++i) {
904 m_active_objects.erase(*i);
907 // Get list of loaded blocks
908 std::vector<v3s16> loaded_blocks;
909 infostream<<"ServerEnvironment::clearAllObjects(): "
910 <<"Listing all loaded blocks"<<std::endl;
911 m_map->listAllLoadedBlocks(loaded_blocks);
912 infostream<<"ServerEnvironment::clearAllObjects(): "
913 <<"Done listing all loaded blocks: "
914 <<loaded_blocks.size()<<std::endl;
916 // Get list of loadable blocks
917 std::vector<v3s16> loadable_blocks;
918 infostream<<"ServerEnvironment::clearAllObjects(): "
919 <<"Listing all loadable blocks"<<std::endl;
920 m_map->listAllLoadableBlocks(loadable_blocks);
921 infostream<<"ServerEnvironment::clearAllObjects(): "
922 <<"Done listing all loadable blocks: "
923 <<loadable_blocks.size()
924 <<", now clearing"<<std::endl;
926 // Grab a reference on each loaded block to avoid unloading it
927 for(std::vector<v3s16>::iterator i = loaded_blocks.begin();
928 i != loaded_blocks.end(); ++i) {
930 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
931 assert(block != NULL);
935 // Remove objects in all loadable blocks
936 u32 unload_interval = g_settings->getS32("max_clearobjects_extra_loaded_blocks");
937 unload_interval = MYMAX(unload_interval, 1);
938 u32 report_interval = loadable_blocks.size() / 10;
939 u32 num_blocks_checked = 0;
940 u32 num_blocks_cleared = 0;
941 u32 num_objs_cleared = 0;
942 for(std::vector<v3s16>::iterator i = loadable_blocks.begin();
943 i != loadable_blocks.end(); ++i) {
945 MapBlock *block = m_map->emergeBlock(p, false);
947 errorstream<<"ServerEnvironment::clearAllObjects(): "
948 <<"Failed to emerge block "<<PP(p)<<std::endl;
951 u32 num_stored = block->m_static_objects.m_stored.size();
952 u32 num_active = block->m_static_objects.m_active.size();
953 if(num_stored != 0 || num_active != 0){
954 block->m_static_objects.m_stored.clear();
955 block->m_static_objects.m_active.clear();
956 block->raiseModified(MOD_STATE_WRITE_NEEDED,
958 num_objs_cleared += num_stored + num_active;
959 num_blocks_cleared++;
961 num_blocks_checked++;
963 if(report_interval != 0 &&
964 num_blocks_checked % report_interval == 0){
965 float percent = 100.0 * (float)num_blocks_checked /
966 loadable_blocks.size();
967 infostream<<"ServerEnvironment::clearAllObjects(): "
968 <<"Cleared "<<num_objs_cleared<<" objects"
969 <<" in "<<num_blocks_cleared<<" blocks ("
970 <<percent<<"%)"<<std::endl;
972 if(num_blocks_checked % unload_interval == 0){
973 m_map->unloadUnreferencedBlocks();
976 m_map->unloadUnreferencedBlocks();
978 // Drop references that were added above
979 for(std::vector<v3s16>::iterator i = loaded_blocks.begin();
980 i != loaded_blocks.end(); ++i) {
982 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
987 infostream<<"ServerEnvironment::clearAllObjects(): "
988 <<"Finished: Cleared "<<num_objs_cleared<<" objects"
989 <<" in "<<num_blocks_cleared<<" blocks"<<std::endl;
992 void ServerEnvironment::step(float dtime)
994 DSTACK(__FUNCTION_NAME);
996 //TimeTaker timer("ServerEnv step");
998 /* Step time of day */
999 stepTimeOfDay(dtime);
1002 // NOTE: This is kind of funny on a singleplayer game, but doesn't
1003 // really matter that much.
1004 m_recommended_send_interval = g_settings->getFloat("dedicated_server_step");
1010 m_game_time_fraction_counter += dtime;
1011 u32 inc_i = (u32)m_game_time_fraction_counter;
1012 m_game_time += inc_i;
1013 m_game_time_fraction_counter -= (float)inc_i;
1020 ScopeProfiler sp(g_profiler, "SEnv: handle players avg", SPT_AVG);
1021 for(std::vector<Player*>::iterator i = m_players.begin();
1022 i != m_players.end(); ++i)
1024 Player *player = *i;
1026 // Ignore disconnected players
1027 if(player->peer_id == 0)
1031 player->move(dtime, this, 100*BS);
1036 Manage active block list
1038 if(m_active_blocks_management_interval.step(dtime, 2.0))
1040 ScopeProfiler sp(g_profiler, "SEnv: manage act. block list avg /2s", SPT_AVG);
1042 Get player block positions
1044 std::vector<v3s16> players_blockpos;
1045 for(std::vector<Player*>::iterator
1046 i = m_players.begin();
1047 i != m_players.end(); ++i) {
1048 Player *player = *i;
1049 // Ignore disconnected players
1050 if(player->peer_id == 0)
1053 v3s16 blockpos = getNodeBlockPos(
1054 floatToInt(player->getPosition(), BS));
1055 players_blockpos.push_back(blockpos);
1059 Update list of active blocks, collecting changes
1061 const s16 active_block_range = g_settings->getS16("active_block_range");
1062 std::set<v3s16> blocks_removed;
1063 std::set<v3s16> blocks_added;
1064 m_active_blocks.update(players_blockpos, active_block_range,
1065 blocks_removed, blocks_added);
1068 Handle removed blocks
1071 // Convert active objects that are no more in active blocks to static
1072 deactivateFarObjects(false);
1074 for(std::set<v3s16>::iterator
1075 i = blocks_removed.begin();
1076 i != blocks_removed.end(); ++i)
1080 /* infostream<<"Server: Block " << PP(p)
1081 << " became inactive"<<std::endl; */
1083 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1087 // Set current time as timestamp (and let it set ChangedFlag)
1088 block->setTimestamp(m_game_time);
1095 for(std::set<v3s16>::iterator
1096 i = blocks_added.begin();
1097 i != blocks_added.end(); ++i)
1101 MapBlock *block = m_map->getBlockOrEmerge(p);
1103 m_active_blocks.m_list.erase(p);
1107 activateBlock(block);
1108 /* infostream<<"Server: Block " << PP(p)
1109 << " became active"<<std::endl; */
1114 Mess around in active blocks
1116 if(m_active_blocks_nodemetadata_interval.step(dtime, 1.0))
1118 ScopeProfiler sp(g_profiler, "SEnv: mess in act. blocks avg /1s", SPT_AVG);
1122 for(std::set<v3s16>::iterator
1123 i = m_active_blocks.m_list.begin();
1124 i != m_active_blocks.m_list.end(); ++i)
1128 /*infostream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
1129 <<") being handled"<<std::endl;*/
1131 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1135 // Reset block usage timer
1136 block->resetUsageTimer();
1138 // Set current time as timestamp
1139 block->setTimestampNoChangedFlag(m_game_time);
1140 // If time has changed much from the one on disk,
1141 // set block to be saved when it is unloaded
1142 if(block->getTimestamp() > block->getDiskTimestamp() + 60)
1143 block->raiseModified(MOD_STATE_WRITE_AT_UNLOAD,
1144 "Timestamp older than 60s (step)");
1147 std::map<v3s16, NodeTimer> elapsed_timers =
1148 block->m_node_timers.step((float)dtime);
1149 if(!elapsed_timers.empty()){
1151 for(std::map<v3s16, NodeTimer>::iterator
1152 i = elapsed_timers.begin();
1153 i != elapsed_timers.end(); i++){
1154 n = block->getNodeNoEx(i->first);
1155 p = i->first + block->getPosRelative();
1156 if(m_script->node_on_timer(p,n,i->second.elapsed))
1157 block->setNodeTimer(i->first,NodeTimer(i->second.timeout,0));
1163 const float abm_interval = 1.0;
1164 if(m_active_block_modifier_interval.step(dtime, abm_interval))
1166 if(m_active_block_interval_overload_skip > 0){
1167 ScopeProfiler sp(g_profiler, "SEnv: ABM overload skips");
1168 m_active_block_interval_overload_skip--;
1171 ScopeProfiler sp(g_profiler, "SEnv: modify in blocks avg /1s", SPT_AVG);
1172 TimeTaker timer("modify in active blocks");
1174 // Initialize handling of ActiveBlockModifiers
1175 ABMHandler abmhandler(m_abms, abm_interval, this, true);
1177 for(std::set<v3s16>::iterator
1178 i = m_active_blocks.m_list.begin();
1179 i != m_active_blocks.m_list.end(); ++i)
1183 /*infostream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
1184 <<") being handled"<<std::endl;*/
1186 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1190 // Set current time as timestamp
1191 block->setTimestampNoChangedFlag(m_game_time);
1193 /* Handle ActiveBlockModifiers */
1194 abmhandler.apply(block);
1197 u32 time_ms = timer.stop(true);
1198 u32 max_time_ms = 200;
1199 if(time_ms > max_time_ms){
1200 infostream<<"WARNING: active block modifiers took "
1201 <<time_ms<<"ms (longer than "
1202 <<max_time_ms<<"ms)"<<std::endl;
1203 m_active_block_interval_overload_skip = (time_ms / max_time_ms) + 1;
1208 Step script environment (run global on_step())
1210 m_script->environment_Step(dtime);
1216 ScopeProfiler sp(g_profiler, "SEnv: step act. objs avg", SPT_AVG);
1217 //TimeTaker timer("Step active objects");
1219 g_profiler->avg("SEnv: num of objects", m_active_objects.size());
1221 // This helps the objects to send data at the same time
1222 bool send_recommended = false;
1223 m_send_recommended_timer += dtime;
1224 if(m_send_recommended_timer > getSendRecommendedInterval())
1226 m_send_recommended_timer -= getSendRecommendedInterval();
1227 send_recommended = true;
1230 for(std::map<u16, ServerActiveObject*>::iterator
1231 i = m_active_objects.begin();
1232 i != m_active_objects.end(); ++i)
1234 ServerActiveObject* obj = i->second;
1235 // Don't step if is to be removed or stored statically
1236 if(obj->m_removed || obj->m_pending_deactivation)
1239 obj->step(dtime, send_recommended);
1240 // Read messages from object
1241 while(!obj->m_messages_out.empty())
1243 m_active_object_messages.push_back(
1244 obj->m_messages_out.front());
1245 obj->m_messages_out.pop();
1251 Manage active objects
1253 if(m_object_management_interval.step(dtime, 0.5))
1255 ScopeProfiler sp(g_profiler, "SEnv: remove removed objs avg /.5s", SPT_AVG);
1257 Remove objects that satisfy (m_removed && m_known_by_count==0)
1259 removeRemovedObjects();
1263 ServerActiveObject* ServerEnvironment::getActiveObject(u16 id)
1265 std::map<u16, ServerActiveObject*>::iterator n;
1266 n = m_active_objects.find(id);
1267 if(n == m_active_objects.end())
1272 bool isFreeServerActiveObjectId(u16 id,
1273 std::map<u16, ServerActiveObject*> &objects)
1278 return objects.find(id) == objects.end();
1281 u16 getFreeServerActiveObjectId(
1282 std::map<u16, ServerActiveObject*> &objects)
1284 //try to reuse id's as late as possible
1285 static u16 last_used_id = 0;
1286 u16 startid = last_used_id;
1290 if(isFreeServerActiveObjectId(last_used_id, objects))
1291 return last_used_id;
1293 if(last_used_id == startid)
1298 u16 ServerEnvironment::addActiveObject(ServerActiveObject *object)
1300 assert(object); // Pre-condition
1302 u16 id = addActiveObjectRaw(object, true, 0);
1307 bool ServerEnvironment::addActiveObjectAsStatic(ServerActiveObject *obj)
1311 v3f objectpos = obj->getBasePosition();
1313 // The block in which the object resides in
1314 v3s16 blockpos_o = getNodeBlockPos(floatToInt(objectpos, BS));
1317 Update the static data
1320 // Create new static object
1321 std::string staticdata = obj->getStaticData();
1322 StaticObject s_obj(obj->getType(), objectpos, staticdata);
1323 // Add to the block where the object is located in
1324 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1325 // Get or generate the block
1326 MapBlock *block = m_map->emergeBlock(blockpos);
1328 bool succeeded = false;
1332 block->m_static_objects.insert(0, s_obj);
1333 block->raiseModified(MOD_STATE_WRITE_AT_UNLOAD,
1334 "addActiveObjectAsStatic");
1338 infostream<<"ServerEnvironment::addActiveObjectAsStatic: "
1339 <<"Could not find or generate "
1340 <<"a block for storing static object"<<std::endl;
1344 if(obj->environmentDeletes())
1352 Finds out what new objects have been added to
1353 inside a radius around a position
1355 void ServerEnvironment::getAddedActiveObjects(v3s16 pos, s16 radius,
1357 std::set<u16> ¤t_objects,
1358 std::set<u16> &added_objects)
1360 v3f pos_f = intToFloat(pos, BS);
1361 f32 radius_f = radius * BS;
1362 f32 player_radius_f = player_radius * BS;
1364 if (player_radius_f < 0)
1365 player_radius_f = 0;
1368 Go through the object list,
1369 - discard m_removed objects,
1370 - discard objects that are too far away,
1371 - discard objects that are found in current_objects.
1372 - add remaining objects to added_objects
1374 for(std::map<u16, ServerActiveObject*>::iterator
1375 i = m_active_objects.begin();
1376 i != m_active_objects.end(); ++i)
1380 ServerActiveObject *object = i->second;
1383 // Discard if removed or deactivating
1384 if(object->m_removed || object->m_pending_deactivation)
1387 f32 distance_f = object->getBasePosition().getDistanceFrom(pos_f);
1388 if (object->getType() == ACTIVEOBJECT_TYPE_PLAYER) {
1389 // Discard if too far
1390 if (distance_f > player_radius_f && player_radius_f != 0)
1392 } else if (distance_f > radius_f)
1395 // Discard if already on current_objects
1396 std::set<u16>::iterator n;
1397 n = current_objects.find(id);
1398 if(n != current_objects.end())
1400 // Add to added_objects
1401 added_objects.insert(id);
1406 Finds out what objects have been removed from
1407 inside a radius around a position
1409 void ServerEnvironment::getRemovedActiveObjects(v3s16 pos, s16 radius,
1411 std::set<u16> ¤t_objects,
1412 std::set<u16> &removed_objects)
1414 v3f pos_f = intToFloat(pos, BS);
1415 f32 radius_f = radius * BS;
1416 f32 player_radius_f = player_radius * BS;
1418 if (player_radius_f < 0)
1419 player_radius_f = 0;
1422 Go through current_objects; object is removed if:
1423 - object is not found in m_active_objects (this is actually an
1424 error condition; objects should be set m_removed=true and removed
1425 only after all clients have been informed about removal), or
1426 - object has m_removed=true, or
1427 - object is too far away
1429 for(std::set<u16>::iterator
1430 i = current_objects.begin();
1431 i != current_objects.end(); ++i)
1434 ServerActiveObject *object = getActiveObject(id);
1437 infostream<<"ServerEnvironment::getRemovedActiveObjects():"
1438 <<" object in current_objects is NULL"<<std::endl;
1439 removed_objects.insert(id);
1443 if(object->m_removed || object->m_pending_deactivation)
1445 removed_objects.insert(id);
1449 f32 distance_f = object->getBasePosition().getDistanceFrom(pos_f);
1450 if (object->getType() == ACTIVEOBJECT_TYPE_PLAYER) {
1451 if (distance_f <= player_radius_f || player_radius_f == 0)
1453 } else if (distance_f <= radius_f)
1456 // Object is no longer visible
1457 removed_objects.insert(id);
1461 ActiveObjectMessage ServerEnvironment::getActiveObjectMessage()
1463 if(m_active_object_messages.empty())
1464 return ActiveObjectMessage(0);
1466 ActiveObjectMessage message = m_active_object_messages.front();
1467 m_active_object_messages.pop_front();
1472 ************ Private methods *************
1475 u16 ServerEnvironment::addActiveObjectRaw(ServerActiveObject *object,
1476 bool set_changed, u32 dtime_s)
1478 assert(object); // Pre-condition
1479 if(object->getId() == 0){
1480 u16 new_id = getFreeServerActiveObjectId(m_active_objects);
1483 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1484 <<"no free ids available"<<std::endl;
1485 if(object->environmentDeletes())
1489 object->setId(new_id);
1492 verbosestream<<"ServerEnvironment::addActiveObjectRaw(): "
1493 <<"supplied with id "<<object->getId()<<std::endl;
1495 if(isFreeServerActiveObjectId(object->getId(), m_active_objects) == false)
1497 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1498 <<"id is not free ("<<object->getId()<<")"<<std::endl;
1499 if(object->environmentDeletes())
1503 /*infostream<<"ServerEnvironment::addActiveObjectRaw(): "
1504 <<"added (id="<<object->getId()<<")"<<std::endl;*/
1506 m_active_objects[object->getId()] = object;
1508 verbosestream<<"ServerEnvironment::addActiveObjectRaw(): "
1509 <<"Added id="<<object->getId()<<"; there are now "
1510 <<m_active_objects.size()<<" active objects."
1513 // Register reference in scripting api (must be done before post-init)
1514 m_script->addObjectReference(object);
1515 // Post-initialize object
1516 object->addedToEnvironment(dtime_s);
1518 // Add static data to block
1519 if(object->isStaticAllowed())
1521 // Add static object to active static list of the block
1522 v3f objectpos = object->getBasePosition();
1523 std::string staticdata = object->getStaticData();
1524 StaticObject s_obj(object->getType(), objectpos, staticdata);
1525 // Add to the block where the object is located in
1526 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1527 MapBlock *block = m_map->emergeBlock(blockpos);
1529 block->m_static_objects.m_active[object->getId()] = s_obj;
1530 object->m_static_exists = true;
1531 object->m_static_block = blockpos;
1534 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1535 "addActiveObjectRaw");
1537 v3s16 p = floatToInt(objectpos, BS);
1538 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1539 <<"could not emerge block for storing id="<<object->getId()
1540 <<" statically (pos="<<PP(p)<<")"<<std::endl;
1544 return object->getId();
1548 Remove objects that satisfy (m_removed && m_known_by_count==0)
1550 void ServerEnvironment::removeRemovedObjects()
1552 std::vector<u16> objects_to_remove;
1553 for(std::map<u16, ServerActiveObject*>::iterator
1554 i = m_active_objects.begin();
1555 i != m_active_objects.end(); ++i) {
1557 ServerActiveObject* obj = i->second;
1558 // This shouldn't happen but check it
1561 infostream<<"NULL object found in ServerEnvironment"
1562 <<" while finding removed objects. id="<<id<<std::endl;
1563 // Id to be removed from m_active_objects
1564 objects_to_remove.push_back(id);
1569 We will delete objects that are marked as removed or thatare
1570 waiting for deletion after deactivation
1572 if(obj->m_removed == false && obj->m_pending_deactivation == false)
1576 Delete static data from block if is marked as removed
1578 if(obj->m_static_exists && obj->m_removed)
1580 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
1582 block->m_static_objects.remove(id);
1583 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1584 "removeRemovedObjects/remove");
1585 obj->m_static_exists = false;
1587 infostream<<"Failed to emerge block from which an object to "
1588 <<"be removed was loaded from. id="<<id<<std::endl;
1592 // If m_known_by_count > 0, don't actually remove. On some future
1593 // invocation this will be 0, which is when removal will continue.
1594 if(obj->m_known_by_count > 0)
1598 Move static data from active to stored if not marked as removed
1600 if(obj->m_static_exists && !obj->m_removed){
1601 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
1603 std::map<u16, StaticObject>::iterator i =
1604 block->m_static_objects.m_active.find(id);
1605 if(i != block->m_static_objects.m_active.end()){
1606 block->m_static_objects.m_stored.push_back(i->second);
1607 block->m_static_objects.m_active.erase(id);
1608 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1609 "removeRemovedObjects/deactivate");
1612 infostream<<"Failed to emerge block from which an object to "
1613 <<"be deactivated was loaded from. id="<<id<<std::endl;
1617 // Tell the object about removal
1618 obj->removingFromEnvironment();
1619 // Deregister in scripting api
1620 m_script->removeObjectReference(obj);
1623 if(obj->environmentDeletes())
1626 // Id to be removed from m_active_objects
1627 objects_to_remove.push_back(id);
1629 // Remove references from m_active_objects
1630 for(std::vector<u16>::iterator i = objects_to_remove.begin();
1631 i != objects_to_remove.end(); ++i) {
1632 m_active_objects.erase(*i);
1636 static void print_hexdump(std::ostream &o, const std::string &data)
1638 const int linelength = 16;
1639 for(int l=0; ; l++){
1640 int i0 = linelength * l;
1641 bool at_end = false;
1642 int thislinelength = linelength;
1643 if(i0 + thislinelength > (int)data.size()){
1644 thislinelength = data.size() - i0;
1647 for(int di=0; di<linelength; di++){
1650 if(di<thislinelength)
1651 snprintf(buf, 4, "%.2x ", data[i]);
1653 snprintf(buf, 4, " ");
1657 for(int di=0; di<thislinelength; di++){
1671 Convert stored objects from blocks near the players to active.
1673 void ServerEnvironment::activateObjects(MapBlock *block, u32 dtime_s)
1678 // Ignore if no stored objects (to not set changed flag)
1679 if(block->m_static_objects.m_stored.empty())
1682 verbosestream<<"ServerEnvironment::activateObjects(): "
1683 <<"activating objects of block "<<PP(block->getPos())
1684 <<" ("<<block->m_static_objects.m_stored.size()
1685 <<" objects)"<<std::endl;
1686 bool large_amount = (block->m_static_objects.m_stored.size() > g_settings->getU16("max_objects_per_block"));
1688 errorstream<<"suspiciously large amount of objects detected: "
1689 <<block->m_static_objects.m_stored.size()<<" in "
1690 <<PP(block->getPos())
1691 <<"; removing all of them."<<std::endl;
1692 // Clear stored list
1693 block->m_static_objects.m_stored.clear();
1694 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1695 "stored list cleared in activateObjects due to "
1696 "large amount of objects");
1700 // Activate stored objects
1701 std::vector<StaticObject> new_stored;
1702 for (std::vector<StaticObject>::iterator
1703 i = block->m_static_objects.m_stored.begin();
1704 i != block->m_static_objects.m_stored.end(); ++i) {
1705 StaticObject &s_obj = *i;
1707 // Create an active object from the data
1708 ServerActiveObject *obj = ServerActiveObject::create
1709 ((ActiveObjectType) s_obj.type, this, 0, s_obj.pos, s_obj.data);
1710 // If couldn't create object, store static data back.
1712 errorstream<<"ServerEnvironment::activateObjects(): "
1713 <<"failed to create active object from static object "
1714 <<"in block "<<PP(s_obj.pos/BS)
1715 <<" type="<<(int)s_obj.type<<" data:"<<std::endl;
1716 print_hexdump(verbosestream, s_obj.data);
1718 new_stored.push_back(s_obj);
1721 verbosestream<<"ServerEnvironment::activateObjects(): "
1722 <<"activated static object pos="<<PP(s_obj.pos/BS)
1723 <<" type="<<(int)s_obj.type<<std::endl;
1724 // This will also add the object to the active static list
1725 addActiveObjectRaw(obj, false, dtime_s);
1727 // Clear stored list
1728 block->m_static_objects.m_stored.clear();
1729 // Add leftover failed stuff to stored list
1730 for(std::vector<StaticObject>::iterator
1731 i = new_stored.begin();
1732 i != new_stored.end(); ++i) {
1733 StaticObject &s_obj = *i;
1734 block->m_static_objects.m_stored.push_back(s_obj);
1737 // Turn the active counterparts of activated objects not pending for
1739 for(std::map<u16, StaticObject>::iterator
1740 i = block->m_static_objects.m_active.begin();
1741 i != block->m_static_objects.m_active.end(); ++i)
1744 ServerActiveObject *object = getActiveObject(id);
1746 object->m_pending_deactivation = false;
1750 Note: Block hasn't really been modified here.
1751 The objects have just been activated and moved from the stored
1752 static list to the active static list.
1753 As such, the block is essentially the same.
1754 Thus, do not call block->raiseModified(MOD_STATE_WRITE_NEEDED).
1755 Otherwise there would be a huge amount of unnecessary I/O.
1760 Convert objects that are not standing inside active blocks to static.
1762 If m_known_by_count != 0, active object is not deleted, but static
1763 data is still updated.
1765 If force_delete is set, active object is deleted nevertheless. It
1766 shall only be set so in the destructor of the environment.
1768 If block wasn't generated (not in memory or on disk),
1770 void ServerEnvironment::deactivateFarObjects(bool force_delete)
1772 std::vector<u16> objects_to_remove;
1773 for(std::map<u16, ServerActiveObject*>::iterator
1774 i = m_active_objects.begin();
1775 i != m_active_objects.end(); ++i) {
1776 ServerActiveObject* obj = i->second;
1779 // Do not deactivate if static data creation not allowed
1780 if(!force_delete && !obj->isStaticAllowed())
1783 // If pending deactivation, let removeRemovedObjects() do it
1784 if(!force_delete && obj->m_pending_deactivation)
1788 v3f objectpos = obj->getBasePosition();
1790 // The block in which the object resides in
1791 v3s16 blockpos_o = getNodeBlockPos(floatToInt(objectpos, BS));
1793 // If object's static data is stored in a deactivated block and object
1794 // is actually located in an active block, re-save to the block in
1795 // which the object is actually located in.
1797 obj->m_static_exists &&
1798 !m_active_blocks.contains(obj->m_static_block) &&
1799 m_active_blocks.contains(blockpos_o))
1801 v3s16 old_static_block = obj->m_static_block;
1803 // Save to block where object is located
1804 MapBlock *block = m_map->emergeBlock(blockpos_o, false);
1806 errorstream<<"ServerEnvironment::deactivateFarObjects(): "
1807 <<"Could not save object id="<<id
1808 <<" to it's current block "<<PP(blockpos_o)
1812 std::string staticdata_new = obj->getStaticData();
1813 StaticObject s_obj(obj->getType(), objectpos, staticdata_new);
1814 block->m_static_objects.insert(id, s_obj);
1815 obj->m_static_block = blockpos_o;
1816 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1817 "deactivateFarObjects: Static data moved in");
1819 // Delete from block where object was located
1820 block = m_map->emergeBlock(old_static_block, false);
1822 errorstream<<"ServerEnvironment::deactivateFarObjects(): "
1823 <<"Could not delete object id="<<id
1824 <<" from it's previous block "<<PP(old_static_block)
1828 block->m_static_objects.remove(id);
1829 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1830 "deactivateFarObjects: Static data moved out");
1834 // If block is active, don't remove
1835 if(!force_delete && m_active_blocks.contains(blockpos_o))
1838 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
1839 <<"deactivating object id="<<id<<" on inactive block "
1840 <<PP(blockpos_o)<<std::endl;
1842 // If known by some client, don't immediately delete.
1843 bool pending_delete = (obj->m_known_by_count > 0 && !force_delete);
1846 Update the static data
1849 if(obj->isStaticAllowed())
1851 // Create new static object
1852 std::string staticdata_new = obj->getStaticData();
1853 StaticObject s_obj(obj->getType(), objectpos, staticdata_new);
1855 bool stays_in_same_block = false;
1856 bool data_changed = true;
1858 if(obj->m_static_exists){
1859 if(obj->m_static_block == blockpos_o)
1860 stays_in_same_block = true;
1862 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
1864 std::map<u16, StaticObject>::iterator n =
1865 block->m_static_objects.m_active.find(id);
1866 if(n != block->m_static_objects.m_active.end()){
1867 StaticObject static_old = n->second;
1869 float save_movem = obj->getMinimumSavedMovement();
1871 if(static_old.data == staticdata_new &&
1872 (static_old.pos - objectpos).getLength() < save_movem)
1873 data_changed = false;
1875 errorstream<<"ServerEnvironment::deactivateFarObjects(): "
1876 <<"id="<<id<<" m_static_exists=true but "
1877 <<"static data doesn't actually exist in "
1878 <<PP(obj->m_static_block)<<std::endl;
1882 bool shall_be_written = (!stays_in_same_block || data_changed);
1884 // Delete old static object
1885 if(obj->m_static_exists)
1887 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
1890 block->m_static_objects.remove(id);
1891 obj->m_static_exists = false;
1892 // Only mark block as modified if data changed considerably
1893 if(shall_be_written)
1894 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1895 "deactivateFarObjects: Static data "
1896 "changed considerably");
1900 // Add to the block where the object is located in
1901 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1902 // Get or generate the block
1903 MapBlock *block = NULL;
1905 block = m_map->emergeBlock(blockpos);
1906 } catch(InvalidPositionException &e){
1907 // Handled via NULL pointer
1908 // NOTE: emergeBlock's failure is usually determined by it
1909 // actually returning NULL
1914 if(block->m_static_objects.m_stored.size() >= g_settings->getU16("max_objects_per_block")){
1915 errorstream<<"ServerEnv: Trying to store id="<<obj->getId()
1916 <<" statically but block "<<PP(blockpos)
1917 <<" already contains "
1918 <<block->m_static_objects.m_stored.size()
1920 <<" Forcing delete."<<std::endl;
1921 force_delete = true;
1923 // If static counterpart already exists in target block,
1925 // This shouldn't happen because the object is removed from
1926 // the previous block before this according to
1927 // obj->m_static_block, but happens rarely for some unknown
1928 // reason. Unsuccessful attempts have been made to find
1930 if(id && block->m_static_objects.m_active.find(id) != block->m_static_objects.m_active.end()){
1931 infostream<<"ServerEnv: WARNING: Performing hack #83274"
1933 block->m_static_objects.remove(id);
1935 // Store static data
1936 u16 store_id = pending_delete ? id : 0;
1937 block->m_static_objects.insert(store_id, s_obj);
1939 // Only mark block as modified if data changed considerably
1940 if(shall_be_written)
1941 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1942 "deactivateFarObjects: Static data "
1943 "changed considerably");
1945 obj->m_static_exists = true;
1946 obj->m_static_block = block->getPos();
1951 v3s16 p = floatToInt(objectpos, BS);
1952 errorstream<<"ServerEnv: Could not find or generate "
1953 <<"a block for storing id="<<obj->getId()
1954 <<" statically (pos="<<PP(p)<<")"<<std::endl;
1961 If known by some client, set pending deactivation.
1962 Otherwise delete it immediately.
1965 if(pending_delete && !force_delete)
1967 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
1968 <<"object id="<<id<<" is known by clients"
1969 <<"; not deleting yet"<<std::endl;
1971 obj->m_pending_deactivation = true;
1975 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
1976 <<"object id="<<id<<" is not known by clients"
1977 <<"; deleting"<<std::endl;
1979 // Tell the object about removal
1980 obj->removingFromEnvironment();
1981 // Deregister in scripting api
1982 m_script->removeObjectReference(obj);
1984 // Delete active object
1985 if(obj->environmentDeletes())
1987 // Id to be removed from m_active_objects
1988 objects_to_remove.push_back(id);
1991 // Remove references from m_active_objects
1992 for(std::vector<u16>::iterator i = objects_to_remove.begin();
1993 i != objects_to_remove.end(); ++i) {
1994 m_active_objects.erase(*i);
2001 #include "clientsimpleobject.h"
2007 ClientEnvironment::ClientEnvironment(ClientMap *map, scene::ISceneManager *smgr,
2008 ITextureSource *texturesource, IGameDef *gamedef,
2009 IrrlichtDevice *irr):
2012 m_texturesource(texturesource),
2017 memset(m_attachements, zero, sizeof(m_attachements));
2020 ClientEnvironment::~ClientEnvironment()
2022 // delete active objects
2023 for(std::map<u16, ClientActiveObject*>::iterator
2024 i = m_active_objects.begin();
2025 i != m_active_objects.end(); ++i)
2030 for(std::vector<ClientSimpleObject*>::iterator
2031 i = m_simple_objects.begin(); i != m_simple_objects.end(); ++i) {
2039 Map & ClientEnvironment::getMap()
2044 ClientMap & ClientEnvironment::getClientMap()
2049 void ClientEnvironment::addPlayer(Player *player)
2051 DSTACK(__FUNCTION_NAME);
2053 It is a failure if player is local and there already is a local
2056 FATAL_ERROR_IF(player->isLocal() == true && getLocalPlayer() != NULL,
2057 "Player is local but there is already a local player");
2059 Environment::addPlayer(player);
2062 LocalPlayer * ClientEnvironment::getLocalPlayer()
2064 for(std::vector<Player*>::iterator i = m_players.begin();
2065 i != m_players.end(); ++i) {
2066 Player *player = *i;
2067 if(player->isLocal())
2068 return (LocalPlayer*)player;
2073 void ClientEnvironment::step(float dtime)
2075 DSTACK(__FUNCTION_NAME);
2077 /* Step time of day */
2078 stepTimeOfDay(dtime);
2080 // Get some settings
2081 bool fly_allowed = m_gamedef->checkLocalPrivilege("fly");
2082 bool free_move = fly_allowed && g_settings->getBool("free_move");
2085 LocalPlayer *lplayer = getLocalPlayer();
2087 // collision info queue
2088 std::vector<CollisionInfo> player_collisions;
2091 Get the speed the player is going
2093 bool is_climbing = lplayer->is_climbing;
2095 f32 player_speed = lplayer->getSpeed().getLength();
2098 Maximum position increment
2100 //f32 position_max_increment = 0.05*BS;
2101 f32 position_max_increment = 0.1*BS;
2103 // Maximum time increment (for collision detection etc)
2104 // time = distance / speed
2105 f32 dtime_max_increment = 1;
2106 if(player_speed > 0.001)
2107 dtime_max_increment = position_max_increment / player_speed;
2109 // Maximum time increment is 10ms or lower
2110 if(dtime_max_increment > 0.01)
2111 dtime_max_increment = 0.01;
2113 // Don't allow overly huge dtime
2117 f32 dtime_downcount = dtime;
2120 Stuff that has a maximum time increment
2129 if(dtime_downcount > dtime_max_increment)
2131 dtime_part = dtime_max_increment;
2132 dtime_downcount -= dtime_part;
2136 dtime_part = dtime_downcount;
2138 Setting this to 0 (no -=dtime_part) disables an infinite loop
2139 when dtime_part is so small that dtime_downcount -= dtime_part
2142 dtime_downcount = 0;
2151 if(free_move == false && is_climbing == false)
2154 v3f speed = lplayer->getSpeed();
2155 if(lplayer->in_liquid == false)
2156 speed.Y -= lplayer->movement_gravity * lplayer->physics_override_gravity * dtime_part * 2;
2158 // Liquid floating / sinking
2159 if(lplayer->in_liquid && !lplayer->swimming_vertical)
2160 speed.Y -= lplayer->movement_liquid_sink * dtime_part * 2;
2162 // Liquid resistance
2163 if(lplayer->in_liquid_stable || lplayer->in_liquid)
2165 // How much the node's viscosity blocks movement, ranges between 0 and 1
2166 // Should match the scale at which viscosity increase affects other liquid attributes
2167 const f32 viscosity_factor = 0.3;
2169 v3f d_wanted = -speed / lplayer->movement_liquid_fluidity;
2170 f32 dl = d_wanted.getLength();
2171 if(dl > lplayer->movement_liquid_fluidity_smooth)
2172 dl = lplayer->movement_liquid_fluidity_smooth;
2173 dl *= (lplayer->liquid_viscosity * viscosity_factor) + (1 - viscosity_factor);
2175 v3f d = d_wanted.normalize() * dl;
2179 if(speed.X > lplayer->movement_liquid_fluidity + lplayer->movement_liquid_fluidity_smooth) speed.X -= lplayer->movement_liquid_fluidity_smooth;
2180 if(speed.X < -lplayer->movement_liquid_fluidity - lplayer->movement_liquid_fluidity_smooth) speed.X += lplayer->movement_liquid_fluidity_smooth;
2181 if(speed.Y > lplayer->movement_liquid_fluidity + lplayer->movement_liquid_fluidity_smooth) speed.Y -= lplayer->movement_liquid_fluidity_smooth;
2182 if(speed.Y < -lplayer->movement_liquid_fluidity - lplayer->movement_liquid_fluidity_smooth) speed.Y += lplayer->movement_liquid_fluidity_smooth;
2183 if(speed.Z > lplayer->movement_liquid_fluidity + lplayer->movement_liquid_fluidity_smooth) speed.Z -= lplayer->movement_liquid_fluidity_smooth;
2184 if(speed.Z < -lplayer->movement_liquid_fluidity - lplayer->movement_liquid_fluidity_smooth) speed.Z += lplayer->movement_liquid_fluidity_smooth;
2188 lplayer->setSpeed(speed);
2193 This also does collision detection.
2195 lplayer->move(dtime_part, this, position_max_increment,
2196 &player_collisions);
2199 while(dtime_downcount > 0.001);
2201 //std::cout<<"Looped "<<loopcount<<" times."<<std::endl;
2203 for(std::vector<CollisionInfo>::iterator i = player_collisions.begin();
2204 i != player_collisions.end(); ++i) {
2205 CollisionInfo &info = *i;
2206 v3f speed_diff = info.new_speed - info.old_speed;;
2207 // Handle only fall damage
2208 // (because otherwise walking against something in fast_move kills you)
2209 if(speed_diff.Y < 0 || info.old_speed.Y >= 0)
2211 // Get rid of other components
2214 f32 pre_factor = 1; // 1 hp per node/s
2215 f32 tolerance = BS*14; // 5 without damage
2216 f32 post_factor = 1; // 1 hp per node/s
2217 if(info.type == COLLISION_NODE)
2219 const ContentFeatures &f = m_gamedef->ndef()->
2220 get(m_map->getNodeNoEx(info.node_p));
2221 // Determine fall damage multiplier
2222 int addp = itemgroup_get(f.groups, "fall_damage_add_percent");
2223 pre_factor = 1.0 + (float)addp/100.0;
2225 float speed = pre_factor * speed_diff.getLength();
2226 if(speed > tolerance)
2228 f32 damage_f = (speed - tolerance)/BS * post_factor;
2229 u16 damage = (u16)(damage_f+0.5);
2231 damageLocalPlayer(damage, true);
2232 MtEvent *e = new SimpleTriggerEvent("PlayerFallingDamage");
2233 m_gamedef->event()->put(e);
2239 A quick draft of lava damage
2241 if(m_lava_hurt_interval.step(dtime, 1.0))
2243 v3f pf = lplayer->getPosition();
2245 // Feet, middle and head
2246 v3s16 p1 = floatToInt(pf + v3f(0, BS*0.1, 0), BS);
2247 MapNode n1 = m_map->getNodeNoEx(p1);
2248 v3s16 p2 = floatToInt(pf + v3f(0, BS*0.8, 0), BS);
2249 MapNode n2 = m_map->getNodeNoEx(p2);
2250 v3s16 p3 = floatToInt(pf + v3f(0, BS*1.6, 0), BS);
2251 MapNode n3 = m_map->getNodeNoEx(p3);
2253 u32 damage_per_second = 0;
2254 damage_per_second = MYMAX(damage_per_second,
2255 m_gamedef->ndef()->get(n1).damage_per_second);
2256 damage_per_second = MYMAX(damage_per_second,
2257 m_gamedef->ndef()->get(n2).damage_per_second);
2258 damage_per_second = MYMAX(damage_per_second,
2259 m_gamedef->ndef()->get(n3).damage_per_second);
2261 if(damage_per_second != 0)
2263 damageLocalPlayer(damage_per_second, true);
2270 if(m_drowning_interval.step(dtime, 2.0))
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);
2278 u8 drowning_damage = c.drowning;
2279 if(drowning_damage > 0 && lplayer->hp > 0){
2280 u16 breath = lplayer->getBreath();
2287 lplayer->setBreath(breath);
2288 updateLocalPlayerBreath(breath);
2291 if(lplayer->getBreath() == 0 && drowning_damage > 0){
2292 damageLocalPlayer(drowning_damage, true);
2295 if(m_breathing_interval.step(dtime, 0.5))
2297 v3f pf = lplayer->getPosition();
2300 v3s16 p = floatToInt(pf + v3f(0, BS*1.6, 0), BS);
2301 MapNode n = m_map->getNodeNoEx(p);
2302 ContentFeatures c = m_gamedef->ndef()->get(n);
2304 lplayer->setBreath(11);
2306 else if(c.drowning == 0){
2307 u16 breath = lplayer->getBreath();
2310 lplayer->setBreath(breath);
2311 updateLocalPlayerBreath(breath);
2317 Stuff that can be done in an arbitarily large dtime
2319 for(std::vector<Player*>::iterator i = m_players.begin();
2320 i != m_players.end(); ++i) {
2321 Player *player = *i;
2324 Handle non-local players
2326 if(player->isLocal() == false) {
2328 player->move(dtime, this, 100*BS);
2333 // Update lighting on local player (used for wield item)
2334 u32 day_night_ratio = getDayNightRatio();
2338 // On InvalidPositionException, use this as default
2339 // (day: LIGHT_SUN, night: 0)
2340 MapNode node_at_lplayer(CONTENT_AIR, 0x0f, 0);
2342 v3s16 p = lplayer->getLightPosition();
2343 node_at_lplayer = m_map->getNodeNoEx(p);
2345 u16 light = getInteriorLight(node_at_lplayer, 0, m_gamedef->ndef());
2346 u8 day = light & 0xff;
2347 u8 night = (light >> 8) & 0xff;
2348 finalColorBlend(lplayer->light_color, day, night, day_night_ratio);
2352 Step active objects and update lighting of them
2355 g_profiler->avg("CEnv: num of objects", m_active_objects.size());
2356 bool update_lighting = m_active_object_light_update_interval.step(dtime, 0.21);
2357 for(std::map<u16, ClientActiveObject*>::iterator
2358 i = m_active_objects.begin();
2359 i != m_active_objects.end(); ++i)
2361 ClientActiveObject* obj = i->second;
2363 obj->step(dtime, this);
2372 v3s16 p = obj->getLightPosition();
2373 MapNode n = m_map->getNodeNoEx(p, &pos_ok);
2375 light = n.getLightBlend(day_night_ratio, m_gamedef->ndef());
2377 light = blend_light(day_night_ratio, LIGHT_SUN, 0);
2379 obj->updateLight(light);
2384 Step and handle simple objects
2386 g_profiler->avg("CEnv: num of simple objects", m_simple_objects.size());
2387 for(std::vector<ClientSimpleObject*>::iterator
2388 i = m_simple_objects.begin(); i != m_simple_objects.end();) {
2389 std::vector<ClientSimpleObject*>::iterator cur = i;
2390 ClientSimpleObject *simple = *cur;
2392 simple->step(dtime);
2393 if(simple->m_to_be_removed) {
2395 i = m_simple_objects.erase(cur);
2403 void ClientEnvironment::addSimpleObject(ClientSimpleObject *simple)
2405 m_simple_objects.push_back(simple);
2408 ClientActiveObject* ClientEnvironment::getActiveObject(u16 id)
2410 std::map<u16, ClientActiveObject*>::iterator n;
2411 n = m_active_objects.find(id);
2412 if(n == m_active_objects.end())
2417 bool isFreeClientActiveObjectId(u16 id,
2418 std::map<u16, ClientActiveObject*> &objects)
2423 return objects.find(id) == objects.end();
2426 u16 getFreeClientActiveObjectId(
2427 std::map<u16, ClientActiveObject*> &objects)
2429 //try to reuse id's as late as possible
2430 static u16 last_used_id = 0;
2431 u16 startid = last_used_id;
2435 if(isFreeClientActiveObjectId(last_used_id, objects))
2436 return last_used_id;
2438 if(last_used_id == startid)
2443 u16 ClientEnvironment::addActiveObject(ClientActiveObject *object)
2445 assert(object); // Pre-condition
2446 if(object->getId() == 0)
2448 u16 new_id = getFreeClientActiveObjectId(m_active_objects);
2451 infostream<<"ClientEnvironment::addActiveObject(): "
2452 <<"no free ids available"<<std::endl;
2456 object->setId(new_id);
2458 if(isFreeClientActiveObjectId(object->getId(), m_active_objects) == false)
2460 infostream<<"ClientEnvironment::addActiveObject(): "
2461 <<"id is not free ("<<object->getId()<<")"<<std::endl;
2465 infostream<<"ClientEnvironment::addActiveObject(): "
2466 <<"added (id="<<object->getId()<<")"<<std::endl;
2467 m_active_objects[object->getId()] = object;
2468 object->addToScene(m_smgr, m_texturesource, m_irr);
2469 { // Update lighting immediately
2474 v3s16 p = object->getLightPosition();
2475 MapNode n = m_map->getNodeNoEx(p, &pos_ok);
2477 light = n.getLightBlend(getDayNightRatio(), m_gamedef->ndef());
2479 light = blend_light(getDayNightRatio(), LIGHT_SUN, 0);
2481 object->updateLight(light);
2483 return object->getId();
2486 void ClientEnvironment::addActiveObject(u16 id, u8 type,
2487 const std::string &init_data)
2489 ClientActiveObject* obj =
2490 ClientActiveObject::create((ActiveObjectType) type, m_gamedef, this);
2493 infostream<<"ClientEnvironment::addActiveObject(): "
2494 <<"id="<<id<<" type="<<type<<": Couldn't create object"
2503 obj->initialize(init_data);
2505 catch(SerializationError &e)
2507 errorstream<<"ClientEnvironment::addActiveObject():"
2508 <<" id="<<id<<" type="<<type
2509 <<": SerializationError in initialize(): "
2511 <<": init_data="<<serializeJsonString(init_data)
2515 addActiveObject(obj);
2518 void ClientEnvironment::removeActiveObject(u16 id)
2520 verbosestream<<"ClientEnvironment::removeActiveObject(): "
2521 <<"id="<<id<<std::endl;
2522 ClientActiveObject* obj = getActiveObject(id);
2525 infostream<<"ClientEnvironment::removeActiveObject(): "
2526 <<"id="<<id<<" not found"<<std::endl;
2529 obj->removeFromScene(true);
2531 m_active_objects.erase(id);
2534 void ClientEnvironment::processActiveObjectMessage(u16 id,
2535 const std::string &data)
2537 ClientActiveObject* obj = getActiveObject(id);
2540 infostream<<"ClientEnvironment::processActiveObjectMessage():"
2541 <<" got message for id="<<id<<", which doesn't exist."
2547 obj->processMessage(data);
2549 catch(SerializationError &e)
2551 errorstream<<"ClientEnvironment::processActiveObjectMessage():"
2552 <<" id="<<id<<" type="<<obj->getType()
2553 <<" SerializationError in processMessage(),"
2554 <<" message="<<serializeJsonString(data)
2560 Callbacks for activeobjects
2563 void ClientEnvironment::damageLocalPlayer(u8 damage, bool handle_hp)
2565 LocalPlayer *lplayer = getLocalPlayer();
2569 if (lplayer->hp > damage)
2570 lplayer->hp -= damage;
2575 ClientEnvEvent event;
2576 event.type = CEE_PLAYER_DAMAGE;
2577 event.player_damage.amount = damage;
2578 event.player_damage.send_to_server = handle_hp;
2579 m_client_event_queue.push_back(event);
2582 void ClientEnvironment::updateLocalPlayerBreath(u16 breath)
2584 ClientEnvEvent event;
2585 event.type = CEE_PLAYER_BREATH;
2586 event.player_breath.amount = breath;
2587 m_client_event_queue.push_back(event);
2591 Client likes to call these
2594 void ClientEnvironment::getActiveObjects(v3f origin, f32 max_d,
2595 std::vector<DistanceSortedActiveObject> &dest)
2597 for(std::map<u16, ClientActiveObject*>::iterator
2598 i = m_active_objects.begin();
2599 i != m_active_objects.end(); ++i)
2601 ClientActiveObject* obj = i->second;
2603 f32 d = (obj->getPosition() - origin).getLength();
2608 DistanceSortedActiveObject dso(obj, d);
2610 dest.push_back(dso);
2614 ClientEnvEvent ClientEnvironment::getClientEvent()
2616 ClientEnvEvent event;
2617 if(m_client_event_queue.empty())
2618 event.type = CEE_NONE;
2620 event = m_client_event_queue.front();
2621 m_client_event_queue.pop_front();
2626 #endif // #ifndef SERVER