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"
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::list<Player*>::iterator i = m_players.begin();
65 i != m_players.end(); ++i)
71 void Environment::addPlayer(Player *player)
73 DSTACK(__FUNCTION_NAME);
75 Check that peer_ids are unique.
76 Also check that names are unique.
77 Exception: there can be multiple players with peer_id=0
79 // If peer id is non-zero, it has to be unique.
80 if(player->peer_id != 0)
81 assert(getPlayer(player->peer_id) == NULL);
82 // Name has to be unique.
83 assert(getPlayer(player->getName()) == NULL);
85 m_players.push_back(player);
88 void Environment::removePlayer(u16 peer_id)
90 DSTACK(__FUNCTION_NAME);
92 for(std::list<Player*>::iterator i = m_players.begin();
93 i != m_players.end();)
96 if(player->peer_id == peer_id) {
98 i = m_players.erase(i);
105 void Environment::removePlayer(const char *name)
107 for (std::list<Player*>::iterator it = m_players.begin();
108 it != m_players.end(); ++it) {
109 if (strcmp((*it)->getName(), name) == 0) {
117 Player * Environment::getPlayer(u16 peer_id)
119 for(std::list<Player*>::iterator i = m_players.begin();
120 i != m_players.end(); ++i)
123 if(player->peer_id == peer_id)
129 Player * Environment::getPlayer(const char *name)
131 for(std::list<Player*>::iterator i = m_players.begin();
132 i != m_players.end(); ++i)
135 if(strcmp(player->getName(), name) == 0)
141 Player * Environment::getRandomConnectedPlayer()
143 std::list<Player*> connected_players = getPlayers(true);
144 u32 chosen_one = myrand() % connected_players.size();
146 for(std::list<Player*>::iterator
147 i = connected_players.begin();
148 i != connected_players.end(); ++i)
160 Player * Environment::getNearestConnectedPlayer(v3f pos)
162 std::list<Player*> connected_players = getPlayers(true);
164 Player *nearest_player = NULL;
165 for(std::list<Player*>::iterator
166 i = connected_players.begin();
167 i != connected_players.end(); ++i)
170 f32 d = player->getPosition().getDistanceFrom(pos);
171 if(d < nearest_d || nearest_player == NULL)
174 nearest_player = player;
177 return nearest_player;
180 std::list<Player*> Environment::getPlayers()
185 std::list<Player*> Environment::getPlayers(bool ignore_disconnected)
187 std::list<Player*> newlist;
188 for(std::list<Player*>::iterator
189 i = m_players.begin();
190 i != m_players.end(); ++i)
194 if(ignore_disconnected)
196 // Ignore disconnected players
197 if(player->peer_id == 0)
201 newlist.push_back(player);
206 u32 Environment::getDayNightRatio()
208 if(m_enable_day_night_ratio_override)
209 return m_day_night_ratio_override;
210 return time_to_daynight_ratio(m_time_of_day_f*24000, m_cache_enable_shaders);
213 void Environment::setTimeOfDaySpeed(float speed)
215 JMutexAutoLock(this->m_lock);
216 m_time_of_day_speed = speed;
219 float Environment::getTimeOfDaySpeed()
221 JMutexAutoLock(this->m_lock);
222 float retval = m_time_of_day_speed;
226 void Environment::stepTimeOfDay(float dtime)
230 JMutexAutoLock(this->m_lock);
231 day_speed = m_time_of_day_speed;
234 m_time_counter += dtime;
235 f32 speed = day_speed * 24000./(24.*3600);
236 u32 units = (u32)(m_time_counter*speed);
240 if(m_time_of_day + units >= 24000)
242 m_time_of_day = (m_time_of_day + units) % 24000;
244 m_time_of_day_f = (float)m_time_of_day / 24000.0;
247 m_time_counter -= (f32)units / speed;
250 m_time_of_day_f += day_speed/24/3600*dtime;
251 if(m_time_of_day_f > 1.0)
252 m_time_of_day_f -= 1.0;
253 if(m_time_of_day_f < 0.0)
254 m_time_of_day_f += 1.0;
262 ABMWithState::ABMWithState(ActiveBlockModifier *abm_):
266 // Initialize timer to random value to spread processing
267 float itv = abm->getTriggerInterval();
268 itv = MYMAX(0.001, itv); // No less than 1ms
269 int minval = MYMAX(-0.51*itv, -60); // Clamp to
270 int maxval = MYMIN(0.51*itv, 60); // +-60 seconds
271 timer = myrand_range(minval, maxval);
278 void fillRadiusBlock(v3s16 p0, s16 r, std::set<v3s16> &list)
281 for(p.X=p0.X-r; p.X<=p0.X+r; p.X++)
282 for(p.Y=p0.Y-r; p.Y<=p0.Y+r; p.Y++)
283 for(p.Z=p0.Z-r; p.Z<=p0.Z+r; p.Z++)
290 void ActiveBlockList::update(std::list<v3s16> &active_positions,
292 std::set<v3s16> &blocks_removed,
293 std::set<v3s16> &blocks_added)
298 std::set<v3s16> newlist = m_forceloaded_list;
299 for(std::list<v3s16>::iterator i = active_positions.begin();
300 i != active_positions.end(); ++i)
302 fillRadiusBlock(*i, radius, newlist);
306 Find out which blocks on the old list are not on the new list
308 // Go through old list
309 for(std::set<v3s16>::iterator i = m_list.begin();
310 i != m_list.end(); ++i)
313 // If not on new list, it's been removed
314 if(newlist.find(p) == newlist.end())
315 blocks_removed.insert(p);
319 Find out which blocks on the new list are not on the old list
321 // Go through new list
322 for(std::set<v3s16>::iterator i = newlist.begin();
323 i != newlist.end(); ++i)
326 // If not on old list, it's been added
327 if(m_list.find(p) == m_list.end())
328 blocks_added.insert(p);
335 for(std::set<v3s16>::iterator i = newlist.begin();
336 i != newlist.end(); ++i)
347 ServerEnvironment::ServerEnvironment(ServerMap *map,
348 GameScripting *scriptIface, IGameDef *gamedef,
349 const std::string &path_world) :
351 m_script(scriptIface),
353 m_path_world(path_world),
354 m_send_recommended_timer(0),
355 m_active_block_interval_overload_skip(0),
357 m_game_time_fraction_counter(0),
358 m_recommended_send_interval(0.1),
359 m_max_lag_estimate(0.1)
363 ServerEnvironment::~ServerEnvironment()
365 // Clear active block list.
366 // This makes the next one delete all active objects.
367 m_active_blocks.clear();
369 // Convert all objects to static and delete the active objects
370 deactivateFarObjects(true);
375 // Delete ActiveBlockModifiers
376 for(std::list<ABMWithState>::iterator
377 i = m_abms.begin(); i != m_abms.end(); ++i){
382 Map & ServerEnvironment::getMap()
387 ServerMap & ServerEnvironment::getServerMap()
392 bool ServerEnvironment::line_of_sight(v3f pos1, v3f pos2, float stepsize, v3s16 *p)
394 float distance = pos1.getDistanceFrom(pos2);
396 //calculate normalized direction vector
397 v3f normalized_vector = v3f((pos2.X - pos1.X)/distance,
398 (pos2.Y - pos1.Y)/distance,
399 (pos2.Z - pos1.Z)/distance);
401 //find out if there's a node on path between pos1 and pos2
402 for (float i = 1; i < distance; i += stepsize) {
403 v3s16 pos = floatToInt(v3f(normalized_vector.X * i,
404 normalized_vector.Y * i,
405 normalized_vector.Z * i) +pos1,BS);
407 MapNode n = getMap().getNodeNoEx(pos);
409 if(n.param0 != CONTENT_AIR) {
419 void ServerEnvironment::saveLoadedPlayers()
421 std::string players_path = m_path_world + DIR_DELIM "players";
422 fs::CreateDir(players_path);
424 for (std::list<Player*>::iterator it = m_players.begin();
425 it != m_players.end();
427 RemotePlayer *player = static_cast<RemotePlayer*>(*it);
428 if (player->checkModified()) {
429 player->save(players_path);
434 void ServerEnvironment::savePlayer(const std::string &playername)
436 std::string players_path = m_path_world + DIR_DELIM "players";
437 fs::CreateDir(players_path);
439 RemotePlayer *player = static_cast<RemotePlayer*>(getPlayer(playername.c_str()));
441 player->save(players_path);
445 Player *ServerEnvironment::loadPlayer(const std::string &playername)
447 std::string players_path = m_path_world + DIR_DELIM "players" DIR_DELIM;
449 RemotePlayer *player = static_cast<RemotePlayer*>(getPlayer(playername.c_str()));
450 bool newplayer = false;
453 player = new RemotePlayer(m_gamedef, playername.c_str());
457 RemotePlayer testplayer(m_gamedef, "");
458 std::string path = players_path + playername;
459 for (u32 i = 0; i < PLAYER_FILE_ALTERNATE_TRIES; i++) {
460 // Open file and deserialize
461 std::ifstream is(path.c_str(), std::ios_base::binary);
465 testplayer.deSerialize(is, path);
467 if (testplayer.getName() == playername) {
468 *player = testplayer;
472 path = players_path + playername + itos(i);
475 infostream << "Player file for player " << playername
476 << " not found" << std::endl;
482 player->setModified(false);
486 void ServerEnvironment::saveMeta()
488 std::string path = m_path_world + DIR_DELIM "env_meta.txt";
490 // Open file and serialize
491 std::ostringstream ss(std::ios_base::binary);
494 args.setU64("game_time", m_game_time);
495 args.setU64("time_of_day", getTimeOfDay());
499 if(!fs::safeWriteToFile(path, ss.str()))
501 infostream<<"ServerEnvironment::saveMeta(): Failed to write "
503 throw SerializationError("Couldn't save env meta");
507 void ServerEnvironment::loadMeta()
509 std::string path = m_path_world + DIR_DELIM "env_meta.txt";
511 // Open file and deserialize
512 std::ifstream is(path.c_str(), std::ios_base::binary);
514 infostream << "ServerEnvironment::loadMeta(): Failed to open "
515 << path << std::endl;
516 throw SerializationError("Couldn't load env meta");
521 if (!args.parseConfigLines(is, "EnvArgsEnd")) {
522 throw SerializationError("ServerEnvironment::loadMeta(): "
523 "EnvArgsEnd not found!");
527 m_game_time = args.getU64("game_time");
528 } catch (SettingNotFoundException &e) {
529 // Getting this is crucial, otherwise timestamps are useless
530 throw SerializationError("Couldn't load env meta game_time");
534 m_time_of_day = args.getU64("time_of_day");
535 } catch (SettingNotFoundException &e) {
536 // This is not as important
537 m_time_of_day = 9000;
543 ActiveBlockModifier *abm;
545 std::set<content_t> required_neighbors;
551 ServerEnvironment *m_env;
552 std::map<content_t, std::list<ActiveABM> > m_aabms;
554 ABMHandler(std::list<ABMWithState> &abms,
555 float dtime_s, ServerEnvironment *env,
561 INodeDefManager *ndef = env->getGameDef()->ndef();
562 for(std::list<ABMWithState>::iterator
563 i = abms.begin(); i != abms.end(); ++i){
564 ActiveBlockModifier *abm = i->abm;
565 float trigger_interval = abm->getTriggerInterval();
566 if(trigger_interval < 0.001)
567 trigger_interval = 0.001;
568 float actual_interval = dtime_s;
571 if(i->timer < trigger_interval)
573 i->timer -= trigger_interval;
574 actual_interval = trigger_interval;
576 float intervals = actual_interval / trigger_interval;
579 float chance = abm->getTriggerChance();
584 aabm.chance = chance / intervals;
588 std::set<std::string> required_neighbors_s
589 = abm->getRequiredNeighbors();
590 for(std::set<std::string>::iterator
591 i = required_neighbors_s.begin();
592 i != required_neighbors_s.end(); i++)
594 ndef->getIds(*i, aabm.required_neighbors);
597 std::set<std::string> contents_s = abm->getTriggerContents();
598 for(std::set<std::string>::iterator
599 i = contents_s.begin(); i != contents_s.end(); i++)
601 std::set<content_t> ids;
602 ndef->getIds(*i, ids);
603 for(std::set<content_t>::const_iterator k = ids.begin();
607 std::map<content_t, std::list<ActiveABM> >::iterator j;
609 if(j == m_aabms.end()){
610 std::list<ActiveABM> aabmlist;
611 m_aabms[c] = aabmlist;
614 j->second.push_back(aabm);
619 // Find out how many objects the given block and its neighbours contain.
620 // Returns the number of objects in the block, and also in 'wider' the
621 // number of objects in the block and all its neighbours. The latter
622 // may an estimate if any neighbours are unloaded.
623 u32 countObjects(MapBlock *block, ServerMap * map, u32 &wider)
626 u32 wider_unknown_count = 0;
627 for(s16 x=-1; x<=1; x++)
628 for(s16 y=-1; y<=1; y++)
629 for(s16 z=-1; z<=1; z++)
631 MapBlock *block2 = map->getBlockNoCreateNoEx(
632 block->getPos() + v3s16(x,y,z));
634 wider_unknown_count++;
637 wider += block2->m_static_objects.m_active.size()
638 + block2->m_static_objects.m_stored.size();
641 u32 active_object_count = block->m_static_objects.m_active.size();
642 u32 wider_known_count = 3*3*3 - wider_unknown_count;
643 wider += wider_unknown_count * wider / wider_known_count;
644 return active_object_count;
647 void apply(MapBlock *block)
652 ServerMap *map = &m_env->getServerMap();
654 u32 active_object_count_wider;
655 u32 active_object_count = this->countObjects(block, map, active_object_count_wider);
656 m_env->m_added_objects = 0;
659 for(p0.X=0; p0.X<MAP_BLOCKSIZE; p0.X++)
660 for(p0.Y=0; p0.Y<MAP_BLOCKSIZE; p0.Y++)
661 for(p0.Z=0; p0.Z<MAP_BLOCKSIZE; p0.Z++)
663 MapNode n = block->getNodeNoEx(p0);
664 content_t c = n.getContent();
665 v3s16 p = p0 + block->getPosRelative();
667 std::map<content_t, std::list<ActiveABM> >::iterator j;
669 if(j == m_aabms.end())
672 for(std::list<ActiveABM>::iterator
673 i = j->second.begin(); i != j->second.end(); i++)
675 if(myrand() % i->chance != 0)
679 if(!i->required_neighbors.empty())
682 for(p1.X = p.X-1; p1.X <= p.X+1; p1.X++)
683 for(p1.Y = p.Y-1; p1.Y <= p.Y+1; p1.Y++)
684 for(p1.Z = p.Z-1; p1.Z <= p.Z+1; p1.Z++)
688 MapNode n = map->getNodeNoEx(p1);
689 content_t c = n.getContent();
690 std::set<content_t>::const_iterator k;
691 k = i->required_neighbors.find(c);
692 if(k != i->required_neighbors.end()){
696 // No required neighbor found
701 // Call all the trigger variations
702 i->abm->trigger(m_env, p, n);
703 i->abm->trigger(m_env, p, n,
704 active_object_count, active_object_count_wider);
706 // Count surrounding objects again if the abms added any
707 if(m_env->m_added_objects > 0) {
708 active_object_count = countObjects(block, map, active_object_count_wider);
709 m_env->m_added_objects = 0;
716 void ServerEnvironment::activateBlock(MapBlock *block, u32 additional_dtime)
718 // Reset usage timer immediately, otherwise a block that becomes active
719 // again at around the same time as it would normally be unloaded will
720 // get unloaded incorrectly. (I think this still leaves a small possibility
721 // of a race condition between this and server::AsyncRunStep, which only
722 // some kind of synchronisation will fix, but it at least reduces the window
723 // of opportunity for it to break from seconds to nanoseconds)
724 block->resetUsageTimer();
726 // Get time difference
728 u32 stamp = block->getTimestamp();
729 if(m_game_time > stamp && stamp != BLOCK_TIMESTAMP_UNDEFINED)
730 dtime_s = m_game_time - block->getTimestamp();
731 dtime_s += additional_dtime;
733 /*infostream<<"ServerEnvironment::activateBlock(): block timestamp: "
734 <<stamp<<", game time: "<<m_game_time<<std::endl;*/
736 // Set current time as timestamp
737 block->setTimestampNoChangedFlag(m_game_time);
739 /*infostream<<"ServerEnvironment::activateBlock(): block is "
740 <<dtime_s<<" seconds old."<<std::endl;*/
742 // Activate stored objects
743 activateObjects(block, dtime_s);
746 std::map<v3s16, NodeTimer> elapsed_timers =
747 block->m_node_timers.step((float)dtime_s);
748 if(!elapsed_timers.empty()){
750 for(std::map<v3s16, NodeTimer>::iterator
751 i = elapsed_timers.begin();
752 i != elapsed_timers.end(); i++){
753 n = block->getNodeNoEx(i->first);
754 v3s16 p = i->first + block->getPosRelative();
755 if(m_script->node_on_timer(p,n,i->second.elapsed))
756 block->setNodeTimer(i->first,NodeTimer(i->second.timeout,0));
760 /* Handle ActiveBlockModifiers */
761 ABMHandler abmhandler(m_abms, dtime_s, this, false);
762 abmhandler.apply(block);
765 void ServerEnvironment::addActiveBlockModifier(ActiveBlockModifier *abm)
767 m_abms.push_back(ABMWithState(abm));
770 bool ServerEnvironment::setNode(v3s16 p, const MapNode &n)
772 INodeDefManager *ndef = m_gamedef->ndef();
773 MapNode n_old = m_map->getNodeNoEx(p);
776 if (ndef->get(n_old).has_on_destruct)
777 m_script->node_on_destruct(p, n_old);
780 if (!m_map->addNodeWithEvent(p, n))
783 // Update active VoxelManipulator if a mapgen thread
784 m_map->updateVManip(p);
786 // Call post-destructor
787 if (ndef->get(n_old).has_after_destruct)
788 m_script->node_after_destruct(p, n_old);
791 if (ndef->get(n).has_on_construct)
792 m_script->node_on_construct(p, n);
797 bool ServerEnvironment::removeNode(v3s16 p)
799 INodeDefManager *ndef = m_gamedef->ndef();
800 MapNode n_old = m_map->getNodeNoEx(p);
803 if (ndef->get(n_old).has_on_destruct)
804 m_script->node_on_destruct(p, n_old);
807 // This is slightly optimized compared to addNodeWithEvent(air)
808 if (!m_map->removeNodeWithEvent(p))
811 // Update active VoxelManipulator if a mapgen thread
812 m_map->updateVManip(p);
814 // Call post-destructor
815 if (ndef->get(n_old).has_after_destruct)
816 m_script->node_after_destruct(p, n_old);
818 // Air doesn't require constructor
822 bool ServerEnvironment::swapNode(v3s16 p, const MapNode &n)
824 if (!m_map->addNodeWithEvent(p, n, false))
827 // Update active VoxelManipulator if a mapgen thread
828 m_map->updateVManip(p);
833 std::set<u16> ServerEnvironment::getObjectsInsideRadius(v3f pos, float radius)
835 std::set<u16> objects;
836 for(std::map<u16, ServerActiveObject*>::iterator
837 i = m_active_objects.begin();
838 i != m_active_objects.end(); ++i)
840 ServerActiveObject* obj = i->second;
842 v3f objectpos = obj->getBasePosition();
843 if(objectpos.getDistanceFrom(pos) > radius)
850 void ServerEnvironment::clearAllObjects()
852 infostream<<"ServerEnvironment::clearAllObjects(): "
853 <<"Removing all active objects"<<std::endl;
854 std::vector<u16> objects_to_remove;
855 for(std::map<u16, ServerActiveObject*>::iterator
856 i = m_active_objects.begin();
857 i != m_active_objects.end(); ++i) {
858 ServerActiveObject* obj = i->second;
859 if(obj->getType() == ACTIVEOBJECT_TYPE_PLAYER)
862 // Delete static object if block is loaded
863 if(obj->m_static_exists){
864 MapBlock *block = m_map->getBlockNoCreateNoEx(obj->m_static_block);
866 block->m_static_objects.remove(id);
867 block->raiseModified(MOD_STATE_WRITE_NEEDED,
869 obj->m_static_exists = false;
872 // If known by some client, don't delete immediately
873 if(obj->m_known_by_count > 0){
874 obj->m_pending_deactivation = true;
875 obj->m_removed = true;
879 // Tell the object about removal
880 obj->removingFromEnvironment();
881 // Deregister in scripting api
882 m_script->removeObjectReference(obj);
884 // Delete active object
885 if(obj->environmentDeletes())
887 // Id to be removed from m_active_objects
888 objects_to_remove.push_back(id);
891 // Remove references from m_active_objects
892 for(std::vector<u16>::iterator i = objects_to_remove.begin();
893 i != objects_to_remove.end(); ++i) {
894 m_active_objects.erase(*i);
897 // Get list of loaded blocks
898 std::vector<v3s16> loaded_blocks;
899 infostream<<"ServerEnvironment::clearAllObjects(): "
900 <<"Listing all loaded blocks"<<std::endl;
901 m_map->listAllLoadedBlocks(loaded_blocks);
902 infostream<<"ServerEnvironment::clearAllObjects(): "
903 <<"Done listing all loaded blocks: "
904 <<loaded_blocks.size()<<std::endl;
906 // Get list of loadable blocks
907 std::vector<v3s16> loadable_blocks;
908 infostream<<"ServerEnvironment::clearAllObjects(): "
909 <<"Listing all loadable blocks"<<std::endl;
910 m_map->listAllLoadableBlocks(loadable_blocks);
911 infostream<<"ServerEnvironment::clearAllObjects(): "
912 <<"Done listing all loadable blocks: "
913 <<loadable_blocks.size()
914 <<", now clearing"<<std::endl;
916 // Grab a reference on each loaded block to avoid unloading it
917 for(std::vector<v3s16>::iterator i = loaded_blocks.begin();
918 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::vector<v3s16>::iterator i = loadable_blocks.begin();
933 i != loadable_blocks.end(); ++i) {
935 MapBlock *block = m_map->emergeBlock(p, false);
937 errorstream<<"ServerEnvironment::clearAllObjects(): "
938 <<"Failed to emerge block "<<PP(p)<<std::endl;
941 u32 num_stored = block->m_static_objects.m_stored.size();
942 u32 num_active = block->m_static_objects.m_active.size();
943 if(num_stored != 0 || num_active != 0){
944 block->m_static_objects.m_stored.clear();
945 block->m_static_objects.m_active.clear();
946 block->raiseModified(MOD_STATE_WRITE_NEEDED,
948 num_objs_cleared += num_stored + num_active;
949 num_blocks_cleared++;
951 num_blocks_checked++;
953 if(report_interval != 0 &&
954 num_blocks_checked % report_interval == 0){
955 float percent = 100.0 * (float)num_blocks_checked /
956 loadable_blocks.size();
957 infostream<<"ServerEnvironment::clearAllObjects(): "
958 <<"Cleared "<<num_objs_cleared<<" objects"
959 <<" in "<<num_blocks_cleared<<" blocks ("
960 <<percent<<"%)"<<std::endl;
962 if(num_blocks_checked % unload_interval == 0){
963 m_map->unloadUnreferencedBlocks();
966 m_map->unloadUnreferencedBlocks();
968 // Drop references that were added above
969 for(std::vector<v3s16>::iterator i = loaded_blocks.begin();
970 i != loaded_blocks.end(); ++i) {
972 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
977 infostream<<"ServerEnvironment::clearAllObjects(): "
978 <<"Finished: Cleared "<<num_objs_cleared<<" objects"
979 <<" in "<<num_blocks_cleared<<" blocks"<<std::endl;
982 void ServerEnvironment::step(float dtime)
984 DSTACK(__FUNCTION_NAME);
986 //TimeTaker timer("ServerEnv step");
988 /* Step time of day */
989 stepTimeOfDay(dtime);
992 // NOTE: This is kind of funny on a singleplayer game, but doesn't
993 // really matter that much.
994 m_recommended_send_interval = g_settings->getFloat("dedicated_server_step");
1000 m_game_time_fraction_counter += dtime;
1001 u32 inc_i = (u32)m_game_time_fraction_counter;
1002 m_game_time += inc_i;
1003 m_game_time_fraction_counter -= (float)inc_i;
1010 ScopeProfiler sp(g_profiler, "SEnv: handle players avg", SPT_AVG);
1011 for(std::list<Player*>::iterator i = m_players.begin();
1012 i != m_players.end(); ++i)
1014 Player *player = *i;
1016 // Ignore disconnected players
1017 if(player->peer_id == 0)
1021 player->move(dtime, this, 100*BS);
1026 Manage active block list
1028 if(m_active_blocks_management_interval.step(dtime, 2.0))
1030 ScopeProfiler sp(g_profiler, "SEnv: manage act. block list avg /2s", SPT_AVG);
1032 Get player block positions
1034 std::list<v3s16> players_blockpos;
1035 for(std::list<Player*>::iterator
1036 i = m_players.begin();
1037 i != m_players.end(); ++i)
1039 Player *player = *i;
1040 // Ignore disconnected players
1041 if(player->peer_id == 0)
1043 v3s16 blockpos = getNodeBlockPos(
1044 floatToInt(player->getPosition(), BS));
1045 players_blockpos.push_back(blockpos);
1049 Update list of active blocks, collecting changes
1051 const s16 active_block_range = g_settings->getS16("active_block_range");
1052 std::set<v3s16> blocks_removed;
1053 std::set<v3s16> blocks_added;
1054 m_active_blocks.update(players_blockpos, active_block_range,
1055 blocks_removed, blocks_added);
1058 Handle removed blocks
1061 // Convert active objects that are no more in active blocks to static
1062 deactivateFarObjects(false);
1064 for(std::set<v3s16>::iterator
1065 i = blocks_removed.begin();
1066 i != blocks_removed.end(); ++i)
1070 /* infostream<<"Server: Block " << PP(p)
1071 << " became inactive"<<std::endl; */
1073 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1077 // Set current time as timestamp (and let it set ChangedFlag)
1078 block->setTimestamp(m_game_time);
1085 for(std::set<v3s16>::iterator
1086 i = blocks_added.begin();
1087 i != blocks_added.end(); ++i)
1091 MapBlock *block = m_map->getBlockOrEmerge(p);
1093 m_active_blocks.m_list.erase(p);
1097 activateBlock(block);
1098 /* infostream<<"Server: Block " << PP(p)
1099 << " became active"<<std::endl; */
1104 Mess around in active blocks
1106 if(m_active_blocks_nodemetadata_interval.step(dtime, 1.0))
1108 ScopeProfiler sp(g_profiler, "SEnv: mess in act. blocks avg /1s", SPT_AVG);
1112 for(std::set<v3s16>::iterator
1113 i = m_active_blocks.m_list.begin();
1114 i != m_active_blocks.m_list.end(); ++i)
1118 /*infostream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
1119 <<") being handled"<<std::endl;*/
1121 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1125 // Reset block usage timer
1126 block->resetUsageTimer();
1128 // Set current time as timestamp
1129 block->setTimestampNoChangedFlag(m_game_time);
1130 // If time has changed much from the one on disk,
1131 // set block to be saved when it is unloaded
1132 if(block->getTimestamp() > block->getDiskTimestamp() + 60)
1133 block->raiseModified(MOD_STATE_WRITE_AT_UNLOAD,
1134 "Timestamp older than 60s (step)");
1137 std::map<v3s16, NodeTimer> elapsed_timers =
1138 block->m_node_timers.step((float)dtime);
1139 if(!elapsed_timers.empty()){
1141 for(std::map<v3s16, NodeTimer>::iterator
1142 i = elapsed_timers.begin();
1143 i != elapsed_timers.end(); i++){
1144 n = block->getNodeNoEx(i->first);
1145 p = i->first + block->getPosRelative();
1146 if(m_script->node_on_timer(p,n,i->second.elapsed))
1147 block->setNodeTimer(i->first,NodeTimer(i->second.timeout,0));
1153 const float abm_interval = 1.0;
1154 if(m_active_block_modifier_interval.step(dtime, abm_interval))
1156 if(m_active_block_interval_overload_skip > 0){
1157 ScopeProfiler sp(g_profiler, "SEnv: ABM overload skips");
1158 m_active_block_interval_overload_skip--;
1161 ScopeProfiler sp(g_profiler, "SEnv: modify in blocks avg /1s", SPT_AVG);
1162 TimeTaker timer("modify in active blocks");
1164 // Initialize handling of ActiveBlockModifiers
1165 ABMHandler abmhandler(m_abms, abm_interval, this, true);
1167 for(std::set<v3s16>::iterator
1168 i = m_active_blocks.m_list.begin();
1169 i != m_active_blocks.m_list.end(); ++i)
1173 /*infostream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
1174 <<") being handled"<<std::endl;*/
1176 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1180 // Set current time as timestamp
1181 block->setTimestampNoChangedFlag(m_game_time);
1183 /* Handle ActiveBlockModifiers */
1184 abmhandler.apply(block);
1187 u32 time_ms = timer.stop(true);
1188 u32 max_time_ms = 200;
1189 if(time_ms > max_time_ms){
1190 infostream<<"WARNING: active block modifiers took "
1191 <<time_ms<<"ms (longer than "
1192 <<max_time_ms<<"ms)"<<std::endl;
1193 m_active_block_interval_overload_skip = (time_ms / max_time_ms) + 1;
1198 Step script environment (run global on_step())
1200 m_script->environment_Step(dtime);
1206 ScopeProfiler sp(g_profiler, "SEnv: step act. objs avg", SPT_AVG);
1207 //TimeTaker timer("Step active objects");
1209 g_profiler->avg("SEnv: num of objects", m_active_objects.size());
1211 // This helps the objects to send data at the same time
1212 bool send_recommended = false;
1213 m_send_recommended_timer += dtime;
1214 if(m_send_recommended_timer > getSendRecommendedInterval())
1216 m_send_recommended_timer -= getSendRecommendedInterval();
1217 send_recommended = true;
1220 for(std::map<u16, ServerActiveObject*>::iterator
1221 i = m_active_objects.begin();
1222 i != m_active_objects.end(); ++i)
1224 ServerActiveObject* obj = i->second;
1225 // Don't step if is to be removed or stored statically
1226 if(obj->m_removed || obj->m_pending_deactivation)
1229 obj->step(dtime, send_recommended);
1230 // Read messages from object
1231 while(!obj->m_messages_out.empty())
1233 m_active_object_messages.push_back(
1234 obj->m_messages_out.pop_front());
1240 Manage active objects
1242 if(m_object_management_interval.step(dtime, 0.5))
1244 ScopeProfiler sp(g_profiler, "SEnv: remove removed objs avg /.5s", SPT_AVG);
1246 Remove objects that satisfy (m_removed && m_known_by_count==0)
1248 removeRemovedObjects();
1252 ServerActiveObject* ServerEnvironment::getActiveObject(u16 id)
1254 std::map<u16, ServerActiveObject*>::iterator n;
1255 n = m_active_objects.find(id);
1256 if(n == m_active_objects.end())
1261 bool isFreeServerActiveObjectId(u16 id,
1262 std::map<u16, ServerActiveObject*> &objects)
1267 return objects.find(id) == objects.end();
1270 u16 getFreeServerActiveObjectId(
1271 std::map<u16, ServerActiveObject*> &objects)
1273 //try to reuse id's as late as possible
1274 static u16 last_used_id = 0;
1275 u16 startid = last_used_id;
1279 if(isFreeServerActiveObjectId(last_used_id, objects))
1280 return last_used_id;
1282 if(last_used_id == startid)
1287 u16 ServerEnvironment::addActiveObject(ServerActiveObject *object)
1291 u16 id = addActiveObjectRaw(object, true, 0);
1296 bool ServerEnvironment::addActiveObjectAsStatic(ServerActiveObject *obj)
1300 v3f objectpos = obj->getBasePosition();
1302 // The block in which the object resides in
1303 v3s16 blockpos_o = getNodeBlockPos(floatToInt(objectpos, BS));
1306 Update the static data
1309 // Create new static object
1310 std::string staticdata = obj->getStaticData();
1311 StaticObject s_obj(obj->getType(), objectpos, staticdata);
1312 // Add to the block where the object is located in
1313 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1314 // Get or generate the block
1315 MapBlock *block = m_map->emergeBlock(blockpos);
1317 bool succeeded = false;
1321 block->m_static_objects.insert(0, s_obj);
1322 block->raiseModified(MOD_STATE_WRITE_AT_UNLOAD,
1323 "addActiveObjectAsStatic");
1327 infostream<<"ServerEnvironment::addActiveObjectAsStatic: "
1328 <<"Could not find or generate "
1329 <<"a block for storing static object"<<std::endl;
1333 if(obj->environmentDeletes())
1341 Finds out what new objects have been added to
1342 inside a radius around a position
1344 void ServerEnvironment::getAddedActiveObjects(v3s16 pos, s16 radius,
1346 std::set<u16> ¤t_objects,
1347 std::set<u16> &added_objects)
1349 v3f pos_f = intToFloat(pos, BS);
1350 f32 radius_f = radius * BS;
1351 f32 player_radius_f = player_radius * BS;
1353 if (player_radius_f < 0)
1354 player_radius_f = 0;
1357 Go through the object list,
1358 - discard m_removed objects,
1359 - discard objects that are too far away,
1360 - discard objects that are found in current_objects.
1361 - add remaining objects to added_objects
1363 for(std::map<u16, ServerActiveObject*>::iterator
1364 i = m_active_objects.begin();
1365 i != m_active_objects.end(); ++i)
1369 ServerActiveObject *object = i->second;
1372 // Discard if removed or deactivating
1373 if(object->m_removed || object->m_pending_deactivation)
1376 f32 distance_f = object->getBasePosition().getDistanceFrom(pos_f);
1377 if (object->getType() == ACTIVEOBJECT_TYPE_PLAYER) {
1378 // Discard if too far
1379 if (distance_f > player_radius_f && player_radius_f != 0)
1381 } else if (distance_f > radius_f)
1384 // Discard if already on current_objects
1385 std::set<u16>::iterator n;
1386 n = current_objects.find(id);
1387 if(n != current_objects.end())
1389 // Add to added_objects
1390 added_objects.insert(id);
1395 Finds out what objects have been removed from
1396 inside a radius around a position
1398 void ServerEnvironment::getRemovedActiveObjects(v3s16 pos, s16 radius,
1400 std::set<u16> ¤t_objects,
1401 std::set<u16> &removed_objects)
1403 v3f pos_f = intToFloat(pos, BS);
1404 f32 radius_f = radius * BS;
1405 f32 player_radius_f = player_radius * BS;
1407 if (player_radius_f < 0)
1408 player_radius_f = 0;
1411 Go through current_objects; object is removed if:
1412 - object is not found in m_active_objects (this is actually an
1413 error condition; objects should be set m_removed=true and removed
1414 only after all clients have been informed about removal), or
1415 - object has m_removed=true, or
1416 - object is too far away
1418 for(std::set<u16>::iterator
1419 i = current_objects.begin();
1420 i != current_objects.end(); ++i)
1423 ServerActiveObject *object = getActiveObject(id);
1426 infostream<<"ServerEnvironment::getRemovedActiveObjects():"
1427 <<" object in current_objects is NULL"<<std::endl;
1428 removed_objects.insert(id);
1432 if(object->m_removed || object->m_pending_deactivation)
1434 removed_objects.insert(id);
1438 f32 distance_f = object->getBasePosition().getDistanceFrom(pos_f);
1439 if (object->getType() == ACTIVEOBJECT_TYPE_PLAYER) {
1440 if (distance_f <= player_radius_f || player_radius_f == 0)
1442 } else if (distance_f <= radius_f)
1445 // Object is no longer visible
1446 removed_objects.insert(id);
1450 ActiveObjectMessage ServerEnvironment::getActiveObjectMessage()
1452 if(m_active_object_messages.empty())
1453 return ActiveObjectMessage(0);
1455 ActiveObjectMessage message = m_active_object_messages.front();
1456 m_active_object_messages.pop_front();
1461 ************ Private methods *************
1464 u16 ServerEnvironment::addActiveObjectRaw(ServerActiveObject *object,
1465 bool set_changed, u32 dtime_s)
1468 if(object->getId() == 0){
1469 u16 new_id = getFreeServerActiveObjectId(m_active_objects);
1472 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1473 <<"no free ids available"<<std::endl;
1474 if(object->environmentDeletes())
1478 object->setId(new_id);
1481 verbosestream<<"ServerEnvironment::addActiveObjectRaw(): "
1482 <<"supplied with id "<<object->getId()<<std::endl;
1484 if(isFreeServerActiveObjectId(object->getId(), m_active_objects) == false)
1486 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1487 <<"id is not free ("<<object->getId()<<")"<<std::endl;
1488 if(object->environmentDeletes())
1492 /*infostream<<"ServerEnvironment::addActiveObjectRaw(): "
1493 <<"added (id="<<object->getId()<<")"<<std::endl;*/
1495 m_active_objects[object->getId()] = object;
1497 verbosestream<<"ServerEnvironment::addActiveObjectRaw(): "
1498 <<"Added id="<<object->getId()<<"; there are now "
1499 <<m_active_objects.size()<<" active objects."
1502 // Register reference in scripting api (must be done before post-init)
1503 m_script->addObjectReference(object);
1504 // Post-initialize object
1505 object->addedToEnvironment(dtime_s);
1507 // Add static data to block
1508 if(object->isStaticAllowed())
1510 // Add static object to active static list of the block
1511 v3f objectpos = object->getBasePosition();
1512 std::string staticdata = object->getStaticData();
1513 StaticObject s_obj(object->getType(), objectpos, staticdata);
1514 // Add to the block where the object is located in
1515 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1516 MapBlock *block = m_map->emergeBlock(blockpos);
1518 block->m_static_objects.m_active[object->getId()] = s_obj;
1519 object->m_static_exists = true;
1520 object->m_static_block = blockpos;
1523 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1524 "addActiveObjectRaw");
1526 v3s16 p = floatToInt(objectpos, BS);
1527 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1528 <<"could not emerge block for storing id="<<object->getId()
1529 <<" statically (pos="<<PP(p)<<")"<<std::endl;
1533 return object->getId();
1537 Remove objects that satisfy (m_removed && m_known_by_count==0)
1539 void ServerEnvironment::removeRemovedObjects()
1541 std::vector<u16> objects_to_remove;
1542 for(std::map<u16, ServerActiveObject*>::iterator
1543 i = m_active_objects.begin();
1544 i != m_active_objects.end(); ++i) {
1546 ServerActiveObject* obj = i->second;
1547 // This shouldn't happen but check it
1550 infostream<<"NULL object found in ServerEnvironment"
1551 <<" while finding removed objects. id="<<id<<std::endl;
1552 // Id to be removed from m_active_objects
1553 objects_to_remove.push_back(id);
1558 We will delete objects that are marked as removed or thatare
1559 waiting for deletion after deactivation
1561 if(obj->m_removed == false && obj->m_pending_deactivation == false)
1565 Delete static data from block if is marked as removed
1567 if(obj->m_static_exists && obj->m_removed)
1569 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
1571 block->m_static_objects.remove(id);
1572 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1573 "removeRemovedObjects/remove");
1574 obj->m_static_exists = false;
1576 infostream<<"Failed to emerge block from which an object to "
1577 <<"be removed was loaded from. id="<<id<<std::endl;
1581 // If m_known_by_count > 0, don't actually remove. On some future
1582 // invocation this will be 0, which is when removal will continue.
1583 if(obj->m_known_by_count > 0)
1587 Move static data from active to stored if not marked as removed
1589 if(obj->m_static_exists && !obj->m_removed){
1590 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
1592 std::map<u16, StaticObject>::iterator i =
1593 block->m_static_objects.m_active.find(id);
1594 if(i != block->m_static_objects.m_active.end()){
1595 block->m_static_objects.m_stored.push_back(i->second);
1596 block->m_static_objects.m_active.erase(id);
1597 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1598 "removeRemovedObjects/deactivate");
1601 infostream<<"Failed to emerge block from which an object to "
1602 <<"be deactivated was loaded from. id="<<id<<std::endl;
1606 // Tell the object about removal
1607 obj->removingFromEnvironment();
1608 // Deregister in scripting api
1609 m_script->removeObjectReference(obj);
1612 if(obj->environmentDeletes())
1615 // Id to be removed from m_active_objects
1616 objects_to_remove.push_back(id);
1618 // Remove references from m_active_objects
1619 for(std::vector<u16>::iterator i = objects_to_remove.begin();
1620 i != objects_to_remove.end(); ++i) {
1621 m_active_objects.erase(*i);
1625 static void print_hexdump(std::ostream &o, const std::string &data)
1627 const int linelength = 16;
1628 for(int l=0; ; l++){
1629 int i0 = linelength * l;
1630 bool at_end = false;
1631 int thislinelength = linelength;
1632 if(i0 + thislinelength > (int)data.size()){
1633 thislinelength = data.size() - i0;
1636 for(int di=0; di<linelength; di++){
1639 if(di<thislinelength)
1640 snprintf(buf, 4, "%.2x ", data[i]);
1642 snprintf(buf, 4, " ");
1646 for(int di=0; di<thislinelength; di++){
1660 Convert stored objects from blocks near the players to active.
1662 void ServerEnvironment::activateObjects(MapBlock *block, u32 dtime_s)
1667 // Ignore if no stored objects (to not set changed flag)
1668 if(block->m_static_objects.m_stored.empty())
1670 verbosestream<<"ServerEnvironment::activateObjects(): "
1671 <<"activating objects of block "<<PP(block->getPos())
1672 <<" ("<<block->m_static_objects.m_stored.size()
1673 <<" objects)"<<std::endl;
1674 bool large_amount = (block->m_static_objects.m_stored.size() > g_settings->getU16("max_objects_per_block"));
1676 errorstream<<"suspiciously large amount of objects detected: "
1677 <<block->m_static_objects.m_stored.size()<<" in "
1678 <<PP(block->getPos())
1679 <<"; removing all of them."<<std::endl;
1680 // Clear stored list
1681 block->m_static_objects.m_stored.clear();
1682 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1683 "stored list cleared in activateObjects due to "
1684 "large amount of objects");
1688 // Activate stored objects
1689 std::list<StaticObject> new_stored;
1690 for(std::list<StaticObject>::iterator
1691 i = block->m_static_objects.m_stored.begin();
1692 i != block->m_static_objects.m_stored.end(); ++i) {
1693 StaticObject &s_obj = *i;
1695 // Create an active object from the data
1696 ServerActiveObject *obj = ServerActiveObject::create
1697 ((ActiveObjectType) s_obj.type, this, 0, s_obj.pos, s_obj.data);
1698 // If couldn't create object, store static data back.
1700 errorstream<<"ServerEnvironment::activateObjects(): "
1701 <<"failed to create active object from static object "
1702 <<"in block "<<PP(s_obj.pos/BS)
1703 <<" type="<<(int)s_obj.type<<" data:"<<std::endl;
1704 print_hexdump(verbosestream, s_obj.data);
1706 new_stored.push_back(s_obj);
1709 verbosestream<<"ServerEnvironment::activateObjects(): "
1710 <<"activated static object pos="<<PP(s_obj.pos/BS)
1711 <<" type="<<(int)s_obj.type<<std::endl;
1712 // This will also add the object to the active static list
1713 addActiveObjectRaw(obj, false, dtime_s);
1715 // Clear stored list
1716 block->m_static_objects.m_stored.clear();
1717 // Add leftover failed stuff to stored list
1718 for(std::list<StaticObject>::iterator
1719 i = new_stored.begin();
1720 i != new_stored.end(); ++i)
1722 StaticObject &s_obj = *i;
1723 block->m_static_objects.m_stored.push_back(s_obj);
1726 // Turn the active counterparts of activated objects not pending for
1728 for(std::map<u16, StaticObject>::iterator
1729 i = block->m_static_objects.m_active.begin();
1730 i != block->m_static_objects.m_active.end(); ++i)
1733 ServerActiveObject *object = getActiveObject(id);
1735 object->m_pending_deactivation = false;
1739 Note: Block hasn't really been modified here.
1740 The objects have just been activated and moved from the stored
1741 static list to the active static list.
1742 As such, the block is essentially the same.
1743 Thus, do not call block->raiseModified(MOD_STATE_WRITE_NEEDED).
1744 Otherwise there would be a huge amount of unnecessary I/O.
1749 Convert objects that are not standing inside active blocks to static.
1751 If m_known_by_count != 0, active object is not deleted, but static
1752 data is still updated.
1754 If force_delete is set, active object is deleted nevertheless. It
1755 shall only be set so in the destructor of the environment.
1757 If block wasn't generated (not in memory or on disk),
1759 void ServerEnvironment::deactivateFarObjects(bool force_delete)
1761 std::list<u16> objects_to_remove;
1762 for(std::map<u16, ServerActiveObject*>::iterator
1763 i = m_active_objects.begin();
1764 i != m_active_objects.end(); ++i)
1766 ServerActiveObject* obj = i->second;
1769 // Do not deactivate if static data creation not allowed
1770 if(!force_delete && !obj->isStaticAllowed())
1773 // If pending deactivation, let removeRemovedObjects() do it
1774 if(!force_delete && obj->m_pending_deactivation)
1778 v3f objectpos = obj->getBasePosition();
1780 // The block in which the object resides in
1781 v3s16 blockpos_o = getNodeBlockPos(floatToInt(objectpos, BS));
1783 // If object's static data is stored in a deactivated block and object
1784 // is actually located in an active block, re-save to the block in
1785 // which the object is actually located in.
1787 obj->m_static_exists &&
1788 !m_active_blocks.contains(obj->m_static_block) &&
1789 m_active_blocks.contains(blockpos_o))
1791 v3s16 old_static_block = obj->m_static_block;
1793 // Save to block where object is located
1794 MapBlock *block = m_map->emergeBlock(blockpos_o, false);
1796 errorstream<<"ServerEnvironment::deactivateFarObjects(): "
1797 <<"Could not save object id="<<id
1798 <<" to it's current block "<<PP(blockpos_o)
1802 std::string staticdata_new = obj->getStaticData();
1803 StaticObject s_obj(obj->getType(), objectpos, staticdata_new);
1804 block->m_static_objects.insert(id, s_obj);
1805 obj->m_static_block = blockpos_o;
1806 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1807 "deactivateFarObjects: Static data moved in");
1809 // Delete from block where object was located
1810 block = m_map->emergeBlock(old_static_block, false);
1812 errorstream<<"ServerEnvironment::deactivateFarObjects(): "
1813 <<"Could not delete object id="<<id
1814 <<" from it's previous block "<<PP(old_static_block)
1818 block->m_static_objects.remove(id);
1819 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1820 "deactivateFarObjects: Static data moved out");
1824 // If block is active, don't remove
1825 if(!force_delete && m_active_blocks.contains(blockpos_o))
1828 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
1829 <<"deactivating object id="<<id<<" on inactive block "
1830 <<PP(blockpos_o)<<std::endl;
1832 // If known by some client, don't immediately delete.
1833 bool pending_delete = (obj->m_known_by_count > 0 && !force_delete);
1836 Update the static data
1839 if(obj->isStaticAllowed())
1841 // Create new static object
1842 std::string staticdata_new = obj->getStaticData();
1843 StaticObject s_obj(obj->getType(), objectpos, staticdata_new);
1845 bool stays_in_same_block = false;
1846 bool data_changed = true;
1848 if(obj->m_static_exists){
1849 if(obj->m_static_block == blockpos_o)
1850 stays_in_same_block = true;
1852 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
1854 std::map<u16, StaticObject>::iterator n =
1855 block->m_static_objects.m_active.find(id);
1856 if(n != block->m_static_objects.m_active.end()){
1857 StaticObject static_old = n->second;
1859 float save_movem = obj->getMinimumSavedMovement();
1861 if(static_old.data == staticdata_new &&
1862 (static_old.pos - objectpos).getLength() < save_movem)
1863 data_changed = false;
1865 errorstream<<"ServerEnvironment::deactivateFarObjects(): "
1866 <<"id="<<id<<" m_static_exists=true but "
1867 <<"static data doesn't actually exist in "
1868 <<PP(obj->m_static_block)<<std::endl;
1872 bool shall_be_written = (!stays_in_same_block || data_changed);
1874 // Delete old static object
1875 if(obj->m_static_exists)
1877 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
1880 block->m_static_objects.remove(id);
1881 obj->m_static_exists = false;
1882 // Only mark block as modified if data changed considerably
1883 if(shall_be_written)
1884 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1885 "deactivateFarObjects: Static data "
1886 "changed considerably");
1890 // Add to the block where the object is located in
1891 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1892 // Get or generate the block
1893 MapBlock *block = NULL;
1895 block = m_map->emergeBlock(blockpos);
1896 } catch(InvalidPositionException &e){
1897 // Handled via NULL pointer
1898 // NOTE: emergeBlock's failure is usually determined by it
1899 // actually returning NULL
1904 if(block->m_static_objects.m_stored.size() >= g_settings->getU16("max_objects_per_block")){
1905 errorstream<<"ServerEnv: Trying to store id="<<obj->getId()
1906 <<" statically but block "<<PP(blockpos)
1907 <<" already contains "
1908 <<block->m_static_objects.m_stored.size()
1910 <<" Forcing delete."<<std::endl;
1911 force_delete = true;
1913 // If static counterpart already exists in target block,
1915 // This shouldn't happen because the object is removed from
1916 // the previous block before this according to
1917 // obj->m_static_block, but happens rarely for some unknown
1918 // reason. Unsuccessful attempts have been made to find
1920 if(id && block->m_static_objects.m_active.find(id) != block->m_static_objects.m_active.end()){
1921 infostream<<"ServerEnv: WARNING: Performing hack #83274"
1923 block->m_static_objects.remove(id);
1925 // Store static data
1926 u16 store_id = pending_delete ? id : 0;
1927 block->m_static_objects.insert(store_id, s_obj);
1929 // Only mark block as modified if data changed considerably
1930 if(shall_be_written)
1931 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1932 "deactivateFarObjects: Static data "
1933 "changed considerably");
1935 obj->m_static_exists = true;
1936 obj->m_static_block = block->getPos();
1941 v3s16 p = floatToInt(objectpos, BS);
1942 errorstream<<"ServerEnv: Could not find or generate "
1943 <<"a block for storing id="<<obj->getId()
1944 <<" statically (pos="<<PP(p)<<")"<<std::endl;
1951 If known by some client, set pending deactivation.
1952 Otherwise delete it immediately.
1955 if(pending_delete && !force_delete)
1957 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
1958 <<"object id="<<id<<" is known by clients"
1959 <<"; not deleting yet"<<std::endl;
1961 obj->m_pending_deactivation = true;
1965 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
1966 <<"object id="<<id<<" is not known by clients"
1967 <<"; deleting"<<std::endl;
1969 // Tell the object about removal
1970 obj->removingFromEnvironment();
1971 // Deregister in scripting api
1972 m_script->removeObjectReference(obj);
1974 // Delete active object
1975 if(obj->environmentDeletes())
1977 // Id to be removed from m_active_objects
1978 objects_to_remove.push_back(id);
1981 // Remove references from m_active_objects
1982 for(std::list<u16>::iterator i = objects_to_remove.begin();
1983 i != objects_to_remove.end(); ++i)
1985 m_active_objects.erase(*i);
1992 #include "clientsimpleobject.h"
1998 ClientEnvironment::ClientEnvironment(ClientMap *map, scene::ISceneManager *smgr,
1999 ITextureSource *texturesource, IGameDef *gamedef,
2000 IrrlichtDevice *irr):
2003 m_texturesource(texturesource),
2008 memset(m_attachements, zero, sizeof(m_attachements));
2011 ClientEnvironment::~ClientEnvironment()
2013 // delete active objects
2014 for(std::map<u16, ClientActiveObject*>::iterator
2015 i = m_active_objects.begin();
2016 i != m_active_objects.end(); ++i)
2021 for(std::list<ClientSimpleObject*>::iterator
2022 i = m_simple_objects.begin(); i != m_simple_objects.end(); ++i)
2031 Map & ClientEnvironment::getMap()
2036 ClientMap & ClientEnvironment::getClientMap()
2041 void ClientEnvironment::addPlayer(Player *player)
2043 DSTACK(__FUNCTION_NAME);
2045 It is a failure if player is local and there already is a local
2048 assert(!(player->isLocal() == true && getLocalPlayer() != NULL));
2050 Environment::addPlayer(player);
2053 LocalPlayer * ClientEnvironment::getLocalPlayer()
2055 for(std::list<Player*>::iterator i = m_players.begin();
2056 i != m_players.end(); ++i)
2058 Player *player = *i;
2059 if(player->isLocal())
2060 return (LocalPlayer*)player;
2065 void ClientEnvironment::step(float dtime)
2067 DSTACK(__FUNCTION_NAME);
2069 /* Step time of day */
2070 stepTimeOfDay(dtime);
2072 // Get some settings
2073 bool fly_allowed = m_gamedef->checkLocalPrivilege("fly");
2074 bool free_move = fly_allowed && g_settings->getBool("free_move");
2077 LocalPlayer *lplayer = getLocalPlayer();
2079 // collision info queue
2080 std::list<CollisionInfo> player_collisions;
2083 Get the speed the player is going
2085 bool is_climbing = lplayer->is_climbing;
2087 f32 player_speed = lplayer->getSpeed().getLength();
2090 Maximum position increment
2092 //f32 position_max_increment = 0.05*BS;
2093 f32 position_max_increment = 0.1*BS;
2095 // Maximum time increment (for collision detection etc)
2096 // time = distance / speed
2097 f32 dtime_max_increment = 1;
2098 if(player_speed > 0.001)
2099 dtime_max_increment = position_max_increment / player_speed;
2101 // Maximum time increment is 10ms or lower
2102 if(dtime_max_increment > 0.01)
2103 dtime_max_increment = 0.01;
2105 // Don't allow overly huge dtime
2109 f32 dtime_downcount = dtime;
2112 Stuff that has a maximum time increment
2121 if(dtime_downcount > dtime_max_increment)
2123 dtime_part = dtime_max_increment;
2124 dtime_downcount -= dtime_part;
2128 dtime_part = dtime_downcount;
2130 Setting this to 0 (no -=dtime_part) disables an infinite loop
2131 when dtime_part is so small that dtime_downcount -= dtime_part
2134 dtime_downcount = 0;
2143 if(free_move == false && is_climbing == false)
2146 v3f speed = lplayer->getSpeed();
2147 if(lplayer->in_liquid == false)
2148 speed.Y -= lplayer->movement_gravity * lplayer->physics_override_gravity * dtime_part * 2;
2150 // Liquid floating / sinking
2151 if(lplayer->in_liquid && !lplayer->swimming_vertical)
2152 speed.Y -= lplayer->movement_liquid_sink * dtime_part * 2;
2154 // Liquid resistance
2155 if(lplayer->in_liquid_stable || lplayer->in_liquid)
2157 // How much the node's viscosity blocks movement, ranges between 0 and 1
2158 // Should match the scale at which viscosity increase affects other liquid attributes
2159 const f32 viscosity_factor = 0.3;
2161 v3f d_wanted = -speed / lplayer->movement_liquid_fluidity;
2162 f32 dl = d_wanted.getLength();
2163 if(dl > lplayer->movement_liquid_fluidity_smooth)
2164 dl = lplayer->movement_liquid_fluidity_smooth;
2165 dl *= (lplayer->liquid_viscosity * viscosity_factor) + (1 - viscosity_factor);
2167 v3f d = d_wanted.normalize() * dl;
2171 if(speed.X > lplayer->movement_liquid_fluidity + lplayer->movement_liquid_fluidity_smooth) speed.X -= lplayer->movement_liquid_fluidity_smooth;
2172 if(speed.X < -lplayer->movement_liquid_fluidity - lplayer->movement_liquid_fluidity_smooth) speed.X += 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.Y < -lplayer->movement_liquid_fluidity - lplayer->movement_liquid_fluidity_smooth) speed.Y += 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;
2176 if(speed.Z < -lplayer->movement_liquid_fluidity - lplayer->movement_liquid_fluidity_smooth) speed.Z += lplayer->movement_liquid_fluidity_smooth;
2180 lplayer->setSpeed(speed);
2185 This also does collision detection.
2187 lplayer->move(dtime_part, this, position_max_increment,
2188 &player_collisions);
2191 while(dtime_downcount > 0.001);
2193 //std::cout<<"Looped "<<loopcount<<" times."<<std::endl;
2195 for(std::list<CollisionInfo>::iterator
2196 i = player_collisions.begin();
2197 i != player_collisions.end(); ++i)
2199 CollisionInfo &info = *i;
2200 v3f speed_diff = info.new_speed - info.old_speed;;
2201 // Handle only fall damage
2202 // (because otherwise walking against something in fast_move kills you)
2203 if(speed_diff.Y < 0 || info.old_speed.Y >= 0)
2205 // Get rid of other components
2208 f32 pre_factor = 1; // 1 hp per node/s
2209 f32 tolerance = BS*14; // 5 without damage
2210 f32 post_factor = 1; // 1 hp per node/s
2211 if(info.type == COLLISION_NODE)
2213 const ContentFeatures &f = m_gamedef->ndef()->
2214 get(m_map->getNodeNoEx(info.node_p));
2215 // Determine fall damage multiplier
2216 int addp = itemgroup_get(f.groups, "fall_damage_add_percent");
2217 pre_factor = 1.0 + (float)addp/100.0;
2219 float speed = pre_factor * speed_diff.getLength();
2220 if(speed > tolerance)
2222 f32 damage_f = (speed - tolerance)/BS * post_factor;
2223 u16 damage = (u16)(damage_f+0.5);
2225 damageLocalPlayer(damage, true);
2226 MtEvent *e = new SimpleTriggerEvent("PlayerFallingDamage");
2227 m_gamedef->event()->put(e);
2233 A quick draft of lava damage
2235 if(m_lava_hurt_interval.step(dtime, 1.0))
2237 v3f pf = lplayer->getPosition();
2239 // Feet, middle and head
2240 v3s16 p1 = floatToInt(pf + v3f(0, BS*0.1, 0), BS);
2241 MapNode n1 = m_map->getNodeNoEx(p1);
2242 v3s16 p2 = floatToInt(pf + v3f(0, BS*0.8, 0), BS);
2243 MapNode n2 = m_map->getNodeNoEx(p2);
2244 v3s16 p3 = floatToInt(pf + v3f(0, BS*1.6, 0), BS);
2245 MapNode n3 = m_map->getNodeNoEx(p3);
2247 u32 damage_per_second = 0;
2248 damage_per_second = MYMAX(damage_per_second,
2249 m_gamedef->ndef()->get(n1).damage_per_second);
2250 damage_per_second = MYMAX(damage_per_second,
2251 m_gamedef->ndef()->get(n2).damage_per_second);
2252 damage_per_second = MYMAX(damage_per_second,
2253 m_gamedef->ndef()->get(n3).damage_per_second);
2255 if(damage_per_second != 0)
2257 damageLocalPlayer(damage_per_second, true);
2264 if(m_drowning_interval.step(dtime, 2.0))
2266 v3f pf = lplayer->getPosition();
2269 v3s16 p = floatToInt(pf + v3f(0, BS*1.6, 0), BS);
2270 MapNode n = m_map->getNodeNoEx(p);
2271 ContentFeatures c = m_gamedef->ndef()->get(n);
2272 u8 drowning_damage = c.drowning;
2273 if(drowning_damage > 0 && lplayer->hp > 0){
2274 u16 breath = lplayer->getBreath();
2281 lplayer->setBreath(breath);
2282 updateLocalPlayerBreath(breath);
2285 if(lplayer->getBreath() == 0 && drowning_damage > 0){
2286 damageLocalPlayer(drowning_damage, true);
2289 if(m_breathing_interval.step(dtime, 0.5))
2291 v3f pf = lplayer->getPosition();
2294 v3s16 p = floatToInt(pf + v3f(0, BS*1.6, 0), BS);
2295 MapNode n = m_map->getNodeNoEx(p);
2296 ContentFeatures c = m_gamedef->ndef()->get(n);
2298 lplayer->setBreath(11);
2300 else if(c.drowning == 0){
2301 u16 breath = lplayer->getBreath();
2304 lplayer->setBreath(breath);
2305 updateLocalPlayerBreath(breath);
2311 Stuff that can be done in an arbitarily large dtime
2313 for(std::list<Player*>::iterator i = m_players.begin();
2314 i != m_players.end(); ++i)
2316 Player *player = *i;
2319 Handle non-local players
2321 if(player->isLocal() == false)
2324 player->move(dtime, this, 100*BS);
2329 // Update lighting on local player (used for wield item)
2330 u32 day_night_ratio = getDayNightRatio();
2334 // On InvalidPositionException, use this as default
2335 // (day: LIGHT_SUN, night: 0)
2336 MapNode node_at_lplayer(CONTENT_AIR, 0x0f, 0);
2338 v3s16 p = lplayer->getLightPosition();
2339 node_at_lplayer = m_map->getNodeNoEx(p);
2341 u16 light = getInteriorLight(node_at_lplayer, 0, m_gamedef->ndef());
2342 u8 day = light & 0xff;
2343 u8 night = (light >> 8) & 0xff;
2344 finalColorBlend(lplayer->light_color, day, night, day_night_ratio);
2348 Step active objects and update lighting of them
2351 g_profiler->avg("CEnv: num of objects", m_active_objects.size());
2352 bool update_lighting = m_active_object_light_update_interval.step(dtime, 0.21);
2353 for(std::map<u16, ClientActiveObject*>::iterator
2354 i = m_active_objects.begin();
2355 i != m_active_objects.end(); ++i)
2357 ClientActiveObject* obj = i->second;
2359 obj->step(dtime, this);
2368 v3s16 p = obj->getLightPosition();
2369 MapNode n = m_map->getNodeNoEx(p, &pos_ok);
2371 light = n.getLightBlend(day_night_ratio, m_gamedef->ndef());
2373 light = blend_light(day_night_ratio, LIGHT_SUN, 0);
2375 obj->updateLight(light);
2380 Step and handle simple objects
2382 g_profiler->avg("CEnv: num of simple objects", m_simple_objects.size());
2383 for(std::list<ClientSimpleObject*>::iterator
2384 i = m_simple_objects.begin(); i != m_simple_objects.end();)
2386 ClientSimpleObject *simple = *i;
2387 std::list<ClientSimpleObject*>::iterator cur = i;
2389 simple->step(dtime);
2390 if(simple->m_to_be_removed){
2392 m_simple_objects.erase(cur);
2397 void ClientEnvironment::addSimpleObject(ClientSimpleObject *simple)
2399 m_simple_objects.push_back(simple);
2402 ClientActiveObject* ClientEnvironment::getActiveObject(u16 id)
2404 std::map<u16, ClientActiveObject*>::iterator n;
2405 n = m_active_objects.find(id);
2406 if(n == m_active_objects.end())
2411 bool isFreeClientActiveObjectId(u16 id,
2412 std::map<u16, ClientActiveObject*> &objects)
2417 return objects.find(id) == objects.end();
2420 u16 getFreeClientActiveObjectId(
2421 std::map<u16, ClientActiveObject*> &objects)
2423 //try to reuse id's as late as possible
2424 static u16 last_used_id = 0;
2425 u16 startid = last_used_id;
2429 if(isFreeClientActiveObjectId(last_used_id, objects))
2430 return last_used_id;
2432 if(last_used_id == startid)
2437 u16 ClientEnvironment::addActiveObject(ClientActiveObject *object)
2440 if(object->getId() == 0)
2442 u16 new_id = getFreeClientActiveObjectId(m_active_objects);
2445 infostream<<"ClientEnvironment::addActiveObject(): "
2446 <<"no free ids available"<<std::endl;
2450 object->setId(new_id);
2452 if(isFreeClientActiveObjectId(object->getId(), m_active_objects) == false)
2454 infostream<<"ClientEnvironment::addActiveObject(): "
2455 <<"id is not free ("<<object->getId()<<")"<<std::endl;
2459 infostream<<"ClientEnvironment::addActiveObject(): "
2460 <<"added (id="<<object->getId()<<")"<<std::endl;
2461 m_active_objects[object->getId()] = object;
2462 object->addToScene(m_smgr, m_texturesource, m_irr);
2463 { // Update lighting immediately
2468 v3s16 p = object->getLightPosition();
2469 MapNode n = m_map->getNodeNoEx(p, &pos_ok);
2471 light = n.getLightBlend(getDayNightRatio(), m_gamedef->ndef());
2473 light = blend_light(getDayNightRatio(), LIGHT_SUN, 0);
2475 object->updateLight(light);
2477 return object->getId();
2480 void ClientEnvironment::addActiveObject(u16 id, u8 type,
2481 const std::string &init_data)
2483 ClientActiveObject* obj =
2484 ClientActiveObject::create((ActiveObjectType) type, m_gamedef, this);
2487 infostream<<"ClientEnvironment::addActiveObject(): "
2488 <<"id="<<id<<" type="<<type<<": Couldn't create object"
2497 obj->initialize(init_data);
2499 catch(SerializationError &e)
2501 errorstream<<"ClientEnvironment::addActiveObject():"
2502 <<" id="<<id<<" type="<<type
2503 <<": SerializationError in initialize(): "
2505 <<": init_data="<<serializeJsonString(init_data)
2509 addActiveObject(obj);
2512 void ClientEnvironment::removeActiveObject(u16 id)
2514 verbosestream<<"ClientEnvironment::removeActiveObject(): "
2515 <<"id="<<id<<std::endl;
2516 ClientActiveObject* obj = getActiveObject(id);
2519 infostream<<"ClientEnvironment::removeActiveObject(): "
2520 <<"id="<<id<<" not found"<<std::endl;
2523 obj->removeFromScene(true);
2525 m_active_objects.erase(id);
2528 void ClientEnvironment::processActiveObjectMessage(u16 id,
2529 const std::string &data)
2531 ClientActiveObject* obj = getActiveObject(id);
2534 infostream<<"ClientEnvironment::processActiveObjectMessage():"
2535 <<" got message for id="<<id<<", which doesn't exist."
2541 obj->processMessage(data);
2543 catch(SerializationError &e)
2545 errorstream<<"ClientEnvironment::processActiveObjectMessage():"
2546 <<" id="<<id<<" type="<<obj->getType()
2547 <<" SerializationError in processMessage(),"
2548 <<" message="<<serializeJsonString(data)
2554 Callbacks for activeobjects
2557 void ClientEnvironment::damageLocalPlayer(u8 damage, bool handle_hp)
2559 LocalPlayer *lplayer = getLocalPlayer();
2563 if (lplayer->hp == 0) // Don't damage a dead player
2565 if(lplayer->hp > damage)
2566 lplayer->hp -= damage;
2571 ClientEnvEvent event;
2572 event.type = CEE_PLAYER_DAMAGE;
2573 event.player_damage.amount = damage;
2574 event.player_damage.send_to_server = handle_hp;
2575 m_client_event_queue.push_back(event);
2578 void ClientEnvironment::updateLocalPlayerBreath(u16 breath)
2580 ClientEnvEvent event;
2581 event.type = CEE_PLAYER_BREATH;
2582 event.player_breath.amount = breath;
2583 m_client_event_queue.push_back(event);
2587 Client likes to call these
2590 void ClientEnvironment::getActiveObjects(v3f origin, f32 max_d,
2591 std::vector<DistanceSortedActiveObject> &dest)
2593 for(std::map<u16, ClientActiveObject*>::iterator
2594 i = m_active_objects.begin();
2595 i != m_active_objects.end(); ++i)
2597 ClientActiveObject* obj = i->second;
2599 f32 d = (obj->getPosition() - origin).getLength();
2604 DistanceSortedActiveObject dso(obj, d);
2606 dest.push_back(dso);
2610 ClientEnvEvent ClientEnvironment::getClientEvent()
2612 ClientEnvEvent event;
2613 if(m_client_event_queue.empty())
2614 event.type = CEE_NONE;
2616 event = m_client_event_queue.front();
2617 m_client_event_queue.pop_front();
2622 #endif // #ifndef SERVER