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.
20 #include "environment.h"
23 #include "collision.h"
24 #include "content_mapnode.h"
26 #include "serverobject.h"
27 #include "content_sao.h"
31 #include "scripting_game.h"
33 #include "nodemetadata.h"
34 #include "main.h" // For g_settings, g_profiler
37 #include "clientmap.h"
38 #include "localplayer.h"
41 #include "daynightratio.h"
44 #include "util/serialize.h"
45 #include "jthread/jmutexautolock.h"
47 #define PP(x) "("<<(x).X<<","<<(x).Y<<","<<(x).Z<<")"
49 Environment::Environment():
51 m_time_of_day_f(9000./24000),
52 m_time_of_day_speed(0),
54 m_enable_day_night_ratio_override(false),
55 m_day_night_ratio_override(0.0f)
59 Environment::~Environment()
62 for(std::list<Player*>::iterator i = m_players.begin();
63 i != m_players.end(); ++i)
69 void Environment::addPlayer(Player *player)
71 DSTACK(__FUNCTION_NAME);
73 Check that peer_ids are unique.
74 Also check that names are unique.
75 Exception: there can be multiple players with peer_id=0
77 // If peer id is non-zero, it has to be unique.
78 if(player->peer_id != 0)
79 assert(getPlayer(player->peer_id) == NULL);
80 // Name has to be unique.
81 assert(getPlayer(player->getName()) == NULL);
83 m_players.push_back(player);
86 void Environment::removePlayer(u16 peer_id)
88 DSTACK(__FUNCTION_NAME);
90 for(std::list<Player*>::iterator i = m_players.begin();
91 i != m_players.end();)
94 if(player->peer_id == peer_id) {
96 i = m_players.erase(i);
103 void Environment::removePlayer(const char *name)
105 for (std::list<Player*>::iterator it = m_players.begin();
106 it != m_players.end(); ++it) {
107 if (strcmp((*it)->getName(), name) == 0) {
115 Player * Environment::getPlayer(u16 peer_id)
117 for(std::list<Player*>::iterator i = m_players.begin();
118 i != m_players.end(); ++i)
121 if(player->peer_id == peer_id)
127 Player * Environment::getPlayer(const char *name)
129 for(std::list<Player*>::iterator i = m_players.begin();
130 i != m_players.end(); ++i)
133 if(strcmp(player->getName(), name) == 0)
139 Player * Environment::getRandomConnectedPlayer()
141 std::list<Player*> connected_players = getPlayers(true);
142 u32 chosen_one = myrand() % connected_players.size();
144 for(std::list<Player*>::iterator
145 i = connected_players.begin();
146 i != connected_players.end(); ++i)
158 Player * Environment::getNearestConnectedPlayer(v3f pos)
160 std::list<Player*> connected_players = getPlayers(true);
162 Player *nearest_player = NULL;
163 for(std::list<Player*>::iterator
164 i = connected_players.begin();
165 i != connected_players.end(); ++i)
168 f32 d = player->getPosition().getDistanceFrom(pos);
169 if(d < nearest_d || nearest_player == NULL)
172 nearest_player = player;
175 return nearest_player;
178 std::list<Player*> Environment::getPlayers()
183 std::list<Player*> Environment::getPlayers(bool ignore_disconnected)
185 std::list<Player*> newlist;
186 for(std::list<Player*>::iterator
187 i = m_players.begin();
188 i != m_players.end(); ++i)
192 if(ignore_disconnected)
194 // Ignore disconnected players
195 if(player->peer_id == 0)
199 newlist.push_back(player);
204 u32 Environment::getDayNightRatio()
206 if(m_enable_day_night_ratio_override)
207 return m_day_night_ratio_override;
208 bool smooth = g_settings->getBool("enable_shaders");
209 return time_to_daynight_ratio(m_time_of_day_f*24000, smooth);
212 void Environment::setTimeOfDaySpeed(float speed)
214 JMutexAutoLock(this->m_lock);
215 m_time_of_day_speed = speed;
218 float Environment::getTimeOfDaySpeed()
220 JMutexAutoLock(this->m_lock);
221 float retval = m_time_of_day_speed;
225 void Environment::stepTimeOfDay(float dtime)
229 JMutexAutoLock(this->m_lock);
230 day_speed = m_time_of_day_speed;
233 m_time_counter += dtime;
234 f32 speed = day_speed * 24000./(24.*3600);
235 u32 units = (u32)(m_time_counter*speed);
239 if(m_time_of_day + units >= 24000)
241 m_time_of_day = (m_time_of_day + units) % 24000;
243 m_time_of_day_f = (float)m_time_of_day / 24000.0;
246 m_time_counter -= (f32)units / speed;
249 m_time_of_day_f += day_speed/24/3600*dtime;
250 if(m_time_of_day_f > 1.0)
251 m_time_of_day_f -= 1.0;
252 if(m_time_of_day_f < 0.0)
253 m_time_of_day_f += 1.0;
261 ABMWithState::ABMWithState(ActiveBlockModifier *abm_):
265 // Initialize timer to random value to spread processing
266 float itv = abm->getTriggerInterval();
267 itv = MYMAX(0.001, itv); // No less than 1ms
268 int minval = MYMAX(-0.51*itv, -60); // Clamp to
269 int maxval = MYMIN(0.51*itv, 60); // +-60 seconds
270 timer = myrand_range(minval, maxval);
277 void fillRadiusBlock(v3s16 p0, s16 r, std::set<v3s16> &list)
280 for(p.X=p0.X-r; p.X<=p0.X+r; p.X++)
281 for(p.Y=p0.Y-r; p.Y<=p0.Y+r; p.Y++)
282 for(p.Z=p0.Z-r; p.Z<=p0.Z+r; p.Z++)
289 void ActiveBlockList::update(std::list<v3s16> &active_positions,
291 std::set<v3s16> &blocks_removed,
292 std::set<v3s16> &blocks_added)
297 std::set<v3s16> newlist = m_forceloaded_list;
298 for(std::list<v3s16>::iterator i = active_positions.begin();
299 i != active_positions.end(); ++i)
301 fillRadiusBlock(*i, radius, newlist);
305 Find out which blocks on the old list are not on the new list
307 // Go through old list
308 for(std::set<v3s16>::iterator i = m_list.begin();
309 i != m_list.end(); ++i)
312 // If not on new list, it's been removed
313 if(newlist.find(p) == newlist.end())
314 blocks_removed.insert(p);
318 Find out which blocks on the new list are not on the old list
320 // Go through new list
321 for(std::set<v3s16>::iterator i = newlist.begin();
322 i != newlist.end(); ++i)
325 // If not on old list, it's been added
326 if(m_list.find(p) == m_list.end())
327 blocks_added.insert(p);
334 for(std::set<v3s16>::iterator i = newlist.begin();
335 i != newlist.end(); ++i)
346 ServerEnvironment::ServerEnvironment(ServerMap *map,
347 GameScripting *scriptIface, IGameDef *gamedef,
348 const std::string &path_world) :
350 m_script(scriptIface),
352 m_path_world(path_world),
353 m_send_recommended_timer(0),
354 m_active_block_interval_overload_skip(0),
356 m_game_time_fraction_counter(0),
357 m_recommended_send_interval(0.1),
358 m_max_lag_estimate(0.1)
362 ServerEnvironment::~ServerEnvironment()
364 // Clear active block list.
365 // This makes the next one delete all active objects.
366 m_active_blocks.clear();
368 // Convert all objects to static and delete the active objects
369 deactivateFarObjects(true);
374 // Delete ActiveBlockModifiers
375 for(std::list<ABMWithState>::iterator
376 i = m_abms.begin(); i != m_abms.end(); ++i){
381 Map & ServerEnvironment::getMap()
386 ServerMap & ServerEnvironment::getServerMap()
391 bool ServerEnvironment::line_of_sight(v3f pos1, v3f pos2, float stepsize, v3s16 *p)
393 float distance = pos1.getDistanceFrom(pos2);
395 //calculate normalized direction vector
396 v3f normalized_vector = v3f((pos2.X - pos1.X)/distance,
397 (pos2.Y - pos1.Y)/distance,
398 (pos2.Z - pos1.Z)/distance);
400 //find out if there's a node on path between pos1 and pos2
401 for (float i = 1; i < distance; i += stepsize) {
402 v3s16 pos = floatToInt(v3f(normalized_vector.X * i,
403 normalized_vector.Y * i,
404 normalized_vector.Z * i) +pos1,BS);
406 MapNode n = getMap().getNodeNoEx(pos);
408 if(n.param0 != CONTENT_AIR) {
418 void ServerEnvironment::saveLoadedPlayers()
420 std::string players_path = m_path_world + DIR_DELIM "players";
421 fs::CreateDir(players_path);
423 for (std::list<Player*>::iterator it = m_players.begin();
424 it != m_players.end();
426 RemotePlayer *player = static_cast<RemotePlayer*>(*it);
427 if (player->checkModified()) {
428 player->save(players_path);
433 void ServerEnvironment::savePlayer(const std::string &playername)
435 std::string players_path = m_path_world + DIR_DELIM "players";
436 fs::CreateDir(players_path);
438 RemotePlayer *player = static_cast<RemotePlayer*>(getPlayer(playername.c_str()));
440 player->save(players_path);
444 Player *ServerEnvironment::loadPlayer(const std::string &playername)
446 std::string players_path = m_path_world + DIR_DELIM "players" DIR_DELIM;
448 RemotePlayer *player = static_cast<RemotePlayer*>(getPlayer(playername.c_str()));
449 bool newplayer = false;
452 player = new RemotePlayer(m_gamedef);
456 RemotePlayer testplayer(m_gamedef);
457 std::string path = players_path + playername;
458 for (u32 i = 0; i < PLAYER_FILE_ALTERNATE_TRIES; i++) {
459 // Open file and deserialize
460 std::ifstream is(path.c_str(), std::ios_base::binary);
464 testplayer.deSerialize(is, path);
466 if (testplayer.getName() == playername) {
467 *player = testplayer;
471 path = players_path + playername + itos(i);
474 infostream << "Player file for player " << playername
475 << " not found" << std::endl;
484 void ServerEnvironment::saveMeta()
486 std::string path = m_path_world + DIR_DELIM "env_meta.txt";
488 // Open file and serialize
489 std::ostringstream ss(std::ios_base::binary);
492 args.setU64("game_time", m_game_time);
493 args.setU64("time_of_day", getTimeOfDay());
497 if(!fs::safeWriteToFile(path, ss.str()))
499 infostream<<"ServerEnvironment::saveMeta(): Failed to write "
501 throw SerializationError("Couldn't save env meta");
505 void ServerEnvironment::loadMeta()
507 std::string path = m_path_world + DIR_DELIM "env_meta.txt";
509 // Open file and deserialize
510 std::ifstream is(path.c_str(), std::ios_base::binary);
512 infostream << "ServerEnvironment::loadMeta(): Failed to open "
513 << path << std::endl;
514 throw SerializationError("Couldn't load env meta");
519 if (!args.parseConfigLines(is, "EnvArgsEnd")) {
520 throw SerializationError("ServerEnvironment::loadMeta(): "
521 "EnvArgsEnd not found!");
525 m_game_time = args.getU64("game_time");
526 } catch (SettingNotFoundException &e) {
527 // Getting this is crucial, otherwise timestamps are useless
528 throw SerializationError("Couldn't load env meta game_time");
532 m_time_of_day = args.getU64("time_of_day");
533 } catch (SettingNotFoundException &e) {
534 // This is not as important
535 m_time_of_day = 9000;
541 ActiveBlockModifier *abm;
543 std::set<content_t> required_neighbors;
549 ServerEnvironment *m_env;
550 std::map<content_t, std::list<ActiveABM> > m_aabms;
552 ABMHandler(std::list<ABMWithState> &abms,
553 float dtime_s, ServerEnvironment *env,
559 INodeDefManager *ndef = env->getGameDef()->ndef();
560 for(std::list<ABMWithState>::iterator
561 i = abms.begin(); i != abms.end(); ++i){
562 ActiveBlockModifier *abm = i->abm;
563 float trigger_interval = abm->getTriggerInterval();
564 if(trigger_interval < 0.001)
565 trigger_interval = 0.001;
566 float actual_interval = dtime_s;
569 if(i->timer < trigger_interval)
571 i->timer -= trigger_interval;
572 actual_interval = trigger_interval;
574 float intervals = actual_interval / trigger_interval;
577 float chance = abm->getTriggerChance();
582 aabm.chance = chance / intervals;
586 std::set<std::string> required_neighbors_s
587 = abm->getRequiredNeighbors();
588 for(std::set<std::string>::iterator
589 i = required_neighbors_s.begin();
590 i != required_neighbors_s.end(); i++)
592 ndef->getIds(*i, aabm.required_neighbors);
595 std::set<std::string> contents_s = abm->getTriggerContents();
596 for(std::set<std::string>::iterator
597 i = contents_s.begin(); i != contents_s.end(); i++)
599 std::set<content_t> ids;
600 ndef->getIds(*i, ids);
601 for(std::set<content_t>::const_iterator k = ids.begin();
605 std::map<content_t, std::list<ActiveABM> >::iterator j;
607 if(j == m_aabms.end()){
608 std::list<ActiveABM> aabmlist;
609 m_aabms[c] = aabmlist;
612 j->second.push_back(aabm);
617 // Find out how many objects the given block and its neighbours contain.
618 // Returns the number of objects in the block, and also in 'wider' the
619 // number of objects in the block and all its neighbours. The latter
620 // may an estimate if any neighbours are unloaded.
621 u32 countObjects(MapBlock *block, ServerMap * map, u32 &wider)
624 u32 wider_unknown_count = 0;
625 for(s16 x=-1; x<=1; x++)
626 for(s16 y=-1; y<=1; y++)
627 for(s16 z=-1; z<=1; z++)
629 MapBlock *block2 = map->getBlockNoCreateNoEx(
630 block->getPos() + v3s16(x,y,z));
632 wider_unknown_count++;
635 wider += block2->m_static_objects.m_active.size()
636 + block2->m_static_objects.m_stored.size();
639 u32 active_object_count = block->m_static_objects.m_active.size();
640 u32 wider_known_count = 3*3*3 - wider_unknown_count;
641 wider += wider_unknown_count * wider / wider_known_count;
642 return active_object_count;
645 void apply(MapBlock *block)
650 ServerMap *map = &m_env->getServerMap();
652 u32 active_object_count_wider;
653 u32 active_object_count = this->countObjects(block, map, active_object_count_wider);
654 m_env->m_added_objects = 0;
657 for(p0.X=0; p0.X<MAP_BLOCKSIZE; p0.X++)
658 for(p0.Y=0; p0.Y<MAP_BLOCKSIZE; p0.Y++)
659 for(p0.Z=0; p0.Z<MAP_BLOCKSIZE; p0.Z++)
661 MapNode n = block->getNodeNoEx(p0);
662 content_t c = n.getContent();
663 v3s16 p = p0 + block->getPosRelative();
665 std::map<content_t, std::list<ActiveABM> >::iterator j;
667 if(j == m_aabms.end())
670 for(std::list<ActiveABM>::iterator
671 i = j->second.begin(); i != j->second.end(); i++)
673 if(myrand() % i->chance != 0)
677 if(!i->required_neighbors.empty())
680 for(p1.X = p.X-1; p1.X <= p.X+1; p1.X++)
681 for(p1.Y = p.Y-1; p1.Y <= p.Y+1; p1.Y++)
682 for(p1.Z = p.Z-1; p1.Z <= p.Z+1; p1.Z++)
686 MapNode n = map->getNodeNoEx(p1);
687 content_t c = n.getContent();
688 std::set<content_t>::const_iterator k;
689 k = i->required_neighbors.find(c);
690 if(k != i->required_neighbors.end()){
694 // No required neighbor found
699 // Call all the trigger variations
700 i->abm->trigger(m_env, p, n);
701 i->abm->trigger(m_env, p, n,
702 active_object_count, active_object_count_wider);
704 // Count surrounding objects again if the abms added any
705 if(m_env->m_added_objects > 0) {
706 active_object_count = countObjects(block, map, active_object_count_wider);
707 m_env->m_added_objects = 0;
714 void ServerEnvironment::activateBlock(MapBlock *block, u32 additional_dtime)
716 // Reset usage timer immediately, otherwise a block that becomes active
717 // again at around the same time as it would normally be unloaded will
718 // get unloaded incorrectly. (I think this still leaves a small possibility
719 // of a race condition between this and server::AsyncRunStep, which only
720 // some kind of synchronisation will fix, but it at least reduces the window
721 // of opportunity for it to break from seconds to nanoseconds)
722 block->resetUsageTimer();
724 // Get time difference
726 u32 stamp = block->getTimestamp();
727 if(m_game_time > stamp && stamp != BLOCK_TIMESTAMP_UNDEFINED)
728 dtime_s = m_game_time - block->getTimestamp();
729 dtime_s += additional_dtime;
731 /*infostream<<"ServerEnvironment::activateBlock(): block timestamp: "
732 <<stamp<<", game time: "<<m_game_time<<std::endl;*/
734 // Set current time as timestamp
735 block->setTimestampNoChangedFlag(m_game_time);
737 /*infostream<<"ServerEnvironment::activateBlock(): block is "
738 <<dtime_s<<" seconds old."<<std::endl;*/
740 // Activate stored objects
741 activateObjects(block, dtime_s);
744 std::map<v3s16, NodeTimer> elapsed_timers =
745 block->m_node_timers.step((float)dtime_s);
746 if(!elapsed_timers.empty()){
748 for(std::map<v3s16, NodeTimer>::iterator
749 i = elapsed_timers.begin();
750 i != elapsed_timers.end(); i++){
751 n = block->getNodeNoEx(i->first);
752 v3s16 p = i->first + block->getPosRelative();
753 if(m_script->node_on_timer(p,n,i->second.elapsed))
754 block->setNodeTimer(i->first,NodeTimer(i->second.timeout,0));
758 /* Handle ActiveBlockModifiers */
759 ABMHandler abmhandler(m_abms, dtime_s, this, false);
760 abmhandler.apply(block);
763 void ServerEnvironment::addActiveBlockModifier(ActiveBlockModifier *abm)
765 m_abms.push_back(ABMWithState(abm));
768 bool ServerEnvironment::setNode(v3s16 p, const MapNode &n)
770 INodeDefManager *ndef = m_gamedef->ndef();
771 MapNode n_old = m_map->getNodeNoEx(p);
774 if (ndef->get(n_old).has_on_destruct)
775 m_script->node_on_destruct(p, n_old);
778 if (!m_map->addNodeWithEvent(p, n))
781 // Update active VoxelManipulator if a mapgen thread
782 m_map->updateVManip(p);
784 // Call post-destructor
785 if (ndef->get(n_old).has_after_destruct)
786 m_script->node_after_destruct(p, n_old);
789 if (ndef->get(n).has_on_construct)
790 m_script->node_on_construct(p, n);
795 bool ServerEnvironment::removeNode(v3s16 p)
797 INodeDefManager *ndef = m_gamedef->ndef();
798 MapNode n_old = m_map->getNodeNoEx(p);
801 if (ndef->get(n_old).has_on_destruct)
802 m_script->node_on_destruct(p, n_old);
805 // This is slightly optimized compared to addNodeWithEvent(air)
806 if (!m_map->removeNodeWithEvent(p))
809 // Update active VoxelManipulator if a mapgen thread
810 m_map->updateVManip(p);
812 // Call post-destructor
813 if (ndef->get(n_old).has_after_destruct)
814 m_script->node_after_destruct(p, n_old);
816 // Air doesn't require constructor
820 bool ServerEnvironment::swapNode(v3s16 p, const MapNode &n)
822 if (!m_map->addNodeWithEvent(p, n, false))
825 // Update active VoxelManipulator if a mapgen thread
826 m_map->updateVManip(p);
831 std::set<u16> ServerEnvironment::getObjectsInsideRadius(v3f pos, float radius)
833 std::set<u16> objects;
834 for(std::map<u16, ServerActiveObject*>::iterator
835 i = m_active_objects.begin();
836 i != m_active_objects.end(); ++i)
838 ServerActiveObject* obj = i->second;
840 v3f objectpos = obj->getBasePosition();
841 if(objectpos.getDistanceFrom(pos) > radius)
848 void ServerEnvironment::clearAllObjects()
850 infostream<<"ServerEnvironment::clearAllObjects(): "
851 <<"Removing all active objects"<<std::endl;
852 std::list<u16> objects_to_remove;
853 for(std::map<u16, ServerActiveObject*>::iterator
854 i = m_active_objects.begin();
855 i != m_active_objects.end(); ++i)
857 ServerActiveObject* obj = i->second;
858 if(obj->getType() == ACTIVEOBJECT_TYPE_PLAYER)
861 // Delete static object if block is loaded
862 if(obj->m_static_exists){
863 MapBlock *block = m_map->getBlockNoCreateNoEx(obj->m_static_block);
865 block->m_static_objects.remove(id);
866 block->raiseModified(MOD_STATE_WRITE_NEEDED,
868 obj->m_static_exists = false;
871 // If known by some client, don't delete immediately
872 if(obj->m_known_by_count > 0){
873 obj->m_pending_deactivation = true;
874 obj->m_removed = true;
878 // Tell the object about removal
879 obj->removingFromEnvironment();
880 // Deregister in scripting api
881 m_script->removeObjectReference(obj);
883 // Delete active object
884 if(obj->environmentDeletes())
886 // Id to be removed from m_active_objects
887 objects_to_remove.push_back(id);
889 // Remove references from m_active_objects
890 for(std::list<u16>::iterator i = objects_to_remove.begin();
891 i != objects_to_remove.end(); ++i)
893 m_active_objects.erase(*i);
896 // Get list of loaded blocks
897 std::list<v3s16> loaded_blocks;
898 infostream<<"ServerEnvironment::clearAllObjects(): "
899 <<"Listing all loaded blocks"<<std::endl;
900 m_map->listAllLoadedBlocks(loaded_blocks);
901 infostream<<"ServerEnvironment::clearAllObjects(): "
902 <<"Done listing all loaded blocks: "
903 <<loaded_blocks.size()<<std::endl;
905 // Get list of loadable blocks
906 std::list<v3s16> loadable_blocks;
907 infostream<<"ServerEnvironment::clearAllObjects(): "
908 <<"Listing all loadable blocks"<<std::endl;
909 m_map->listAllLoadableBlocks(loadable_blocks);
910 infostream<<"ServerEnvironment::clearAllObjects(): "
911 <<"Done listing all loadable blocks: "
912 <<loadable_blocks.size()
913 <<", now clearing"<<std::endl;
915 // Grab a reference on each loaded block to avoid unloading it
916 for(std::list<v3s16>::iterator i = loaded_blocks.begin();
917 i != loaded_blocks.end(); ++i)
920 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
925 // Remove objects in all loadable blocks
926 u32 unload_interval = g_settings->getS32("max_clearobjects_extra_loaded_blocks");
927 unload_interval = MYMAX(unload_interval, 1);
928 u32 report_interval = loadable_blocks.size() / 10;
929 u32 num_blocks_checked = 0;
930 u32 num_blocks_cleared = 0;
931 u32 num_objs_cleared = 0;
932 for(std::list<v3s16>::iterator i = loadable_blocks.begin();
933 i != loadable_blocks.end(); ++i)
936 MapBlock *block = m_map->emergeBlock(p, false);
938 errorstream<<"ServerEnvironment::clearAllObjects(): "
939 <<"Failed to emerge block "<<PP(p)<<std::endl;
942 u32 num_stored = block->m_static_objects.m_stored.size();
943 u32 num_active = block->m_static_objects.m_active.size();
944 if(num_stored != 0 || num_active != 0){
945 block->m_static_objects.m_stored.clear();
946 block->m_static_objects.m_active.clear();
947 block->raiseModified(MOD_STATE_WRITE_NEEDED,
949 num_objs_cleared += num_stored + num_active;
950 num_blocks_cleared++;
952 num_blocks_checked++;
954 if(report_interval != 0 &&
955 num_blocks_checked % report_interval == 0){
956 float percent = 100.0 * (float)num_blocks_checked /
957 loadable_blocks.size();
958 infostream<<"ServerEnvironment::clearAllObjects(): "
959 <<"Cleared "<<num_objs_cleared<<" objects"
960 <<" in "<<num_blocks_cleared<<" blocks ("
961 <<percent<<"%)"<<std::endl;
963 if(num_blocks_checked % unload_interval == 0){
964 m_map->unloadUnreferencedBlocks();
967 m_map->unloadUnreferencedBlocks();
969 // Drop references that were added above
970 for(std::list<v3s16>::iterator i = loaded_blocks.begin();
971 i != loaded_blocks.end(); ++i)
974 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
979 infostream<<"ServerEnvironment::clearAllObjects(): "
980 <<"Finished: Cleared "<<num_objs_cleared<<" objects"
981 <<" in "<<num_blocks_cleared<<" blocks"<<std::endl;
984 void ServerEnvironment::step(float dtime)
986 DSTACK(__FUNCTION_NAME);
988 //TimeTaker timer("ServerEnv step");
990 /* Step time of day */
991 stepTimeOfDay(dtime);
994 // NOTE: This is kind of funny on a singleplayer game, but doesn't
995 // really matter that much.
996 m_recommended_send_interval = g_settings->getFloat("dedicated_server_step");
1002 m_game_time_fraction_counter += dtime;
1003 u32 inc_i = (u32)m_game_time_fraction_counter;
1004 m_game_time += inc_i;
1005 m_game_time_fraction_counter -= (float)inc_i;
1012 ScopeProfiler sp(g_profiler, "SEnv: handle players avg", SPT_AVG);
1013 for(std::list<Player*>::iterator i = m_players.begin();
1014 i != m_players.end(); ++i)
1016 Player *player = *i;
1018 // Ignore disconnected players
1019 if(player->peer_id == 0)
1023 player->move(dtime, this, 100*BS);
1028 Manage active block list
1030 if(m_active_blocks_management_interval.step(dtime, 2.0))
1032 ScopeProfiler sp(g_profiler, "SEnv: manage act. block list avg /2s", SPT_AVG);
1034 Get player block positions
1036 std::list<v3s16> players_blockpos;
1037 for(std::list<Player*>::iterator
1038 i = m_players.begin();
1039 i != m_players.end(); ++i)
1041 Player *player = *i;
1042 // Ignore disconnected players
1043 if(player->peer_id == 0)
1045 v3s16 blockpos = getNodeBlockPos(
1046 floatToInt(player->getPosition(), BS));
1047 players_blockpos.push_back(blockpos);
1051 Update list of active blocks, collecting changes
1053 const s16 active_block_range = g_settings->getS16("active_block_range");
1054 std::set<v3s16> blocks_removed;
1055 std::set<v3s16> blocks_added;
1056 m_active_blocks.update(players_blockpos, active_block_range,
1057 blocks_removed, blocks_added);
1060 Handle removed blocks
1063 // Convert active objects that are no more in active blocks to static
1064 deactivateFarObjects(false);
1066 for(std::set<v3s16>::iterator
1067 i = blocks_removed.begin();
1068 i != blocks_removed.end(); ++i)
1072 /* infostream<<"Server: Block " << PP(p)
1073 << " became inactive"<<std::endl; */
1075 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1079 // Set current time as timestamp (and let it set ChangedFlag)
1080 block->setTimestamp(m_game_time);
1087 for(std::set<v3s16>::iterator
1088 i = blocks_added.begin();
1089 i != blocks_added.end(); ++i)
1093 MapBlock *block = m_map->getBlockOrEmerge(p);
1095 m_active_blocks.m_list.erase(p);
1099 activateBlock(block);
1100 /* infostream<<"Server: Block " << PP(p)
1101 << " became active"<<std::endl; */
1106 Mess around in active blocks
1108 if(m_active_blocks_nodemetadata_interval.step(dtime, 1.0))
1110 ScopeProfiler sp(g_profiler, "SEnv: mess in act. blocks avg /1s", SPT_AVG);
1114 for(std::set<v3s16>::iterator
1115 i = m_active_blocks.m_list.begin();
1116 i != m_active_blocks.m_list.end(); ++i)
1120 /*infostream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
1121 <<") being handled"<<std::endl;*/
1123 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1127 // Reset block usage timer
1128 block->resetUsageTimer();
1130 // Set current time as timestamp
1131 block->setTimestampNoChangedFlag(m_game_time);
1132 // If time has changed much from the one on disk,
1133 // set block to be saved when it is unloaded
1134 if(block->getTimestamp() > block->getDiskTimestamp() + 60)
1135 block->raiseModified(MOD_STATE_WRITE_AT_UNLOAD,
1136 "Timestamp older than 60s (step)");
1139 std::map<v3s16, NodeTimer> elapsed_timers =
1140 block->m_node_timers.step((float)dtime);
1141 if(!elapsed_timers.empty()){
1143 for(std::map<v3s16, NodeTimer>::iterator
1144 i = elapsed_timers.begin();
1145 i != elapsed_timers.end(); i++){
1146 n = block->getNodeNoEx(i->first);
1147 p = i->first + block->getPosRelative();
1148 if(m_script->node_on_timer(p,n,i->second.elapsed))
1149 block->setNodeTimer(i->first,NodeTimer(i->second.timeout,0));
1155 const float abm_interval = 1.0;
1156 if(m_active_block_modifier_interval.step(dtime, abm_interval))
1158 if(m_active_block_interval_overload_skip > 0){
1159 ScopeProfiler sp(g_profiler, "SEnv: ABM overload skips");
1160 m_active_block_interval_overload_skip--;
1163 ScopeProfiler sp(g_profiler, "SEnv: modify in blocks avg /1s", SPT_AVG);
1164 TimeTaker timer("modify in active blocks");
1166 // Initialize handling of ActiveBlockModifiers
1167 ABMHandler abmhandler(m_abms, abm_interval, this, true);
1169 for(std::set<v3s16>::iterator
1170 i = m_active_blocks.m_list.begin();
1171 i != m_active_blocks.m_list.end(); ++i)
1175 /*infostream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
1176 <<") being handled"<<std::endl;*/
1178 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1182 // Set current time as timestamp
1183 block->setTimestampNoChangedFlag(m_game_time);
1185 /* Handle ActiveBlockModifiers */
1186 abmhandler.apply(block);
1189 u32 time_ms = timer.stop(true);
1190 u32 max_time_ms = 200;
1191 if(time_ms > max_time_ms){
1192 infostream<<"WARNING: active block modifiers took "
1193 <<time_ms<<"ms (longer than "
1194 <<max_time_ms<<"ms)"<<std::endl;
1195 m_active_block_interval_overload_skip = (time_ms / max_time_ms) + 1;
1200 Step script environment (run global on_step())
1202 m_script->environment_Step(dtime);
1208 ScopeProfiler sp(g_profiler, "SEnv: step act. objs avg", SPT_AVG);
1209 //TimeTaker timer("Step active objects");
1211 g_profiler->avg("SEnv: num of objects", m_active_objects.size());
1213 // This helps the objects to send data at the same time
1214 bool send_recommended = false;
1215 m_send_recommended_timer += dtime;
1216 if(m_send_recommended_timer > getSendRecommendedInterval())
1218 m_send_recommended_timer -= getSendRecommendedInterval();
1219 send_recommended = true;
1222 for(std::map<u16, ServerActiveObject*>::iterator
1223 i = m_active_objects.begin();
1224 i != m_active_objects.end(); ++i)
1226 ServerActiveObject* obj = i->second;
1227 // Remove non-peaceful mobs on peaceful mode
1228 if(g_settings->getBool("only_peaceful_mobs")){
1229 if(!obj->isPeaceful())
1230 obj->m_removed = true;
1232 // Don't step if is to be removed or stored statically
1233 if(obj->m_removed || obj->m_pending_deactivation)
1236 obj->step(dtime, send_recommended);
1237 // Read messages from object
1238 while(!obj->m_messages_out.empty())
1240 m_active_object_messages.push_back(
1241 obj->m_messages_out.pop_front());
1247 Manage active objects
1249 if(m_object_management_interval.step(dtime, 0.5))
1251 ScopeProfiler sp(g_profiler, "SEnv: remove removed objs avg /.5s", SPT_AVG);
1253 Remove objects that satisfy (m_removed && m_known_by_count==0)
1255 removeRemovedObjects();
1259 ServerActiveObject* ServerEnvironment::getActiveObject(u16 id)
1261 std::map<u16, ServerActiveObject*>::iterator n;
1262 n = m_active_objects.find(id);
1263 if(n == m_active_objects.end())
1268 bool isFreeServerActiveObjectId(u16 id,
1269 std::map<u16, ServerActiveObject*> &objects)
1274 return objects.find(id) == objects.end();
1277 u16 getFreeServerActiveObjectId(
1278 std::map<u16, ServerActiveObject*> &objects)
1280 //try to reuse id's as late as possible
1281 static u16 last_used_id = 0;
1282 u16 startid = last_used_id;
1286 if(isFreeServerActiveObjectId(last_used_id, objects))
1287 return last_used_id;
1289 if(last_used_id == startid)
1294 u16 ServerEnvironment::addActiveObject(ServerActiveObject *object)
1298 u16 id = addActiveObjectRaw(object, true, 0);
1303 bool ServerEnvironment::addActiveObjectAsStatic(ServerActiveObject *obj)
1307 v3f objectpos = obj->getBasePosition();
1309 // The block in which the object resides in
1310 v3s16 blockpos_o = getNodeBlockPos(floatToInt(objectpos, BS));
1313 Update the static data
1316 // Create new static object
1317 std::string staticdata = obj->getStaticData();
1318 StaticObject s_obj(obj->getType(), objectpos, staticdata);
1319 // Add to the block where the object is located in
1320 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1321 // Get or generate the block
1322 MapBlock *block = m_map->emergeBlock(blockpos);
1324 bool succeeded = false;
1328 block->m_static_objects.insert(0, s_obj);
1329 block->raiseModified(MOD_STATE_WRITE_AT_UNLOAD,
1330 "addActiveObjectAsStatic");
1334 infostream<<"ServerEnvironment::addActiveObjectAsStatic: "
1335 <<"Could not find or generate "
1336 <<"a block for storing static object"<<std::endl;
1340 if(obj->environmentDeletes())
1348 Finds out what new objects have been added to
1349 inside a radius around a position
1351 void ServerEnvironment::getAddedActiveObjects(v3s16 pos, s16 radius,
1352 std::set<u16> ¤t_objects,
1353 std::set<u16> &added_objects)
1355 v3f pos_f = intToFloat(pos, BS);
1356 f32 radius_f = radius * BS;
1358 Go through the object list,
1359 - discard m_removed objects,
1360 - discard objects that are too far away,
1361 - discard objects that are found in current_objects.
1362 - add remaining objects to added_objects
1364 for(std::map<u16, ServerActiveObject*>::iterator
1365 i = m_active_objects.begin();
1366 i != m_active_objects.end(); ++i)
1370 ServerActiveObject *object = i->second;
1373 // Discard if removed or deactivating
1374 if(object->m_removed || object->m_pending_deactivation)
1376 if(object->unlimitedTransferDistance() == false){
1377 // Discard if too far
1378 f32 distance_f = object->getBasePosition().getDistanceFrom(pos_f);
1379 if(distance_f > radius_f)
1382 // Discard if already on current_objects
1383 std::set<u16>::iterator n;
1384 n = current_objects.find(id);
1385 if(n != current_objects.end())
1387 // Add to added_objects
1388 added_objects.insert(id);
1393 Finds out what objects have been removed from
1394 inside a radius around a position
1396 void ServerEnvironment::getRemovedActiveObjects(v3s16 pos, s16 radius,
1397 std::set<u16> ¤t_objects,
1398 std::set<u16> &removed_objects)
1400 v3f pos_f = intToFloat(pos, BS);
1401 f32 radius_f = radius * BS;
1403 Go through current_objects; object is removed if:
1404 - object is not found in m_active_objects (this is actually an
1405 error condition; objects should be set m_removed=true and removed
1406 only after all clients have been informed about removal), or
1407 - object has m_removed=true, or
1408 - object is too far away
1410 for(std::set<u16>::iterator
1411 i = current_objects.begin();
1412 i != current_objects.end(); ++i)
1415 ServerActiveObject *object = getActiveObject(id);
1418 infostream<<"ServerEnvironment::getRemovedActiveObjects():"
1419 <<" object in current_objects is NULL"<<std::endl;
1420 removed_objects.insert(id);
1424 if(object->m_removed || object->m_pending_deactivation)
1426 removed_objects.insert(id);
1430 // If transfer distance is unlimited, don't remove
1431 if(object->unlimitedTransferDistance())
1434 f32 distance_f = object->getBasePosition().getDistanceFrom(pos_f);
1436 if(distance_f >= radius_f)
1438 removed_objects.insert(id);
1446 ActiveObjectMessage ServerEnvironment::getActiveObjectMessage()
1448 if(m_active_object_messages.empty())
1449 return ActiveObjectMessage(0);
1451 ActiveObjectMessage message = m_active_object_messages.front();
1452 m_active_object_messages.pop_front();
1457 ************ Private methods *************
1460 u16 ServerEnvironment::addActiveObjectRaw(ServerActiveObject *object,
1461 bool set_changed, u32 dtime_s)
1464 if(object->getId() == 0){
1465 u16 new_id = getFreeServerActiveObjectId(m_active_objects);
1468 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1469 <<"no free ids available"<<std::endl;
1470 if(object->environmentDeletes())
1474 object->setId(new_id);
1477 verbosestream<<"ServerEnvironment::addActiveObjectRaw(): "
1478 <<"supplied with id "<<object->getId()<<std::endl;
1480 if(isFreeServerActiveObjectId(object->getId(), m_active_objects) == false)
1482 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1483 <<"id is not free ("<<object->getId()<<")"<<std::endl;
1484 if(object->environmentDeletes())
1488 /*infostream<<"ServerEnvironment::addActiveObjectRaw(): "
1489 <<"added (id="<<object->getId()<<")"<<std::endl;*/
1491 m_active_objects[object->getId()] = object;
1493 verbosestream<<"ServerEnvironment::addActiveObjectRaw(): "
1494 <<"Added id="<<object->getId()<<"; there are now "
1495 <<m_active_objects.size()<<" active objects."
1498 // Register reference in scripting api (must be done before post-init)
1499 m_script->addObjectReference(object);
1500 // Post-initialize object
1501 object->addedToEnvironment(dtime_s);
1503 // Add static data to block
1504 if(object->isStaticAllowed())
1506 // Add static object to active static list of the block
1507 v3f objectpos = object->getBasePosition();
1508 std::string staticdata = object->getStaticData();
1509 StaticObject s_obj(object->getType(), objectpos, staticdata);
1510 // Add to the block where the object is located in
1511 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1512 MapBlock *block = m_map->emergeBlock(blockpos);
1514 block->m_static_objects.m_active[object->getId()] = s_obj;
1515 object->m_static_exists = true;
1516 object->m_static_block = blockpos;
1519 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1520 "addActiveObjectRaw");
1522 v3s16 p = floatToInt(objectpos, BS);
1523 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1524 <<"could not emerge block for storing id="<<object->getId()
1525 <<" statically (pos="<<PP(p)<<")"<<std::endl;
1529 return object->getId();
1533 Remove objects that satisfy (m_removed && m_known_by_count==0)
1535 void ServerEnvironment::removeRemovedObjects()
1537 std::list<u16> objects_to_remove;
1538 for(std::map<u16, ServerActiveObject*>::iterator
1539 i = m_active_objects.begin();
1540 i != m_active_objects.end(); ++i)
1543 ServerActiveObject* obj = i->second;
1544 // This shouldn't happen but check it
1547 infostream<<"NULL object found in ServerEnvironment"
1548 <<" while finding removed objects. id="<<id<<std::endl;
1549 // Id to be removed from m_active_objects
1550 objects_to_remove.push_back(id);
1555 We will delete objects that are marked as removed or thatare
1556 waiting for deletion after deactivation
1558 if(obj->m_removed == false && obj->m_pending_deactivation == false)
1562 Delete static data from block if is marked as removed
1564 if(obj->m_static_exists && obj->m_removed)
1566 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
1568 block->m_static_objects.remove(id);
1569 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1570 "removeRemovedObjects/remove");
1571 obj->m_static_exists = false;
1573 infostream<<"Failed to emerge block from which an object to "
1574 <<"be removed was loaded from. id="<<id<<std::endl;
1578 // If m_known_by_count > 0, don't actually remove. On some future
1579 // invocation this will be 0, which is when removal will continue.
1580 if(obj->m_known_by_count > 0)
1584 Move static data from active to stored if not marked as removed
1586 if(obj->m_static_exists && !obj->m_removed){
1587 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
1589 std::map<u16, StaticObject>::iterator i =
1590 block->m_static_objects.m_active.find(id);
1591 if(i != block->m_static_objects.m_active.end()){
1592 block->m_static_objects.m_stored.push_back(i->second);
1593 block->m_static_objects.m_active.erase(id);
1594 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1595 "removeRemovedObjects/deactivate");
1598 infostream<<"Failed to emerge block from which an object to "
1599 <<"be deactivated was loaded from. id="<<id<<std::endl;
1603 // Tell the object about removal
1604 obj->removingFromEnvironment();
1605 // Deregister in scripting api
1606 m_script->removeObjectReference(obj);
1609 if(obj->environmentDeletes())
1611 // Id to be removed from m_active_objects
1612 objects_to_remove.push_back(id);
1614 // Remove references from m_active_objects
1615 for(std::list<u16>::iterator i = objects_to_remove.begin();
1616 i != objects_to_remove.end(); ++i)
1618 m_active_objects.erase(*i);
1622 static void print_hexdump(std::ostream &o, const std::string &data)
1624 const int linelength = 16;
1625 for(int l=0; ; l++){
1626 int i0 = linelength * l;
1627 bool at_end = false;
1628 int thislinelength = linelength;
1629 if(i0 + thislinelength > (int)data.size()){
1630 thislinelength = data.size() - i0;
1633 for(int di=0; di<linelength; di++){
1636 if(di<thislinelength)
1637 snprintf(buf, 4, "%.2x ", data[i]);
1639 snprintf(buf, 4, " ");
1643 for(int di=0; di<thislinelength; di++){
1657 Convert stored objects from blocks near the players to active.
1659 void ServerEnvironment::activateObjects(MapBlock *block, u32 dtime_s)
1663 // Ignore if no stored objects (to not set changed flag)
1664 if(block->m_static_objects.m_stored.size() == 0)
1666 verbosestream<<"ServerEnvironment::activateObjects(): "
1667 <<"activating objects of block "<<PP(block->getPos())
1668 <<" ("<<block->m_static_objects.m_stored.size()
1669 <<" objects)"<<std::endl;
1670 bool large_amount = (block->m_static_objects.m_stored.size() > g_settings->getU16("max_objects_per_block"));
1672 errorstream<<"suspiciously large amount of objects detected: "
1673 <<block->m_static_objects.m_stored.size()<<" in "
1674 <<PP(block->getPos())
1675 <<"; removing all of them."<<std::endl;
1676 // Clear stored list
1677 block->m_static_objects.m_stored.clear();
1678 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1679 "stored list cleared in activateObjects due to "
1680 "large amount of objects");
1684 // Activate stored objects
1685 std::list<StaticObject> new_stored;
1686 for(std::list<StaticObject>::iterator
1687 i = block->m_static_objects.m_stored.begin();
1688 i != block->m_static_objects.m_stored.end(); ++i)
1690 /*infostream<<"Server: Creating an active object from "
1691 <<"static data"<<std::endl;*/
1692 StaticObject &s_obj = *i;
1693 // Create an active object from the data
1694 ServerActiveObject *obj = ServerActiveObject::create
1695 (s_obj.type, this, 0, s_obj.pos, s_obj.data);
1696 // If couldn't create object, store static data back.
1699 errorstream<<"ServerEnvironment::activateObjects(): "
1700 <<"failed to create active object from static object "
1701 <<"in block "<<PP(s_obj.pos/BS)
1702 <<" type="<<(int)s_obj.type<<" data:"<<std::endl;
1703 print_hexdump(verbosestream, s_obj.data);
1705 new_stored.push_back(s_obj);
1708 verbosestream<<"ServerEnvironment::activateObjects(): "
1709 <<"activated static object pos="<<PP(s_obj.pos/BS)
1710 <<" type="<<(int)s_obj.type<<std::endl;
1711 // This will also add the object to the active static list
1712 addActiveObjectRaw(obj, false, dtime_s);
1714 // Clear stored list
1715 block->m_static_objects.m_stored.clear();
1716 // Add leftover failed stuff to stored list
1717 for(std::list<StaticObject>::iterator
1718 i = new_stored.begin();
1719 i != new_stored.end(); ++i)
1721 StaticObject &s_obj = *i;
1722 block->m_static_objects.m_stored.push_back(s_obj);
1725 // Turn the active counterparts of activated objects not pending for
1727 for(std::map<u16, StaticObject>::iterator
1728 i = block->m_static_objects.m_active.begin();
1729 i != block->m_static_objects.m_active.end(); ++i)
1732 ServerActiveObject *object = getActiveObject(id);
1734 object->m_pending_deactivation = false;
1738 Note: Block hasn't really been modified here.
1739 The objects have just been activated and moved from the stored
1740 static list to the active static list.
1741 As such, the block is essentially the same.
1742 Thus, do not call block->raiseModified(MOD_STATE_WRITE_NEEDED).
1743 Otherwise there would be a huge amount of unnecessary I/O.
1748 Convert objects that are not standing inside active blocks to static.
1750 If m_known_by_count != 0, active object is not deleted, but static
1751 data is still updated.
1753 If force_delete is set, active object is deleted nevertheless. It
1754 shall only be set so in the destructor of the environment.
1756 If block wasn't generated (not in memory or on disk),
1758 void ServerEnvironment::deactivateFarObjects(bool force_delete)
1760 std::list<u16> objects_to_remove;
1761 for(std::map<u16, ServerActiveObject*>::iterator
1762 i = m_active_objects.begin();
1763 i != m_active_objects.end(); ++i)
1765 ServerActiveObject* obj = i->second;
1768 // Do not deactivate if static data creation not allowed
1769 if(!force_delete && !obj->isStaticAllowed())
1772 // If pending deactivation, let removeRemovedObjects() do it
1773 if(!force_delete && obj->m_pending_deactivation)
1777 v3f objectpos = obj->getBasePosition();
1779 // The block in which the object resides in
1780 v3s16 blockpos_o = getNodeBlockPos(floatToInt(objectpos, BS));
1782 // If object's static data is stored in a deactivated block and object
1783 // is actually located in an active block, re-save to the block in
1784 // which the object is actually located in.
1786 obj->m_static_exists &&
1787 !m_active_blocks.contains(obj->m_static_block) &&
1788 m_active_blocks.contains(blockpos_o))
1790 v3s16 old_static_block = obj->m_static_block;
1792 // Save to block where object is located
1793 MapBlock *block = m_map->emergeBlock(blockpos_o, false);
1795 errorstream<<"ServerEnvironment::deactivateFarObjects(): "
1796 <<"Could not save object id="<<id
1797 <<" to it's current block "<<PP(blockpos_o)
1801 std::string staticdata_new = obj->getStaticData();
1802 StaticObject s_obj(obj->getType(), objectpos, staticdata_new);
1803 block->m_static_objects.insert(id, s_obj);
1804 obj->m_static_block = blockpos_o;
1805 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1806 "deactivateFarObjects: Static data moved in");
1808 // Delete from block where object was located
1809 block = m_map->emergeBlock(old_static_block, false);
1811 errorstream<<"ServerEnvironment::deactivateFarObjects(): "
1812 <<"Could not delete object id="<<id
1813 <<" from it's previous block "<<PP(old_static_block)
1817 block->m_static_objects.remove(id);
1818 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1819 "deactivateFarObjects: Static data moved out");
1823 // If block is active, don't remove
1824 if(!force_delete && m_active_blocks.contains(blockpos_o))
1827 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
1828 <<"deactivating object id="<<id<<" on inactive block "
1829 <<PP(blockpos_o)<<std::endl;
1831 // If known by some client, don't immediately delete.
1832 bool pending_delete = (obj->m_known_by_count > 0 && !force_delete);
1835 Update the static data
1838 if(obj->isStaticAllowed())
1840 // Create new static object
1841 std::string staticdata_new = obj->getStaticData();
1842 StaticObject s_obj(obj->getType(), objectpos, staticdata_new);
1844 bool stays_in_same_block = false;
1845 bool data_changed = true;
1847 if(obj->m_static_exists){
1848 if(obj->m_static_block == blockpos_o)
1849 stays_in_same_block = true;
1851 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
1853 std::map<u16, StaticObject>::iterator n =
1854 block->m_static_objects.m_active.find(id);
1855 if(n != block->m_static_objects.m_active.end()){
1856 StaticObject static_old = n->second;
1858 float save_movem = obj->getMinimumSavedMovement();
1860 if(static_old.data == staticdata_new &&
1861 (static_old.pos - objectpos).getLength() < save_movem)
1862 data_changed = false;
1864 errorstream<<"ServerEnvironment::deactivateFarObjects(): "
1865 <<"id="<<id<<" m_static_exists=true but "
1866 <<"static data doesn't actually exist in "
1867 <<PP(obj->m_static_block)<<std::endl;
1871 bool shall_be_written = (!stays_in_same_block || data_changed);
1873 // Delete old static object
1874 if(obj->m_static_exists)
1876 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
1879 block->m_static_objects.remove(id);
1880 obj->m_static_exists = false;
1881 // Only mark block as modified if data changed considerably
1882 if(shall_be_written)
1883 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1884 "deactivateFarObjects: Static data "
1885 "changed considerably");
1889 // Add to the block where the object is located in
1890 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1891 // Get or generate the block
1892 MapBlock *block = NULL;
1894 block = m_map->emergeBlock(blockpos);
1895 } catch(InvalidPositionException &e){
1896 // Handled via NULL pointer
1897 // NOTE: emergeBlock's failure is usually determined by it
1898 // actually returning NULL
1903 if(block->m_static_objects.m_stored.size() >= g_settings->getU16("max_objects_per_block")){
1904 errorstream<<"ServerEnv: Trying to store id="<<obj->getId()
1905 <<" statically but block "<<PP(blockpos)
1906 <<" already contains "
1907 <<block->m_static_objects.m_stored.size()
1909 <<" Forcing delete."<<std::endl;
1910 force_delete = true;
1912 // If static counterpart already exists in target block,
1914 // This shouldn't happen because the object is removed from
1915 // the previous block before this according to
1916 // obj->m_static_block, but happens rarely for some unknown
1917 // reason. Unsuccessful attempts have been made to find
1919 if(id && block->m_static_objects.m_active.find(id) != block->m_static_objects.m_active.end()){
1920 infostream<<"ServerEnv: WARNING: Performing hack #83274"
1922 block->m_static_objects.remove(id);
1924 // Store static data
1925 u16 store_id = pending_delete ? id : 0;
1926 block->m_static_objects.insert(store_id, s_obj);
1928 // Only mark block as modified if data changed considerably
1929 if(shall_be_written)
1930 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1931 "deactivateFarObjects: Static data "
1932 "changed considerably");
1934 obj->m_static_exists = true;
1935 obj->m_static_block = block->getPos();
1940 v3s16 p = floatToInt(objectpos, BS);
1941 errorstream<<"ServerEnv: Could not find or generate "
1942 <<"a block for storing id="<<obj->getId()
1943 <<" statically (pos="<<PP(p)<<")"<<std::endl;
1950 If known by some client, set pending deactivation.
1951 Otherwise delete it immediately.
1954 if(pending_delete && !force_delete)
1956 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
1957 <<"object id="<<id<<" is known by clients"
1958 <<"; not deleting yet"<<std::endl;
1960 obj->m_pending_deactivation = true;
1964 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
1965 <<"object id="<<id<<" is not known by clients"
1966 <<"; deleting"<<std::endl;
1968 // Tell the object about removal
1969 obj->removingFromEnvironment();
1970 // Deregister in scripting api
1971 m_script->removeObjectReference(obj);
1973 // Delete active object
1974 if(obj->environmentDeletes())
1976 // Id to be removed from m_active_objects
1977 objects_to_remove.push_back(id);
1980 // Remove references from m_active_objects
1981 for(std::list<u16>::iterator i = objects_to_remove.begin();
1982 i != objects_to_remove.end(); ++i)
1984 m_active_objects.erase(*i);
1991 #include "clientsimpleobject.h"
1997 ClientEnvironment::ClientEnvironment(ClientMap *map, scene::ISceneManager *smgr,
1998 ITextureSource *texturesource, IGameDef *gamedef,
1999 IrrlichtDevice *irr):
2002 m_texturesource(texturesource),
2007 memset(m_attachements, zero, sizeof(m_attachements));
2010 ClientEnvironment::~ClientEnvironment()
2012 // delete active objects
2013 for(std::map<u16, ClientActiveObject*>::iterator
2014 i = m_active_objects.begin();
2015 i != m_active_objects.end(); ++i)
2020 for(std::list<ClientSimpleObject*>::iterator
2021 i = m_simple_objects.begin(); i != m_simple_objects.end(); ++i)
2030 Map & ClientEnvironment::getMap()
2035 ClientMap & ClientEnvironment::getClientMap()
2040 void ClientEnvironment::addPlayer(Player *player)
2042 DSTACK(__FUNCTION_NAME);
2044 It is a failure if player is local and there already is a local
2047 assert(!(player->isLocal() == true && getLocalPlayer() != NULL));
2049 Environment::addPlayer(player);
2052 LocalPlayer * ClientEnvironment::getLocalPlayer()
2054 for(std::list<Player*>::iterator i = m_players.begin();
2055 i != m_players.end(); ++i)
2057 Player *player = *i;
2058 if(player->isLocal())
2059 return (LocalPlayer*)player;
2064 void ClientEnvironment::step(float dtime)
2066 DSTACK(__FUNCTION_NAME);
2068 /* Step time of day */
2069 stepTimeOfDay(dtime);
2071 // Get some settings
2072 bool fly_allowed = m_gamedef->checkLocalPrivilege("fly");
2073 bool free_move = fly_allowed && g_settings->getBool("free_move");
2076 LocalPlayer *lplayer = getLocalPlayer();
2078 // collision info queue
2079 std::list<CollisionInfo> player_collisions;
2082 Get the speed the player is going
2084 bool is_climbing = lplayer->is_climbing;
2086 f32 player_speed = lplayer->getSpeed().getLength();
2089 Maximum position increment
2091 //f32 position_max_increment = 0.05*BS;
2092 f32 position_max_increment = 0.1*BS;
2094 // Maximum time increment (for collision detection etc)
2095 // time = distance / speed
2096 f32 dtime_max_increment = 1;
2097 if(player_speed > 0.001)
2098 dtime_max_increment = position_max_increment / player_speed;
2100 // Maximum time increment is 10ms or lower
2101 if(dtime_max_increment > 0.01)
2102 dtime_max_increment = 0.01;
2104 // Don't allow overly huge dtime
2108 f32 dtime_downcount = dtime;
2111 Stuff that has a maximum time increment
2120 if(dtime_downcount > dtime_max_increment)
2122 dtime_part = dtime_max_increment;
2123 dtime_downcount -= dtime_part;
2127 dtime_part = dtime_downcount;
2129 Setting this to 0 (no -=dtime_part) disables an infinite loop
2130 when dtime_part is so small that dtime_downcount -= dtime_part
2133 dtime_downcount = 0;
2142 if(free_move == false && is_climbing == false)
2145 v3f speed = lplayer->getSpeed();
2146 if(lplayer->in_liquid == false)
2147 speed.Y -= lplayer->movement_gravity * lplayer->physics_override_gravity * dtime_part * 2;
2149 // Liquid floating / sinking
2150 if(lplayer->in_liquid && !lplayer->swimming_vertical)
2151 speed.Y -= lplayer->movement_liquid_sink * dtime_part * 2;
2153 // Liquid resistance
2154 if(lplayer->in_liquid_stable || lplayer->in_liquid)
2156 // How much the node's viscosity blocks movement, ranges between 0 and 1
2157 // Should match the scale at which viscosity increase affects other liquid attributes
2158 const f32 viscosity_factor = 0.3;
2160 v3f d_wanted = -speed / lplayer->movement_liquid_fluidity;
2161 f32 dl = d_wanted.getLength();
2162 if(dl > lplayer->movement_liquid_fluidity_smooth)
2163 dl = lplayer->movement_liquid_fluidity_smooth;
2164 dl *= (lplayer->liquid_viscosity * viscosity_factor) + (1 - viscosity_factor);
2166 v3f d = d_wanted.normalize() * dl;
2170 if(speed.X > lplayer->movement_liquid_fluidity + lplayer->movement_liquid_fluidity_smooth) speed.X -= lplayer->movement_liquid_fluidity_smooth;
2171 if(speed.X < -lplayer->movement_liquid_fluidity - lplayer->movement_liquid_fluidity_smooth) speed.X += lplayer->movement_liquid_fluidity_smooth;
2172 if(speed.Y > lplayer->movement_liquid_fluidity + lplayer->movement_liquid_fluidity_smooth) speed.Y -= lplayer->movement_liquid_fluidity_smooth;
2173 if(speed.Y < -lplayer->movement_liquid_fluidity - lplayer->movement_liquid_fluidity_smooth) speed.Y += lplayer->movement_liquid_fluidity_smooth;
2174 if(speed.Z > lplayer->movement_liquid_fluidity + lplayer->movement_liquid_fluidity_smooth) speed.Z -= lplayer->movement_liquid_fluidity_smooth;
2175 if(speed.Z < -lplayer->movement_liquid_fluidity - lplayer->movement_liquid_fluidity_smooth) speed.Z += lplayer->movement_liquid_fluidity_smooth;
2179 lplayer->setSpeed(speed);
2184 This also does collision detection.
2186 lplayer->move(dtime_part, this, position_max_increment,
2187 &player_collisions);
2190 while(dtime_downcount > 0.001);
2192 //std::cout<<"Looped "<<loopcount<<" times."<<std::endl;
2194 for(std::list<CollisionInfo>::iterator
2195 i = player_collisions.begin();
2196 i != player_collisions.end(); ++i)
2198 CollisionInfo &info = *i;
2199 v3f speed_diff = info.new_speed - info.old_speed;;
2200 // Handle only fall damage
2201 // (because otherwise walking against something in fast_move kills you)
2202 if(speed_diff.Y < 0 || info.old_speed.Y >= 0)
2204 // Get rid of other components
2207 f32 pre_factor = 1; // 1 hp per node/s
2208 f32 tolerance = BS*14; // 5 without damage
2209 f32 post_factor = 1; // 1 hp per node/s
2210 if(info.type == COLLISION_NODE)
2212 const ContentFeatures &f = m_gamedef->ndef()->
2213 get(m_map->getNodeNoEx(info.node_p));
2214 // Determine fall damage multiplier
2215 int addp = itemgroup_get(f.groups, "fall_damage_add_percent");
2216 pre_factor = 1.0 + (float)addp/100.0;
2218 float speed = pre_factor * speed_diff.getLength();
2219 if(speed > tolerance)
2221 f32 damage_f = (speed - tolerance)/BS * post_factor;
2222 u16 damage = (u16)(damage_f+0.5);
2224 damageLocalPlayer(damage, true);
2225 MtEvent *e = new SimpleTriggerEvent("PlayerFallingDamage");
2226 m_gamedef->event()->put(e);
2232 A quick draft of lava damage
2234 if(m_lava_hurt_interval.step(dtime, 1.0))
2236 v3f pf = lplayer->getPosition();
2238 // Feet, middle and head
2239 v3s16 p1 = floatToInt(pf + v3f(0, BS*0.1, 0), BS);
2240 MapNode n1 = m_map->getNodeNoEx(p1);
2241 v3s16 p2 = floatToInt(pf + v3f(0, BS*0.8, 0), BS);
2242 MapNode n2 = m_map->getNodeNoEx(p2);
2243 v3s16 p3 = floatToInt(pf + v3f(0, BS*1.6, 0), BS);
2244 MapNode n3 = m_map->getNodeNoEx(p3);
2246 u32 damage_per_second = 0;
2247 damage_per_second = MYMAX(damage_per_second,
2248 m_gamedef->ndef()->get(n1).damage_per_second);
2249 damage_per_second = MYMAX(damage_per_second,
2250 m_gamedef->ndef()->get(n2).damage_per_second);
2251 damage_per_second = MYMAX(damage_per_second,
2252 m_gamedef->ndef()->get(n3).damage_per_second);
2254 if(damage_per_second != 0)
2256 damageLocalPlayer(damage_per_second, true);
2263 if(m_drowning_interval.step(dtime, 2.0))
2265 v3f pf = lplayer->getPosition();
2268 v3s16 p = floatToInt(pf + v3f(0, BS*1.6, 0), BS);
2269 MapNode n = m_map->getNodeNoEx(p);
2270 ContentFeatures c = m_gamedef->ndef()->get(n);
2271 u8 drowning_damage = c.drowning;
2272 if(drowning_damage > 0 && lplayer->hp > 0){
2273 u16 breath = lplayer->getBreath();
2280 lplayer->setBreath(breath);
2281 updateLocalPlayerBreath(breath);
2284 if(lplayer->getBreath() == 0 && drowning_damage > 0){
2285 damageLocalPlayer(drowning_damage, true);
2288 if(m_breathing_interval.step(dtime, 0.5))
2290 v3f pf = lplayer->getPosition();
2293 v3s16 p = floatToInt(pf + v3f(0, BS*1.6, 0), BS);
2294 MapNode n = m_map->getNodeNoEx(p);
2295 ContentFeatures c = m_gamedef->ndef()->get(n);
2297 lplayer->setBreath(11);
2299 else if(c.drowning == 0){
2300 u16 breath = lplayer->getBreath();
2303 lplayer->setBreath(breath);
2304 updateLocalPlayerBreath(breath);
2310 Stuff that can be done in an arbitarily large dtime
2312 for(std::list<Player*>::iterator i = m_players.begin();
2313 i != m_players.end(); ++i)
2315 Player *player = *i;
2318 Handle non-local players
2320 if(player->isLocal() == false)
2323 player->move(dtime, this, 100*BS);
2327 // Update lighting on all players on client
2331 v3s16 p = player->getLightPosition();
2332 MapNode n = m_map->getNode(p);
2333 light = n.getLightBlendF1((float)getDayNightRatio()/1000, m_gamedef->ndef());
2335 catch(InvalidPositionException &e){
2336 light = blend_light_f1((float)getDayNightRatio()/1000, LIGHT_SUN, 0);
2338 player->light = light;
2342 Step active objects and update lighting of them
2345 g_profiler->avg("CEnv: num of objects", m_active_objects.size());
2346 bool update_lighting = m_active_object_light_update_interval.step(dtime, 0.21);
2347 for(std::map<u16, ClientActiveObject*>::iterator
2348 i = m_active_objects.begin();
2349 i != m_active_objects.end(); ++i)
2351 ClientActiveObject* obj = i->second;
2353 obj->step(dtime, this);
2361 v3s16 p = obj->getLightPosition();
2362 MapNode n = m_map->getNode(p);
2363 light = n.getLightBlend(getDayNightRatio(), m_gamedef->ndef());
2365 catch(InvalidPositionException &e){
2366 light = blend_light(getDayNightRatio(), LIGHT_SUN, 0);
2368 obj->updateLight(light);
2373 Step and handle simple objects
2375 g_profiler->avg("CEnv: num of simple objects", m_simple_objects.size());
2376 for(std::list<ClientSimpleObject*>::iterator
2377 i = m_simple_objects.begin(); i != m_simple_objects.end();)
2379 ClientSimpleObject *simple = *i;
2380 std::list<ClientSimpleObject*>::iterator cur = i;
2382 simple->step(dtime);
2383 if(simple->m_to_be_removed){
2385 m_simple_objects.erase(cur);
2390 void ClientEnvironment::addSimpleObject(ClientSimpleObject *simple)
2392 m_simple_objects.push_back(simple);
2395 ClientActiveObject* ClientEnvironment::getActiveObject(u16 id)
2397 std::map<u16, ClientActiveObject*>::iterator n;
2398 n = m_active_objects.find(id);
2399 if(n == m_active_objects.end())
2404 bool isFreeClientActiveObjectId(u16 id,
2405 std::map<u16, ClientActiveObject*> &objects)
2410 return objects.find(id) == objects.end();
2413 u16 getFreeClientActiveObjectId(
2414 std::map<u16, ClientActiveObject*> &objects)
2416 //try to reuse id's as late as possible
2417 static u16 last_used_id = 0;
2418 u16 startid = last_used_id;
2422 if(isFreeClientActiveObjectId(last_used_id, objects))
2423 return last_used_id;
2425 if(last_used_id == startid)
2430 u16 ClientEnvironment::addActiveObject(ClientActiveObject *object)
2433 if(object->getId() == 0)
2435 u16 new_id = getFreeClientActiveObjectId(m_active_objects);
2438 infostream<<"ClientEnvironment::addActiveObject(): "
2439 <<"no free ids available"<<std::endl;
2443 object->setId(new_id);
2445 if(isFreeClientActiveObjectId(object->getId(), m_active_objects) == false)
2447 infostream<<"ClientEnvironment::addActiveObject(): "
2448 <<"id is not free ("<<object->getId()<<")"<<std::endl;
2452 infostream<<"ClientEnvironment::addActiveObject(): "
2453 <<"added (id="<<object->getId()<<")"<<std::endl;
2454 m_active_objects[object->getId()] = object;
2455 object->addToScene(m_smgr, m_texturesource, m_irr);
2456 { // Update lighting immediately
2460 v3s16 p = object->getLightPosition();
2461 MapNode n = m_map->getNode(p);
2462 light = n.getLightBlend(getDayNightRatio(), m_gamedef->ndef());
2464 catch(InvalidPositionException &e){
2465 light = blend_light(getDayNightRatio(), LIGHT_SUN, 0);
2467 object->updateLight(light);
2469 return object->getId();
2472 void ClientEnvironment::addActiveObject(u16 id, u8 type,
2473 const std::string &init_data)
2475 ClientActiveObject* obj =
2476 ClientActiveObject::create(type, m_gamedef, this);
2479 infostream<<"ClientEnvironment::addActiveObject(): "
2480 <<"id="<<id<<" type="<<type<<": Couldn't create object"
2489 obj->initialize(init_data);
2491 catch(SerializationError &e)
2493 errorstream<<"ClientEnvironment::addActiveObject():"
2494 <<" id="<<id<<" type="<<type
2495 <<": SerializationError in initialize(): "
2497 <<": init_data="<<serializeJsonString(init_data)
2501 addActiveObject(obj);
2504 void ClientEnvironment::removeActiveObject(u16 id)
2506 verbosestream<<"ClientEnvironment::removeActiveObject(): "
2507 <<"id="<<id<<std::endl;
2508 ClientActiveObject* obj = getActiveObject(id);
2511 infostream<<"ClientEnvironment::removeActiveObject(): "
2512 <<"id="<<id<<" not found"<<std::endl;
2515 obj->removeFromScene(true);
2517 m_active_objects.erase(id);
2520 void ClientEnvironment::processActiveObjectMessage(u16 id,
2521 const std::string &data)
2523 ClientActiveObject* obj = getActiveObject(id);
2526 infostream<<"ClientEnvironment::processActiveObjectMessage():"
2527 <<" got message for id="<<id<<", which doesn't exist."
2533 obj->processMessage(data);
2535 catch(SerializationError &e)
2537 errorstream<<"ClientEnvironment::processActiveObjectMessage():"
2538 <<" id="<<id<<" type="<<obj->getType()
2539 <<" SerializationError in processMessage(),"
2540 <<" message="<<serializeJsonString(data)
2546 Callbacks for activeobjects
2549 void ClientEnvironment::damageLocalPlayer(u8 damage, bool handle_hp)
2551 LocalPlayer *lplayer = getLocalPlayer();
2555 if(lplayer->hp > damage)
2556 lplayer->hp -= damage;
2561 ClientEnvEvent event;
2562 event.type = CEE_PLAYER_DAMAGE;
2563 event.player_damage.amount = damage;
2564 event.player_damage.send_to_server = handle_hp;
2565 m_client_event_queue.push_back(event);
2568 void ClientEnvironment::updateLocalPlayerBreath(u16 breath)
2570 ClientEnvEvent event;
2571 event.type = CEE_PLAYER_BREATH;
2572 event.player_breath.amount = breath;
2573 m_client_event_queue.push_back(event);
2577 Client likes to call these
2580 void ClientEnvironment::getActiveObjects(v3f origin, f32 max_d,
2581 std::vector<DistanceSortedActiveObject> &dest)
2583 for(std::map<u16, ClientActiveObject*>::iterator
2584 i = m_active_objects.begin();
2585 i != m_active_objects.end(); ++i)
2587 ClientActiveObject* obj = i->second;
2589 f32 d = (obj->getPosition() - origin).getLength();
2594 DistanceSortedActiveObject dso(obj, d);
2596 dest.push_back(dso);
2600 ClientEnvEvent ClientEnvironment::getClientEvent()
2602 ClientEnvEvent event;
2603 if(m_client_event_queue.empty())
2604 event.type = CEE_NONE;
2606 event = m_client_event_queue.front();
2607 m_client_event_queue.pop_front();
2612 #endif // #ifndef SERVER