3 Copyright (C) 2010-2013 celeron55, Perttu Ahola <celeron55@gmail.com>
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU Lesser General Public License as published by
7 the Free Software Foundation; either version 2.1 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU Lesser General Public License for more details.
15 You should have received a copy of the GNU Lesser General Public License along
16 with this program; if not, write to the Free Software Foundation, Inc.,
17 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
21 #include "environment.h"
24 #include "collision.h"
25 #include "content_mapnode.h"
27 #include "serverobject.h"
28 #include "content_sao.h"
32 #include "scripting_game.h"
34 #include "nodemetadata.h"
37 #include "clientmap.h"
38 #include "localplayer.h"
39 #include "mapblock_mesh.h"
43 #include "daynightratio.h"
46 #include "util/serialize.h"
47 #include "jthread/jmutexautolock.h"
49 #define PP(x) "("<<(x).X<<","<<(x).Y<<","<<(x).Z<<")"
51 Environment::Environment():
53 m_time_of_day_f(9000./24000),
54 m_time_of_day_speed(0),
56 m_enable_day_night_ratio_override(false),
57 m_day_night_ratio_override(0.0f)
59 m_cache_enable_shaders = g_settings->getBool("enable_shaders");
62 Environment::~Environment()
65 for(std::vector<Player*>::iterator i = m_players.begin();
66 i != m_players.end(); ++i) {
71 void Environment::addPlayer(Player *player)
73 DSTACK(__FUNCTION_NAME);
75 Check that peer_ids are unique.
76 Also check that names are unique.
77 Exception: there can be multiple players with peer_id=0
79 // If peer id is non-zero, it has to be unique.
80 if(player->peer_id != 0)
81 FATAL_ERROR_IF(getPlayer(player->peer_id) != NULL, "Peer id not unique");
82 // Name has to be unique.
83 FATAL_ERROR_IF(getPlayer(player->getName()) != NULL, "Player name not unique");
85 m_players.push_back(player);
88 void Environment::removePlayer(u16 peer_id)
90 DSTACK(__FUNCTION_NAME);
92 for(std::vector<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::vector<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::vector<Player*>::iterator i = m_players.begin();
120 i != m_players.end(); ++i) {
122 if(player->peer_id == peer_id)
128 Player * Environment::getPlayer(const char *name)
130 for(std::vector<Player*>::iterator i = m_players.begin();
131 i != m_players.end(); ++i) {
133 if(strcmp(player->getName(), name) == 0)
139 Player * Environment::getRandomConnectedPlayer()
141 std::vector<Player*> connected_players = getPlayers(true);
142 u32 chosen_one = myrand() % connected_players.size();
144 for(std::vector<Player*>::iterator
145 i = connected_players.begin();
146 i != connected_players.end(); ++i) {
147 if(j == chosen_one) {
156 Player * Environment::getNearestConnectedPlayer(v3f pos)
158 std::vector<Player*> connected_players = getPlayers(true);
160 Player *nearest_player = NULL;
161 for(std::vector<Player*>::iterator
162 i = connected_players.begin();
163 i != connected_players.end(); ++i) {
165 f32 d = player->getPosition().getDistanceFrom(pos);
166 if(d < nearest_d || nearest_player == NULL) {
168 nearest_player = player;
171 return nearest_player;
174 std::vector<Player*> Environment::getPlayers()
179 std::vector<Player*> Environment::getPlayers(bool ignore_disconnected)
181 std::vector<Player*> newlist;
182 for(std::vector<Player*>::iterator
183 i = m_players.begin();
184 i != m_players.end(); ++i) {
187 if(ignore_disconnected) {
188 // Ignore disconnected players
189 if(player->peer_id == 0)
193 newlist.push_back(player);
198 u32 Environment::getDayNightRatio()
200 if(m_enable_day_night_ratio_override)
201 return m_day_night_ratio_override;
202 return time_to_daynight_ratio(m_time_of_day_f*24000, m_cache_enable_shaders);
205 void Environment::setTimeOfDaySpeed(float speed)
207 JMutexAutoLock(this->m_timeofday_lock);
208 m_time_of_day_speed = speed;
211 float Environment::getTimeOfDaySpeed()
213 JMutexAutoLock(this->m_timeofday_lock);
214 float retval = m_time_of_day_speed;
218 void Environment::setTimeOfDay(u32 time)
220 JMutexAutoLock(this->m_time_lock);
221 m_time_of_day = time;
222 m_time_of_day_f = (float)time / 24000.0;
225 u32 Environment::getTimeOfDay()
227 JMutexAutoLock(this->m_time_lock);
228 u32 retval = m_time_of_day;
232 float Environment::getTimeOfDayF()
234 JMutexAutoLock(this->m_time_lock);
235 float retval = m_time_of_day_f;
239 void Environment::stepTimeOfDay(float dtime)
241 // getTimeOfDaySpeed lock the value we need to prevent MT problems
242 float day_speed = getTimeOfDaySpeed();
244 m_time_counter += dtime;
245 f32 speed = day_speed * 24000./(24.*3600);
246 u32 units = (u32)(m_time_counter*speed);
250 if(m_time_of_day + units >= 24000)
252 m_time_of_day = (m_time_of_day + units) % 24000;
254 m_time_of_day_f = (float)m_time_of_day / 24000.0;
257 m_time_counter -= (f32)units / speed;
260 m_time_of_day_f += day_speed/24/3600*dtime;
261 if(m_time_of_day_f > 1.0)
262 m_time_of_day_f -= 1.0;
263 if(m_time_of_day_f < 0.0)
264 m_time_of_day_f += 1.0;
272 ABMWithState::ABMWithState(ActiveBlockModifier *abm_):
276 // Initialize timer to random value to spread processing
277 float itv = abm->getTriggerInterval();
278 itv = MYMAX(0.001, itv); // No less than 1ms
279 int minval = MYMAX(-0.51*itv, -60); // Clamp to
280 int maxval = MYMIN(0.51*itv, 60); // +-60 seconds
281 timer = myrand_range(minval, maxval);
288 void fillRadiusBlock(v3s16 p0, s16 r, std::set<v3s16> &list)
291 for(p.X=p0.X-r; p.X<=p0.X+r; p.X++)
292 for(p.Y=p0.Y-r; p.Y<=p0.Y+r; p.Y++)
293 for(p.Z=p0.Z-r; p.Z<=p0.Z+r; p.Z++)
300 void ActiveBlockList::update(std::vector<v3s16> &active_positions,
302 std::set<v3s16> &blocks_removed,
303 std::set<v3s16> &blocks_added)
308 std::set<v3s16> newlist = m_forceloaded_list;
309 for(std::vector<v3s16>::iterator i = active_positions.begin();
310 i != active_positions.end(); ++i)
312 fillRadiusBlock(*i, radius, newlist);
316 Find out which blocks on the old list are not on the new list
318 // Go through old list
319 for(std::set<v3s16>::iterator i = m_list.begin();
320 i != m_list.end(); ++i)
323 // If not on new list, it's been removed
324 if(newlist.find(p) == newlist.end())
325 blocks_removed.insert(p);
329 Find out which blocks on the new list are not on the old list
331 // Go through new list
332 for(std::set<v3s16>::iterator i = newlist.begin();
333 i != newlist.end(); ++i)
336 // If not on old list, it's been added
337 if(m_list.find(p) == m_list.end())
338 blocks_added.insert(p);
345 for(std::set<v3s16>::iterator i = newlist.begin();
346 i != newlist.end(); ++i)
357 ServerEnvironment::ServerEnvironment(ServerMap *map,
358 GameScripting *scriptIface, IGameDef *gamedef,
359 const std::string &path_world) :
361 m_script(scriptIface),
363 m_path_world(path_world),
364 m_send_recommended_timer(0),
365 m_active_block_interval_overload_skip(0),
367 m_game_time_fraction_counter(0),
368 m_recommended_send_interval(0.1),
369 m_max_lag_estimate(0.1)
373 ServerEnvironment::~ServerEnvironment()
375 // Clear active block list.
376 // This makes the next one delete all active objects.
377 m_active_blocks.clear();
379 // Convert all objects to static and delete the active objects
380 deactivateFarObjects(true);
385 // Delete ActiveBlockModifiers
386 for(std::vector<ABMWithState>::iterator
387 i = m_abms.begin(); i != m_abms.end(); ++i){
392 Map & ServerEnvironment::getMap()
397 ServerMap & ServerEnvironment::getServerMap()
402 bool ServerEnvironment::line_of_sight(v3f pos1, v3f pos2, float stepsize, v3s16 *p)
404 float distance = pos1.getDistanceFrom(pos2);
406 //calculate normalized direction vector
407 v3f normalized_vector = v3f((pos2.X - pos1.X)/distance,
408 (pos2.Y - pos1.Y)/distance,
409 (pos2.Z - pos1.Z)/distance);
411 //find out if there's a node on path between pos1 and pos2
412 for (float i = 1; i < distance; i += stepsize) {
413 v3s16 pos = floatToInt(v3f(normalized_vector.X * i,
414 normalized_vector.Y * i,
415 normalized_vector.Z * i) +pos1,BS);
417 MapNode n = getMap().getNodeNoEx(pos);
419 if(n.param0 != CONTENT_AIR) {
429 void ServerEnvironment::kickAllPlayers(AccessDeniedCode reason,
430 const std::string &str_reason, bool reconnect)
432 for (std::vector<Player*>::iterator it = m_players.begin();
433 it != m_players.end();
435 ((Server*)m_gamedef)->DenyAccessVerCompliant((*it)->peer_id,
436 (*it)->protocol_version, (AccessDeniedCode)reason,
437 str_reason, reconnect);
441 void ServerEnvironment::saveLoadedPlayers()
443 std::string players_path = m_path_world + DIR_DELIM "players";
444 fs::CreateDir(players_path);
446 for (std::vector<Player*>::iterator it = m_players.begin();
447 it != m_players.end();
449 RemotePlayer *player = static_cast<RemotePlayer*>(*it);
450 if (player->checkModified()) {
451 player->save(players_path);
456 void ServerEnvironment::savePlayer(const std::string &playername)
458 std::string players_path = m_path_world + DIR_DELIM "players";
459 fs::CreateDir(players_path);
461 RemotePlayer *player = static_cast<RemotePlayer*>(getPlayer(playername.c_str()));
463 player->save(players_path);
467 Player *ServerEnvironment::loadPlayer(const std::string &playername)
469 bool newplayer = false;
471 std::string players_path = m_path_world + DIR_DELIM "players" DIR_DELIM;
472 std::string path = players_path + playername;
474 RemotePlayer *player = static_cast<RemotePlayer *>(getPlayer(playername.c_str()));
476 player = new RemotePlayer(m_gamedef, "");
480 for (u32 i = 0; i < PLAYER_FILE_ALTERNATE_TRIES; i++) {
481 //// Open file and deserialize
482 std::ifstream is(path.c_str(), std::ios_base::binary);
485 player->deSerialize(is, path);
488 if (player->getName() == playername) {
493 path = players_path + playername + itos(i);
497 infostream << "Player file for player " << playername
498 << " not found" << std::endl;
506 player->setModified(false);
510 void ServerEnvironment::saveMeta()
512 std::string path = m_path_world + DIR_DELIM "env_meta.txt";
514 // Open file and serialize
515 std::ostringstream ss(std::ios_base::binary);
518 args.setU64("game_time", m_game_time);
519 args.setU64("time_of_day", getTimeOfDay());
523 if(!fs::safeWriteToFile(path, ss.str()))
525 infostream<<"ServerEnvironment::saveMeta(): Failed to write "
527 throw SerializationError("Couldn't save env meta");
531 void ServerEnvironment::loadMeta()
533 std::string path = m_path_world + DIR_DELIM "env_meta.txt";
535 // Open file and deserialize
536 std::ifstream is(path.c_str(), std::ios_base::binary);
538 infostream << "ServerEnvironment::loadMeta(): Failed to open "
539 << path << std::endl;
540 throw SerializationError("Couldn't load env meta");
545 if (!args.parseConfigLines(is, "EnvArgsEnd")) {
546 throw SerializationError("ServerEnvironment::loadMeta(): "
547 "EnvArgsEnd not found!");
551 m_game_time = args.getU64("game_time");
552 } catch (SettingNotFoundException &e) {
553 // Getting this is crucial, otherwise timestamps are useless
554 throw SerializationError("Couldn't load env meta game_time");
558 m_time_of_day = args.getU64("time_of_day");
559 } catch (SettingNotFoundException &e) {
560 // This is not as important
561 m_time_of_day = 9000;
567 ActiveBlockModifier *abm;
569 std::set<content_t> required_neighbors;
575 ServerEnvironment *m_env;
576 std::map<content_t, std::vector<ActiveABM> > m_aabms;
578 ABMHandler(std::vector<ABMWithState> &abms,
579 float dtime_s, ServerEnvironment *env,
585 INodeDefManager *ndef = env->getGameDef()->ndef();
586 for(std::vector<ABMWithState>::iterator
587 i = abms.begin(); i != abms.end(); ++i) {
588 ActiveBlockModifier *abm = i->abm;
589 float trigger_interval = abm->getTriggerInterval();
590 if(trigger_interval < 0.001)
591 trigger_interval = 0.001;
592 float actual_interval = dtime_s;
595 if(i->timer < trigger_interval)
597 i->timer -= trigger_interval;
598 actual_interval = trigger_interval;
600 float intervals = actual_interval / trigger_interval;
603 float chance = abm->getTriggerChance();
608 aabm.chance = chance / intervals;
612 std::set<std::string> required_neighbors_s
613 = abm->getRequiredNeighbors();
614 for(std::set<std::string>::iterator
615 i = required_neighbors_s.begin();
616 i != required_neighbors_s.end(); i++)
618 ndef->getIds(*i, aabm.required_neighbors);
621 std::set<std::string> contents_s = abm->getTriggerContents();
622 for(std::set<std::string>::iterator
623 i = contents_s.begin(); i != contents_s.end(); i++)
625 std::set<content_t> ids;
626 ndef->getIds(*i, ids);
627 for(std::set<content_t>::const_iterator k = ids.begin();
631 std::map<content_t, std::vector<ActiveABM> >::iterator j;
633 if(j == m_aabms.end()){
634 std::vector<ActiveABM> aabmlist;
635 m_aabms[c] = aabmlist;
638 j->second.push_back(aabm);
643 // Find out how many objects the given block and its neighbours contain.
644 // Returns the number of objects in the block, and also in 'wider' the
645 // number of objects in the block and all its neighbours. The latter
646 // may an estimate if any neighbours are unloaded.
647 u32 countObjects(MapBlock *block, ServerMap * map, u32 &wider)
650 u32 wider_unknown_count = 0;
651 for(s16 x=-1; x<=1; x++)
652 for(s16 y=-1; y<=1; y++)
653 for(s16 z=-1; z<=1; z++)
655 MapBlock *block2 = map->getBlockNoCreateNoEx(
656 block->getPos() + v3s16(x,y,z));
658 wider_unknown_count++;
661 wider += block2->m_static_objects.m_active.size()
662 + block2->m_static_objects.m_stored.size();
665 u32 active_object_count = block->m_static_objects.m_active.size();
666 u32 wider_known_count = 3*3*3 - wider_unknown_count;
667 wider += wider_unknown_count * wider / wider_known_count;
668 return active_object_count;
671 void apply(MapBlock *block)
676 ServerMap *map = &m_env->getServerMap();
678 u32 active_object_count_wider;
679 u32 active_object_count = this->countObjects(block, map, active_object_count_wider);
680 m_env->m_added_objects = 0;
683 for(p0.X=0; p0.X<MAP_BLOCKSIZE; p0.X++)
684 for(p0.Y=0; p0.Y<MAP_BLOCKSIZE; p0.Y++)
685 for(p0.Z=0; p0.Z<MAP_BLOCKSIZE; p0.Z++)
687 MapNode n = block->getNodeNoEx(p0);
688 content_t c = n.getContent();
689 v3s16 p = p0 + block->getPosRelative();
691 std::map<content_t, std::vector<ActiveABM> >::iterator j;
693 if(j == m_aabms.end())
696 for(std::vector<ActiveABM>::iterator
697 i = j->second.begin(); i != j->second.end(); i++) {
698 if(myrand() % i->chance != 0)
702 if(!i->required_neighbors.empty())
705 for(p1.X = p.X-1; p1.X <= p.X+1; p1.X++)
706 for(p1.Y = p.Y-1; p1.Y <= p.Y+1; p1.Y++)
707 for(p1.Z = p.Z-1; p1.Z <= p.Z+1; p1.Z++)
711 MapNode n = map->getNodeNoEx(p1);
712 content_t c = n.getContent();
713 std::set<content_t>::const_iterator k;
714 k = i->required_neighbors.find(c);
715 if(k != i->required_neighbors.end()){
719 // No required neighbor found
724 // Call all the trigger variations
725 i->abm->trigger(m_env, p, n);
726 i->abm->trigger(m_env, p, n,
727 active_object_count, active_object_count_wider);
729 // Count surrounding objects again if the abms added any
730 if(m_env->m_added_objects > 0) {
731 active_object_count = countObjects(block, map, active_object_count_wider);
732 m_env->m_added_objects = 0;
739 void ServerEnvironment::activateBlock(MapBlock *block, u32 additional_dtime)
741 // Reset usage timer immediately, otherwise a block that becomes active
742 // again at around the same time as it would normally be unloaded will
743 // get unloaded incorrectly. (I think this still leaves a small possibility
744 // of a race condition between this and server::AsyncRunStep, which only
745 // some kind of synchronisation will fix, but it at least reduces the window
746 // of opportunity for it to break from seconds to nanoseconds)
747 block->resetUsageTimer();
749 // Get time difference
751 u32 stamp = block->getTimestamp();
752 if(m_game_time > stamp && stamp != BLOCK_TIMESTAMP_UNDEFINED)
753 dtime_s = m_game_time - block->getTimestamp();
754 dtime_s += additional_dtime;
756 /*infostream<<"ServerEnvironment::activateBlock(): block timestamp: "
757 <<stamp<<", game time: "<<m_game_time<<std::endl;*/
759 // Set current time as timestamp
760 block->setTimestampNoChangedFlag(m_game_time);
762 /*infostream<<"ServerEnvironment::activateBlock(): block is "
763 <<dtime_s<<" seconds old."<<std::endl;*/
765 // Activate stored objects
766 activateObjects(block, dtime_s);
769 std::map<v3s16, NodeTimer> elapsed_timers =
770 block->m_node_timers.step((float)dtime_s);
771 if(!elapsed_timers.empty()){
773 for(std::map<v3s16, NodeTimer>::iterator
774 i = elapsed_timers.begin();
775 i != elapsed_timers.end(); i++){
776 n = block->getNodeNoEx(i->first);
777 v3s16 p = i->first + block->getPosRelative();
778 if(m_script->node_on_timer(p,n,i->second.elapsed))
779 block->setNodeTimer(i->first,NodeTimer(i->second.timeout,0));
783 /* Handle ActiveBlockModifiers */
784 ABMHandler abmhandler(m_abms, dtime_s, this, false);
785 abmhandler.apply(block);
788 void ServerEnvironment::addActiveBlockModifier(ActiveBlockModifier *abm)
790 m_abms.push_back(ABMWithState(abm));
793 bool ServerEnvironment::setNode(v3s16 p, const MapNode &n)
795 INodeDefManager *ndef = m_gamedef->ndef();
796 MapNode n_old = m_map->getNodeNoEx(p);
799 if (ndef->get(n_old).has_on_destruct)
800 m_script->node_on_destruct(p, n_old);
803 if (!m_map->addNodeWithEvent(p, n))
806 // Update active VoxelManipulator if a mapgen thread
807 m_map->updateVManip(p);
809 // Call post-destructor
810 if (ndef->get(n_old).has_after_destruct)
811 m_script->node_after_destruct(p, n_old);
814 if (ndef->get(n).has_on_construct)
815 m_script->node_on_construct(p, n);
820 bool ServerEnvironment::removeNode(v3s16 p)
822 INodeDefManager *ndef = m_gamedef->ndef();
823 MapNode n_old = m_map->getNodeNoEx(p);
826 if (ndef->get(n_old).has_on_destruct)
827 m_script->node_on_destruct(p, n_old);
830 // This is slightly optimized compared to addNodeWithEvent(air)
831 if (!m_map->removeNodeWithEvent(p))
834 // Update active VoxelManipulator if a mapgen thread
835 m_map->updateVManip(p);
837 // Call post-destructor
838 if (ndef->get(n_old).has_after_destruct)
839 m_script->node_after_destruct(p, n_old);
841 // Air doesn't require constructor
845 bool ServerEnvironment::swapNode(v3s16 p, const MapNode &n)
847 if (!m_map->addNodeWithEvent(p, n, false))
850 // Update active VoxelManipulator if a mapgen thread
851 m_map->updateVManip(p);
856 void ServerEnvironment::getObjectsInsideRadius(std::vector<u16> &objects, v3f pos, float radius)
858 for(std::map<u16, ServerActiveObject*>::iterator
859 i = m_active_objects.begin();
860 i != m_active_objects.end(); ++i)
862 ServerActiveObject* obj = i->second;
864 v3f objectpos = obj->getBasePosition();
865 if(objectpos.getDistanceFrom(pos) > radius)
867 objects.push_back(id);
871 void ServerEnvironment::clearAllObjects()
873 infostream<<"ServerEnvironment::clearAllObjects(): "
874 <<"Removing all active objects"<<std::endl;
875 std::vector<u16> objects_to_remove;
876 for(std::map<u16, ServerActiveObject*>::iterator
877 i = m_active_objects.begin();
878 i != m_active_objects.end(); ++i) {
879 ServerActiveObject* obj = i->second;
880 if(obj->getType() == ACTIVEOBJECT_TYPE_PLAYER)
883 // Delete static object if block is loaded
884 if(obj->m_static_exists){
885 MapBlock *block = m_map->getBlockNoCreateNoEx(obj->m_static_block);
887 block->m_static_objects.remove(id);
888 block->raiseModified(MOD_STATE_WRITE_NEEDED,
889 MOD_REASON_CLEAR_ALL_OBJECTS);
890 obj->m_static_exists = false;
893 // If known by some client, don't delete immediately
894 if(obj->m_known_by_count > 0){
895 obj->m_pending_deactivation = true;
896 obj->m_removed = true;
900 // Tell the object about removal
901 obj->removingFromEnvironment();
902 // Deregister in scripting api
903 m_script->removeObjectReference(obj);
905 // Delete active object
906 if(obj->environmentDeletes())
908 // Id to be removed from m_active_objects
909 objects_to_remove.push_back(id);
912 // Remove references from m_active_objects
913 for(std::vector<u16>::iterator i = objects_to_remove.begin();
914 i != objects_to_remove.end(); ++i) {
915 m_active_objects.erase(*i);
918 // Get list of loaded blocks
919 std::vector<v3s16> loaded_blocks;
920 infostream<<"ServerEnvironment::clearAllObjects(): "
921 <<"Listing all loaded blocks"<<std::endl;
922 m_map->listAllLoadedBlocks(loaded_blocks);
923 infostream<<"ServerEnvironment::clearAllObjects(): "
924 <<"Done listing all loaded blocks: "
925 <<loaded_blocks.size()<<std::endl;
927 // Get list of loadable blocks
928 std::vector<v3s16> loadable_blocks;
929 infostream<<"ServerEnvironment::clearAllObjects(): "
930 <<"Listing all loadable blocks"<<std::endl;
931 m_map->listAllLoadableBlocks(loadable_blocks);
932 infostream<<"ServerEnvironment::clearAllObjects(): "
933 <<"Done listing all loadable blocks: "
934 <<loadable_blocks.size()
935 <<", now clearing"<<std::endl;
937 // Grab a reference on each loaded block to avoid unloading it
938 for(std::vector<v3s16>::iterator i = loaded_blocks.begin();
939 i != loaded_blocks.end(); ++i) {
941 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
942 assert(block != NULL);
946 // Remove objects in all loadable blocks
947 u32 unload_interval = g_settings->getS32("max_clearobjects_extra_loaded_blocks");
948 unload_interval = MYMAX(unload_interval, 1);
949 u32 report_interval = loadable_blocks.size() / 10;
950 u32 num_blocks_checked = 0;
951 u32 num_blocks_cleared = 0;
952 u32 num_objs_cleared = 0;
953 for(std::vector<v3s16>::iterator i = loadable_blocks.begin();
954 i != loadable_blocks.end(); ++i) {
956 MapBlock *block = m_map->emergeBlock(p, false);
958 errorstream<<"ServerEnvironment::clearAllObjects(): "
959 <<"Failed to emerge block "<<PP(p)<<std::endl;
962 u32 num_stored = block->m_static_objects.m_stored.size();
963 u32 num_active = block->m_static_objects.m_active.size();
964 if(num_stored != 0 || num_active != 0){
965 block->m_static_objects.m_stored.clear();
966 block->m_static_objects.m_active.clear();
967 block->raiseModified(MOD_STATE_WRITE_NEEDED,
968 MOD_REASON_CLEAR_ALL_OBJECTS);
969 num_objs_cleared += num_stored + num_active;
970 num_blocks_cleared++;
972 num_blocks_checked++;
974 if(report_interval != 0 &&
975 num_blocks_checked % report_interval == 0){
976 float percent = 100.0 * (float)num_blocks_checked /
977 loadable_blocks.size();
978 infostream<<"ServerEnvironment::clearAllObjects(): "
979 <<"Cleared "<<num_objs_cleared<<" objects"
980 <<" in "<<num_blocks_cleared<<" blocks ("
981 <<percent<<"%)"<<std::endl;
983 if(num_blocks_checked % unload_interval == 0){
984 m_map->unloadUnreferencedBlocks();
987 m_map->unloadUnreferencedBlocks();
989 // Drop references that were added above
990 for(std::vector<v3s16>::iterator i = loaded_blocks.begin();
991 i != loaded_blocks.end(); ++i) {
993 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
998 infostream<<"ServerEnvironment::clearAllObjects(): "
999 <<"Finished: Cleared "<<num_objs_cleared<<" objects"
1000 <<" in "<<num_blocks_cleared<<" blocks"<<std::endl;
1003 void ServerEnvironment::step(float dtime)
1005 DSTACK(__FUNCTION_NAME);
1007 //TimeTaker timer("ServerEnv step");
1009 /* Step time of day */
1010 stepTimeOfDay(dtime);
1013 // NOTE: This is kind of funny on a singleplayer game, but doesn't
1014 // really matter that much.
1015 m_recommended_send_interval = g_settings->getFloat("dedicated_server_step");
1021 m_game_time_fraction_counter += dtime;
1022 u32 inc_i = (u32)m_game_time_fraction_counter;
1023 m_game_time += inc_i;
1024 m_game_time_fraction_counter -= (float)inc_i;
1031 ScopeProfiler sp(g_profiler, "SEnv: handle players avg", SPT_AVG);
1032 for(std::vector<Player*>::iterator i = m_players.begin();
1033 i != m_players.end(); ++i)
1035 Player *player = *i;
1037 // Ignore disconnected players
1038 if(player->peer_id == 0)
1042 player->move(dtime, this, 100*BS);
1047 Manage active block list
1049 if(m_active_blocks_management_interval.step(dtime, 2.0))
1051 ScopeProfiler sp(g_profiler, "SEnv: manage act. block list avg /2s", SPT_AVG);
1053 Get player block positions
1055 std::vector<v3s16> players_blockpos;
1056 for(std::vector<Player*>::iterator
1057 i = m_players.begin();
1058 i != m_players.end(); ++i) {
1059 Player *player = *i;
1060 // Ignore disconnected players
1061 if(player->peer_id == 0)
1064 v3s16 blockpos = getNodeBlockPos(
1065 floatToInt(player->getPosition(), BS));
1066 players_blockpos.push_back(blockpos);
1070 Update list of active blocks, collecting changes
1072 const s16 active_block_range = g_settings->getS16("active_block_range");
1073 std::set<v3s16> blocks_removed;
1074 std::set<v3s16> blocks_added;
1075 m_active_blocks.update(players_blockpos, active_block_range,
1076 blocks_removed, blocks_added);
1079 Handle removed blocks
1082 // Convert active objects that are no more in active blocks to static
1083 deactivateFarObjects(false);
1085 for(std::set<v3s16>::iterator
1086 i = blocks_removed.begin();
1087 i != blocks_removed.end(); ++i)
1091 /* infostream<<"Server: Block " << PP(p)
1092 << " became inactive"<<std::endl; */
1094 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1098 // Set current time as timestamp (and let it set ChangedFlag)
1099 block->setTimestamp(m_game_time);
1106 for(std::set<v3s16>::iterator
1107 i = blocks_added.begin();
1108 i != blocks_added.end(); ++i)
1112 MapBlock *block = m_map->getBlockOrEmerge(p);
1114 m_active_blocks.m_list.erase(p);
1118 activateBlock(block);
1119 /* infostream<<"Server: Block " << PP(p)
1120 << " became active"<<std::endl; */
1125 Mess around in active blocks
1127 if(m_active_blocks_nodemetadata_interval.step(dtime, 1.0))
1129 ScopeProfiler sp(g_profiler, "SEnv: mess in act. blocks avg /1s", SPT_AVG);
1133 for(std::set<v3s16>::iterator
1134 i = m_active_blocks.m_list.begin();
1135 i != m_active_blocks.m_list.end(); ++i)
1139 /*infostream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
1140 <<") being handled"<<std::endl;*/
1142 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1146 // Reset block usage timer
1147 block->resetUsageTimer();
1149 // Set current time as timestamp
1150 block->setTimestampNoChangedFlag(m_game_time);
1151 // If time has changed much from the one on disk,
1152 // set block to be saved when it is unloaded
1153 if(block->getTimestamp() > block->getDiskTimestamp() + 60)
1154 block->raiseModified(MOD_STATE_WRITE_AT_UNLOAD,
1155 MOD_REASON_BLOCK_EXPIRED);
1158 std::map<v3s16, NodeTimer> elapsed_timers =
1159 block->m_node_timers.step((float)dtime);
1160 if(!elapsed_timers.empty()){
1162 for(std::map<v3s16, NodeTimer>::iterator
1163 i = elapsed_timers.begin();
1164 i != elapsed_timers.end(); i++){
1165 n = block->getNodeNoEx(i->first);
1166 p = i->first + block->getPosRelative();
1167 if(m_script->node_on_timer(p,n,i->second.elapsed))
1168 block->setNodeTimer(i->first,NodeTimer(i->second.timeout,0));
1174 const float abm_interval = 1.0;
1175 if(m_active_block_modifier_interval.step(dtime, abm_interval))
1177 if(m_active_block_interval_overload_skip > 0){
1178 ScopeProfiler sp(g_profiler, "SEnv: ABM overload skips");
1179 m_active_block_interval_overload_skip--;
1182 ScopeProfiler sp(g_profiler, "SEnv: modify in blocks avg /1s", SPT_AVG);
1183 TimeTaker timer("modify in active blocks");
1185 // Initialize handling of ActiveBlockModifiers
1186 ABMHandler abmhandler(m_abms, abm_interval, this, true);
1188 for(std::set<v3s16>::iterator
1189 i = m_active_blocks.m_list.begin();
1190 i != m_active_blocks.m_list.end(); ++i)
1194 /*infostream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
1195 <<") being handled"<<std::endl;*/
1197 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1201 // Set current time as timestamp
1202 block->setTimestampNoChangedFlag(m_game_time);
1204 /* Handle ActiveBlockModifiers */
1205 abmhandler.apply(block);
1208 u32 time_ms = timer.stop(true);
1209 u32 max_time_ms = 200;
1210 if(time_ms > max_time_ms){
1211 infostream<<"WARNING: active block modifiers took "
1212 <<time_ms<<"ms (longer than "
1213 <<max_time_ms<<"ms)"<<std::endl;
1214 m_active_block_interval_overload_skip = (time_ms / max_time_ms) + 1;
1219 Step script environment (run global on_step())
1221 m_script->environment_Step(dtime);
1227 ScopeProfiler sp(g_profiler, "SEnv: step act. objs avg", SPT_AVG);
1228 //TimeTaker timer("Step active objects");
1230 g_profiler->avg("SEnv: num of objects", m_active_objects.size());
1232 // This helps the objects to send data at the same time
1233 bool send_recommended = false;
1234 m_send_recommended_timer += dtime;
1235 if(m_send_recommended_timer > getSendRecommendedInterval())
1237 m_send_recommended_timer -= getSendRecommendedInterval();
1238 send_recommended = true;
1241 for(std::map<u16, ServerActiveObject*>::iterator
1242 i = m_active_objects.begin();
1243 i != m_active_objects.end(); ++i)
1245 ServerActiveObject* obj = i->second;
1246 // Don't step if is to be removed or stored statically
1247 if(obj->m_removed || obj->m_pending_deactivation)
1250 obj->step(dtime, send_recommended);
1251 // Read messages from object
1252 while(!obj->m_messages_out.empty())
1254 m_active_object_messages.push(
1255 obj->m_messages_out.front());
1256 obj->m_messages_out.pop();
1262 Manage active objects
1264 if(m_object_management_interval.step(dtime, 0.5))
1266 ScopeProfiler sp(g_profiler, "SEnv: remove removed objs avg /.5s", SPT_AVG);
1268 Remove objects that satisfy (m_removed && m_known_by_count==0)
1270 removeRemovedObjects();
1274 ServerActiveObject* ServerEnvironment::getActiveObject(u16 id)
1276 std::map<u16, ServerActiveObject*>::iterator n;
1277 n = m_active_objects.find(id);
1278 if(n == m_active_objects.end())
1283 bool isFreeServerActiveObjectId(u16 id,
1284 std::map<u16, ServerActiveObject*> &objects)
1289 return objects.find(id) == objects.end();
1292 u16 getFreeServerActiveObjectId(
1293 std::map<u16, ServerActiveObject*> &objects)
1295 //try to reuse id's as late as possible
1296 static u16 last_used_id = 0;
1297 u16 startid = last_used_id;
1301 if(isFreeServerActiveObjectId(last_used_id, objects))
1302 return last_used_id;
1304 if(last_used_id == startid)
1309 u16 ServerEnvironment::addActiveObject(ServerActiveObject *object)
1311 assert(object); // Pre-condition
1313 u16 id = addActiveObjectRaw(object, true, 0);
1318 Finds out what new objects have been added to
1319 inside a radius around a position
1321 void ServerEnvironment::getAddedActiveObjects(v3s16 pos, s16 radius,
1323 std::set<u16> ¤t_objects,
1324 std::set<u16> &added_objects)
1326 v3f pos_f = intToFloat(pos, BS);
1327 f32 radius_f = radius * BS;
1328 f32 player_radius_f = player_radius * BS;
1330 if (player_radius_f < 0)
1331 player_radius_f = 0;
1334 Go through the object list,
1335 - discard m_removed objects,
1336 - discard objects that are too far away,
1337 - discard objects that are found in current_objects.
1338 - add remaining objects to added_objects
1340 for(std::map<u16, ServerActiveObject*>::iterator
1341 i = m_active_objects.begin();
1342 i != m_active_objects.end(); ++i)
1346 ServerActiveObject *object = i->second;
1349 // Discard if removed or deactivating
1350 if(object->m_removed || object->m_pending_deactivation)
1353 f32 distance_f = object->getBasePosition().getDistanceFrom(pos_f);
1354 if (object->getType() == ACTIVEOBJECT_TYPE_PLAYER) {
1355 // Discard if too far
1356 if (distance_f > player_radius_f && player_radius_f != 0)
1358 } else if (distance_f > radius_f)
1361 // Discard if already on current_objects
1362 std::set<u16>::iterator n;
1363 n = current_objects.find(id);
1364 if(n != current_objects.end())
1366 // Add to added_objects
1367 added_objects.insert(id);
1372 Finds out what objects have been removed from
1373 inside a radius around a position
1375 void ServerEnvironment::getRemovedActiveObjects(v3s16 pos, s16 radius,
1377 std::set<u16> ¤t_objects,
1378 std::set<u16> &removed_objects)
1380 v3f pos_f = intToFloat(pos, BS);
1381 f32 radius_f = radius * BS;
1382 f32 player_radius_f = player_radius * BS;
1384 if (player_radius_f < 0)
1385 player_radius_f = 0;
1388 Go through current_objects; object is removed if:
1389 - object is not found in m_active_objects (this is actually an
1390 error condition; objects should be set m_removed=true and removed
1391 only after all clients have been informed about removal), or
1392 - object has m_removed=true, or
1393 - object is too far away
1395 for(std::set<u16>::iterator
1396 i = current_objects.begin();
1397 i != current_objects.end(); ++i)
1400 ServerActiveObject *object = getActiveObject(id);
1403 infostream<<"ServerEnvironment::getRemovedActiveObjects():"
1404 <<" object in current_objects is NULL"<<std::endl;
1405 removed_objects.insert(id);
1409 if(object->m_removed || object->m_pending_deactivation)
1411 removed_objects.insert(id);
1415 f32 distance_f = object->getBasePosition().getDistanceFrom(pos_f);
1416 if (object->getType() == ACTIVEOBJECT_TYPE_PLAYER) {
1417 if (distance_f <= player_radius_f || player_radius_f == 0)
1419 } else if (distance_f <= radius_f)
1422 // Object is no longer visible
1423 removed_objects.insert(id);
1427 ActiveObjectMessage ServerEnvironment::getActiveObjectMessage()
1429 if(m_active_object_messages.empty())
1430 return ActiveObjectMessage(0);
1432 ActiveObjectMessage message = m_active_object_messages.front();
1433 m_active_object_messages.pop();
1438 ************ Private methods *************
1441 u16 ServerEnvironment::addActiveObjectRaw(ServerActiveObject *object,
1442 bool set_changed, u32 dtime_s)
1444 assert(object); // Pre-condition
1445 if(object->getId() == 0){
1446 u16 new_id = getFreeServerActiveObjectId(m_active_objects);
1449 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1450 <<"no free ids available"<<std::endl;
1451 if(object->environmentDeletes())
1455 object->setId(new_id);
1458 verbosestream<<"ServerEnvironment::addActiveObjectRaw(): "
1459 <<"supplied with id "<<object->getId()<<std::endl;
1461 if(isFreeServerActiveObjectId(object->getId(), m_active_objects) == false)
1463 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1464 <<"id is not free ("<<object->getId()<<")"<<std::endl;
1465 if(object->environmentDeletes())
1469 /*infostream<<"ServerEnvironment::addActiveObjectRaw(): "
1470 <<"added (id="<<object->getId()<<")"<<std::endl;*/
1472 m_active_objects[object->getId()] = object;
1474 verbosestream<<"ServerEnvironment::addActiveObjectRaw(): "
1475 <<"Added id="<<object->getId()<<"; there are now "
1476 <<m_active_objects.size()<<" active objects."
1479 // Register reference in scripting api (must be done before post-init)
1480 m_script->addObjectReference(object);
1481 // Post-initialize object
1482 object->addedToEnvironment(dtime_s);
1484 // Add static data to block
1485 if(object->isStaticAllowed())
1487 // Add static object to active static list of the block
1488 v3f objectpos = object->getBasePosition();
1489 std::string staticdata = object->getStaticData();
1490 StaticObject s_obj(object->getType(), objectpos, staticdata);
1491 // Add to the block where the object is located in
1492 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1493 MapBlock *block = m_map->emergeBlock(blockpos);
1495 block->m_static_objects.m_active[object->getId()] = s_obj;
1496 object->m_static_exists = true;
1497 object->m_static_block = blockpos;
1500 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1501 MOD_REASON_ADD_ACTIVE_OBJECT_RAW);
1503 v3s16 p = floatToInt(objectpos, BS);
1504 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1505 <<"could not emerge block for storing id="<<object->getId()
1506 <<" statically (pos="<<PP(p)<<")"<<std::endl;
1510 return object->getId();
1514 Remove objects that satisfy (m_removed && m_known_by_count==0)
1516 void ServerEnvironment::removeRemovedObjects()
1518 std::vector<u16> objects_to_remove;
1519 for(std::map<u16, ServerActiveObject*>::iterator
1520 i = m_active_objects.begin();
1521 i != m_active_objects.end(); ++i) {
1523 ServerActiveObject* obj = i->second;
1524 // This shouldn't happen but check it
1527 infostream<<"NULL object found in ServerEnvironment"
1528 <<" while finding removed objects. id="<<id<<std::endl;
1529 // Id to be removed from m_active_objects
1530 objects_to_remove.push_back(id);
1535 We will delete objects that are marked as removed or thatare
1536 waiting for deletion after deactivation
1538 if(obj->m_removed == false && obj->m_pending_deactivation == false)
1542 Delete static data from block if is marked as removed
1544 if(obj->m_static_exists && obj->m_removed)
1546 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
1548 block->m_static_objects.remove(id);
1549 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1550 MOD_REASON_REMOVE_OBJECTS_REMOVE);
1551 obj->m_static_exists = false;
1553 infostream<<"Failed to emerge block from which an object to "
1554 <<"be removed was loaded from. id="<<id<<std::endl;
1558 // If m_known_by_count > 0, don't actually remove. On some future
1559 // invocation this will be 0, which is when removal will continue.
1560 if(obj->m_known_by_count > 0)
1564 Move static data from active to stored if not marked as removed
1566 if(obj->m_static_exists && !obj->m_removed){
1567 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
1569 std::map<u16, StaticObject>::iterator i =
1570 block->m_static_objects.m_active.find(id);
1571 if(i != block->m_static_objects.m_active.end()){
1572 block->m_static_objects.m_stored.push_back(i->second);
1573 block->m_static_objects.m_active.erase(id);
1574 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1575 MOD_REASON_REMOVE_OBJECTS_DEACTIVATE);
1578 infostream<<"Failed to emerge block from which an object to "
1579 <<"be deactivated was loaded from. id="<<id<<std::endl;
1583 // Tell the object about removal
1584 obj->removingFromEnvironment();
1585 // Deregister in scripting api
1586 m_script->removeObjectReference(obj);
1589 if(obj->environmentDeletes())
1592 // Id to be removed from m_active_objects
1593 objects_to_remove.push_back(id);
1595 // Remove references from m_active_objects
1596 for(std::vector<u16>::iterator i = objects_to_remove.begin();
1597 i != objects_to_remove.end(); ++i) {
1598 m_active_objects.erase(*i);
1602 static void print_hexdump(std::ostream &o, const std::string &data)
1604 const int linelength = 16;
1605 for(int l=0; ; l++){
1606 int i0 = linelength * l;
1607 bool at_end = false;
1608 int thislinelength = linelength;
1609 if(i0 + thislinelength > (int)data.size()){
1610 thislinelength = data.size() - i0;
1613 for(int di=0; di<linelength; di++){
1616 if(di<thislinelength)
1617 snprintf(buf, 4, "%.2x ", data[i]);
1619 snprintf(buf, 4, " ");
1623 for(int di=0; di<thislinelength; di++){
1637 Convert stored objects from blocks near the players to active.
1639 void ServerEnvironment::activateObjects(MapBlock *block, u32 dtime_s)
1644 // Ignore if no stored objects (to not set changed flag)
1645 if(block->m_static_objects.m_stored.empty())
1648 verbosestream<<"ServerEnvironment::activateObjects(): "
1649 <<"activating objects of block "<<PP(block->getPos())
1650 <<" ("<<block->m_static_objects.m_stored.size()
1651 <<" objects)"<<std::endl;
1652 bool large_amount = (block->m_static_objects.m_stored.size() > g_settings->getU16("max_objects_per_block"));
1654 errorstream<<"suspiciously large amount of objects detected: "
1655 <<block->m_static_objects.m_stored.size()<<" in "
1656 <<PP(block->getPos())
1657 <<"; removing all of them."<<std::endl;
1658 // Clear stored list
1659 block->m_static_objects.m_stored.clear();
1660 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1661 MOD_REASON_TOO_MANY_OBJECTS);
1665 // Activate stored objects
1666 std::vector<StaticObject> new_stored;
1667 for (std::vector<StaticObject>::iterator
1668 i = block->m_static_objects.m_stored.begin();
1669 i != block->m_static_objects.m_stored.end(); ++i) {
1670 StaticObject &s_obj = *i;
1672 // Create an active object from the data
1673 ServerActiveObject *obj = ServerActiveObject::create
1674 ((ActiveObjectType) s_obj.type, this, 0, s_obj.pos, s_obj.data);
1675 // If couldn't create object, store static data back.
1677 errorstream<<"ServerEnvironment::activateObjects(): "
1678 <<"failed to create active object from static object "
1679 <<"in block "<<PP(s_obj.pos/BS)
1680 <<" type="<<(int)s_obj.type<<" data:"<<std::endl;
1681 print_hexdump(verbosestream, s_obj.data);
1683 new_stored.push_back(s_obj);
1686 verbosestream<<"ServerEnvironment::activateObjects(): "
1687 <<"activated static object pos="<<PP(s_obj.pos/BS)
1688 <<" type="<<(int)s_obj.type<<std::endl;
1689 // This will also add the object to the active static list
1690 addActiveObjectRaw(obj, false, dtime_s);
1692 // Clear stored list
1693 block->m_static_objects.m_stored.clear();
1694 // Add leftover failed stuff to stored list
1695 for(std::vector<StaticObject>::iterator
1696 i = new_stored.begin();
1697 i != new_stored.end(); ++i) {
1698 StaticObject &s_obj = *i;
1699 block->m_static_objects.m_stored.push_back(s_obj);
1702 // Turn the active counterparts of activated objects not pending for
1704 for(std::map<u16, StaticObject>::iterator
1705 i = block->m_static_objects.m_active.begin();
1706 i != block->m_static_objects.m_active.end(); ++i)
1709 ServerActiveObject *object = getActiveObject(id);
1711 object->m_pending_deactivation = false;
1715 Note: Block hasn't really been modified here.
1716 The objects have just been activated and moved from the stored
1717 static list to the active static list.
1718 As such, the block is essentially the same.
1719 Thus, do not call block->raiseModified(MOD_STATE_WRITE_NEEDED).
1720 Otherwise there would be a huge amount of unnecessary I/O.
1725 Convert objects that are not standing inside active blocks to static.
1727 If m_known_by_count != 0, active object is not deleted, but static
1728 data is still updated.
1730 If force_delete is set, active object is deleted nevertheless. It
1731 shall only be set so in the destructor of the environment.
1733 If block wasn't generated (not in memory or on disk),
1735 void ServerEnvironment::deactivateFarObjects(bool force_delete)
1737 std::vector<u16> objects_to_remove;
1738 for(std::map<u16, ServerActiveObject*>::iterator
1739 i = m_active_objects.begin();
1740 i != m_active_objects.end(); ++i) {
1741 ServerActiveObject* obj = i->second;
1744 // Do not deactivate if static data creation not allowed
1745 if(!force_delete && !obj->isStaticAllowed())
1748 // If pending deactivation, let removeRemovedObjects() do it
1749 if(!force_delete && obj->m_pending_deactivation)
1753 v3f objectpos = obj->getBasePosition();
1755 // The block in which the object resides in
1756 v3s16 blockpos_o = getNodeBlockPos(floatToInt(objectpos, BS));
1758 // If object's static data is stored in a deactivated block and object
1759 // is actually located in an active block, re-save to the block in
1760 // which the object is actually located in.
1762 obj->m_static_exists &&
1763 !m_active_blocks.contains(obj->m_static_block) &&
1764 m_active_blocks.contains(blockpos_o))
1766 v3s16 old_static_block = obj->m_static_block;
1768 // Save to block where object is located
1769 MapBlock *block = m_map->emergeBlock(blockpos_o, false);
1771 errorstream<<"ServerEnvironment::deactivateFarObjects(): "
1772 <<"Could not save object id="<<id
1773 <<" to it's current block "<<PP(blockpos_o)
1777 std::string staticdata_new = obj->getStaticData();
1778 StaticObject s_obj(obj->getType(), objectpos, staticdata_new);
1779 block->m_static_objects.insert(id, s_obj);
1780 obj->m_static_block = blockpos_o;
1781 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1782 MOD_REASON_STATIC_DATA_ADDED);
1784 // Delete from block where object was located
1785 block = m_map->emergeBlock(old_static_block, false);
1787 errorstream<<"ServerEnvironment::deactivateFarObjects(): "
1788 <<"Could not delete object id="<<id
1789 <<" from it's previous block "<<PP(old_static_block)
1793 block->m_static_objects.remove(id);
1794 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1795 MOD_REASON_STATIC_DATA_REMOVED);
1799 // If block is active, don't remove
1800 if(!force_delete && m_active_blocks.contains(blockpos_o))
1803 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
1804 <<"deactivating object id="<<id<<" on inactive block "
1805 <<PP(blockpos_o)<<std::endl;
1807 // If known by some client, don't immediately delete.
1808 bool pending_delete = (obj->m_known_by_count > 0 && !force_delete);
1811 Update the static data
1814 if(obj->isStaticAllowed())
1816 // Create new static object
1817 std::string staticdata_new = obj->getStaticData();
1818 StaticObject s_obj(obj->getType(), objectpos, staticdata_new);
1820 bool stays_in_same_block = false;
1821 bool data_changed = true;
1823 if (obj->m_static_exists) {
1824 if (obj->m_static_block == blockpos_o)
1825 stays_in_same_block = true;
1827 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
1830 std::map<u16, StaticObject>::iterator n =
1831 block->m_static_objects.m_active.find(id);
1832 if (n != block->m_static_objects.m_active.end()) {
1833 StaticObject static_old = n->second;
1835 float save_movem = obj->getMinimumSavedMovement();
1837 if (static_old.data == staticdata_new &&
1838 (static_old.pos - objectpos).getLength() < save_movem)
1839 data_changed = false;
1841 errorstream<<"ServerEnvironment::deactivateFarObjects(): "
1842 <<"id="<<id<<" m_static_exists=true but "
1843 <<"static data doesn't actually exist in "
1844 <<PP(obj->m_static_block)<<std::endl;
1849 bool shall_be_written = (!stays_in_same_block || data_changed);
1851 // Delete old static object
1852 if(obj->m_static_exists)
1854 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
1857 block->m_static_objects.remove(id);
1858 obj->m_static_exists = false;
1859 // Only mark block as modified if data changed considerably
1860 if(shall_be_written)
1861 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1862 MOD_REASON_STATIC_DATA_CHANGED);
1866 // Add to the block where the object is located in
1867 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1868 // Get or generate the block
1869 MapBlock *block = NULL;
1871 block = m_map->emergeBlock(blockpos);
1872 } catch(InvalidPositionException &e){
1873 // Handled via NULL pointer
1874 // NOTE: emergeBlock's failure is usually determined by it
1875 // actually returning NULL
1880 if(block->m_static_objects.m_stored.size() >= g_settings->getU16("max_objects_per_block")){
1881 errorstream<<"ServerEnv: Trying to store id="<<obj->getId()
1882 <<" statically but block "<<PP(blockpos)
1883 <<" already contains "
1884 <<block->m_static_objects.m_stored.size()
1886 <<" Forcing delete."<<std::endl;
1887 force_delete = true;
1889 // If static counterpart already exists in target block,
1891 // This shouldn't happen because the object is removed from
1892 // the previous block before this according to
1893 // obj->m_static_block, but happens rarely for some unknown
1894 // reason. Unsuccessful attempts have been made to find
1896 if(id && block->m_static_objects.m_active.find(id) != block->m_static_objects.m_active.end()){
1897 infostream<<"ServerEnv: WARNING: Performing hack #83274"
1899 block->m_static_objects.remove(id);
1901 // Store static data
1902 u16 store_id = pending_delete ? id : 0;
1903 block->m_static_objects.insert(store_id, s_obj);
1905 // Only mark block as modified if data changed considerably
1906 if(shall_be_written)
1907 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1908 MOD_REASON_STATIC_DATA_CHANGED);
1910 obj->m_static_exists = true;
1911 obj->m_static_block = block->getPos();
1916 v3s16 p = floatToInt(objectpos, BS);
1917 errorstream<<"ServerEnv: Could not find or generate "
1918 <<"a block for storing id="<<obj->getId()
1919 <<" statically (pos="<<PP(p)<<")"<<std::endl;
1926 If known by some client, set pending deactivation.
1927 Otherwise delete it immediately.
1930 if(pending_delete && !force_delete)
1932 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
1933 <<"object id="<<id<<" is known by clients"
1934 <<"; not deleting yet"<<std::endl;
1936 obj->m_pending_deactivation = true;
1940 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
1941 <<"object id="<<id<<" is not known by clients"
1942 <<"; deleting"<<std::endl;
1944 // Tell the object about removal
1945 obj->removingFromEnvironment();
1946 // Deregister in scripting api
1947 m_script->removeObjectReference(obj);
1949 // Delete active object
1950 if(obj->environmentDeletes())
1952 // Id to be removed from m_active_objects
1953 objects_to_remove.push_back(id);
1956 // Remove references from m_active_objects
1957 for(std::vector<u16>::iterator i = objects_to_remove.begin();
1958 i != objects_to_remove.end(); ++i) {
1959 m_active_objects.erase(*i);
1966 #include "clientsimpleobject.h"
1972 ClientEnvironment::ClientEnvironment(ClientMap *map, scene::ISceneManager *smgr,
1973 ITextureSource *texturesource, IGameDef *gamedef,
1974 IrrlichtDevice *irr):
1977 m_texturesource(texturesource),
1982 memset(attachement_parent_ids, zero, sizeof(attachement_parent_ids));
1985 ClientEnvironment::~ClientEnvironment()
1987 // delete active objects
1988 for(std::map<u16, ClientActiveObject*>::iterator
1989 i = m_active_objects.begin();
1990 i != m_active_objects.end(); ++i)
1995 for(std::vector<ClientSimpleObject*>::iterator
1996 i = m_simple_objects.begin(); i != m_simple_objects.end(); ++i) {
2004 Map & ClientEnvironment::getMap()
2009 ClientMap & ClientEnvironment::getClientMap()
2014 void ClientEnvironment::addPlayer(Player *player)
2016 DSTACK(__FUNCTION_NAME);
2018 It is a failure if player is local and there already is a local
2021 FATAL_ERROR_IF(player->isLocal() == true && getLocalPlayer() != NULL,
2022 "Player is local but there is already a local player");
2024 Environment::addPlayer(player);
2027 LocalPlayer * ClientEnvironment::getLocalPlayer()
2029 for(std::vector<Player*>::iterator i = m_players.begin();
2030 i != m_players.end(); ++i) {
2031 Player *player = *i;
2032 if(player->isLocal())
2033 return (LocalPlayer*)player;
2038 void ClientEnvironment::step(float dtime)
2040 DSTACK(__FUNCTION_NAME);
2042 /* Step time of day */
2043 stepTimeOfDay(dtime);
2045 // Get some settings
2046 bool fly_allowed = m_gamedef->checkLocalPrivilege("fly");
2047 bool free_move = fly_allowed && g_settings->getBool("free_move");
2050 LocalPlayer *lplayer = getLocalPlayer();
2052 // collision info queue
2053 std::vector<CollisionInfo> player_collisions;
2056 Get the speed the player is going
2058 bool is_climbing = lplayer->is_climbing;
2060 f32 player_speed = lplayer->getSpeed().getLength();
2063 Maximum position increment
2065 //f32 position_max_increment = 0.05*BS;
2066 f32 position_max_increment = 0.1*BS;
2068 // Maximum time increment (for collision detection etc)
2069 // time = distance / speed
2070 f32 dtime_max_increment = 1;
2071 if(player_speed > 0.001)
2072 dtime_max_increment = position_max_increment / player_speed;
2074 // Maximum time increment is 10ms or lower
2075 if(dtime_max_increment > 0.01)
2076 dtime_max_increment = 0.01;
2078 // Don't allow overly huge dtime
2082 f32 dtime_downcount = dtime;
2085 Stuff that has a maximum time increment
2094 if(dtime_downcount > dtime_max_increment)
2096 dtime_part = dtime_max_increment;
2097 dtime_downcount -= dtime_part;
2101 dtime_part = dtime_downcount;
2103 Setting this to 0 (no -=dtime_part) disables an infinite loop
2104 when dtime_part is so small that dtime_downcount -= dtime_part
2107 dtime_downcount = 0;
2116 if(free_move == false && is_climbing == false)
2119 v3f speed = lplayer->getSpeed();
2120 if(lplayer->in_liquid == false)
2121 speed.Y -= lplayer->movement_gravity * lplayer->physics_override_gravity * dtime_part * 2;
2123 // Liquid floating / sinking
2124 if(lplayer->in_liquid && !lplayer->swimming_vertical)
2125 speed.Y -= lplayer->movement_liquid_sink * dtime_part * 2;
2127 // Liquid resistance
2128 if(lplayer->in_liquid_stable || lplayer->in_liquid)
2130 // How much the node's viscosity blocks movement, ranges between 0 and 1
2131 // Should match the scale at which viscosity increase affects other liquid attributes
2132 const f32 viscosity_factor = 0.3;
2134 v3f d_wanted = -speed / lplayer->movement_liquid_fluidity;
2135 f32 dl = d_wanted.getLength();
2136 if(dl > lplayer->movement_liquid_fluidity_smooth)
2137 dl = lplayer->movement_liquid_fluidity_smooth;
2138 dl *= (lplayer->liquid_viscosity * viscosity_factor) + (1 - viscosity_factor);
2140 v3f d = d_wanted.normalize() * dl;
2144 lplayer->setSpeed(speed);
2149 This also does collision detection.
2151 lplayer->move(dtime_part, this, position_max_increment,
2152 &player_collisions);
2155 while(dtime_downcount > 0.001);
2157 //std::cout<<"Looped "<<loopcount<<" times."<<std::endl;
2159 for(std::vector<CollisionInfo>::iterator i = player_collisions.begin();
2160 i != player_collisions.end(); ++i) {
2161 CollisionInfo &info = *i;
2162 v3f speed_diff = info.new_speed - info.old_speed;;
2163 // Handle only fall damage
2164 // (because otherwise walking against something in fast_move kills you)
2165 if(speed_diff.Y < 0 || info.old_speed.Y >= 0)
2167 // Get rid of other components
2170 f32 pre_factor = 1; // 1 hp per node/s
2171 f32 tolerance = BS*14; // 5 without damage
2172 f32 post_factor = 1; // 1 hp per node/s
2173 if(info.type == COLLISION_NODE)
2175 const ContentFeatures &f = m_gamedef->ndef()->
2176 get(m_map->getNodeNoEx(info.node_p));
2177 // Determine fall damage multiplier
2178 int addp = itemgroup_get(f.groups, "fall_damage_add_percent");
2179 pre_factor = 1.0 + (float)addp/100.0;
2181 float speed = pre_factor * speed_diff.getLength();
2182 if(speed > tolerance)
2184 f32 damage_f = (speed - tolerance)/BS * post_factor;
2185 u16 damage = (u16)(damage_f+0.5);
2187 damageLocalPlayer(damage, true);
2188 MtEvent *e = new SimpleTriggerEvent("PlayerFallingDamage");
2189 m_gamedef->event()->put(e);
2195 A quick draft of lava damage
2197 if(m_lava_hurt_interval.step(dtime, 1.0))
2199 v3f pf = lplayer->getPosition();
2201 // Feet, middle and head
2202 v3s16 p1 = floatToInt(pf + v3f(0, BS*0.1, 0), BS);
2203 MapNode n1 = m_map->getNodeNoEx(p1);
2204 v3s16 p2 = floatToInt(pf + v3f(0, BS*0.8, 0), BS);
2205 MapNode n2 = m_map->getNodeNoEx(p2);
2206 v3s16 p3 = floatToInt(pf + v3f(0, BS*1.6, 0), BS);
2207 MapNode n3 = m_map->getNodeNoEx(p3);
2209 u32 damage_per_second = 0;
2210 damage_per_second = MYMAX(damage_per_second,
2211 m_gamedef->ndef()->get(n1).damage_per_second);
2212 damage_per_second = MYMAX(damage_per_second,
2213 m_gamedef->ndef()->get(n2).damage_per_second);
2214 damage_per_second = MYMAX(damage_per_second,
2215 m_gamedef->ndef()->get(n3).damage_per_second);
2217 if(damage_per_second != 0)
2219 damageLocalPlayer(damage_per_second, true);
2226 if(m_drowning_interval.step(dtime, 2.0))
2228 v3f pf = lplayer->getPosition();
2231 v3s16 p = floatToInt(pf + v3f(0, BS*1.6, 0), BS);
2232 MapNode n = m_map->getNodeNoEx(p);
2233 ContentFeatures c = m_gamedef->ndef()->get(n);
2234 u8 drowning_damage = c.drowning;
2235 if(drowning_damage > 0 && lplayer->hp > 0){
2236 u16 breath = lplayer->getBreath();
2243 lplayer->setBreath(breath);
2244 updateLocalPlayerBreath(breath);
2247 if(lplayer->getBreath() == 0 && drowning_damage > 0){
2248 damageLocalPlayer(drowning_damage, true);
2251 if(m_breathing_interval.step(dtime, 0.5))
2253 v3f pf = lplayer->getPosition();
2256 v3s16 p = floatToInt(pf + v3f(0, BS*1.6, 0), BS);
2257 MapNode n = m_map->getNodeNoEx(p);
2258 ContentFeatures c = m_gamedef->ndef()->get(n);
2260 lplayer->setBreath(11);
2262 else if(c.drowning == 0){
2263 u16 breath = lplayer->getBreath();
2266 lplayer->setBreath(breath);
2267 updateLocalPlayerBreath(breath);
2273 Stuff that can be done in an arbitarily large dtime
2275 for(std::vector<Player*>::iterator i = m_players.begin();
2276 i != m_players.end(); ++i) {
2277 Player *player = *i;
2280 Handle non-local players
2282 if(player->isLocal() == false) {
2284 player->move(dtime, this, 100*BS);
2289 // Update lighting on local player (used for wield item)
2290 u32 day_night_ratio = getDayNightRatio();
2294 // On InvalidPositionException, use this as default
2295 // (day: LIGHT_SUN, night: 0)
2296 MapNode node_at_lplayer(CONTENT_AIR, 0x0f, 0);
2298 v3s16 p = lplayer->getLightPosition();
2299 node_at_lplayer = m_map->getNodeNoEx(p);
2301 u16 light = getInteriorLight(node_at_lplayer, 0, m_gamedef->ndef());
2302 u8 day = light & 0xff;
2303 u8 night = (light >> 8) & 0xff;
2304 finalColorBlend(lplayer->light_color, day, night, day_night_ratio);
2308 Step active objects and update lighting of them
2311 g_profiler->avg("CEnv: num of objects", m_active_objects.size());
2312 bool update_lighting = m_active_object_light_update_interval.step(dtime, 0.21);
2313 for(std::map<u16, ClientActiveObject*>::iterator
2314 i = m_active_objects.begin();
2315 i != m_active_objects.end(); ++i)
2317 ClientActiveObject* obj = i->second;
2319 obj->step(dtime, this);
2328 v3s16 p = obj->getLightPosition();
2329 MapNode n = m_map->getNodeNoEx(p, &pos_ok);
2331 light = n.getLightBlend(day_night_ratio, m_gamedef->ndef());
2333 light = blend_light(day_night_ratio, LIGHT_SUN, 0);
2335 obj->updateLight(light);
2340 Step and handle simple objects
2342 g_profiler->avg("CEnv: num of simple objects", m_simple_objects.size());
2343 for(std::vector<ClientSimpleObject*>::iterator
2344 i = m_simple_objects.begin(); i != m_simple_objects.end();) {
2345 std::vector<ClientSimpleObject*>::iterator cur = i;
2346 ClientSimpleObject *simple = *cur;
2348 simple->step(dtime);
2349 if(simple->m_to_be_removed) {
2351 i = m_simple_objects.erase(cur);
2359 void ClientEnvironment::addSimpleObject(ClientSimpleObject *simple)
2361 m_simple_objects.push_back(simple);
2364 GenericCAO* ClientEnvironment::getGenericCAO(u16 id)
2366 ClientActiveObject *obj = getActiveObject(id);
2367 if (obj && obj->getType() == ACTIVEOBJECT_TYPE_GENERIC)
2368 return (GenericCAO*) obj;
2373 ClientActiveObject* ClientEnvironment::getActiveObject(u16 id)
2375 std::map<u16, ClientActiveObject*>::iterator n;
2376 n = m_active_objects.find(id);
2377 if(n == m_active_objects.end())
2382 bool isFreeClientActiveObjectId(u16 id,
2383 std::map<u16, ClientActiveObject*> &objects)
2388 return objects.find(id) == objects.end();
2391 u16 getFreeClientActiveObjectId(
2392 std::map<u16, ClientActiveObject*> &objects)
2394 //try to reuse id's as late as possible
2395 static u16 last_used_id = 0;
2396 u16 startid = last_used_id;
2400 if(isFreeClientActiveObjectId(last_used_id, objects))
2401 return last_used_id;
2403 if(last_used_id == startid)
2408 u16 ClientEnvironment::addActiveObject(ClientActiveObject *object)
2410 assert(object); // Pre-condition
2411 if(object->getId() == 0)
2413 u16 new_id = getFreeClientActiveObjectId(m_active_objects);
2416 infostream<<"ClientEnvironment::addActiveObject(): "
2417 <<"no free ids available"<<std::endl;
2421 object->setId(new_id);
2423 if(isFreeClientActiveObjectId(object->getId(), m_active_objects) == false)
2425 infostream<<"ClientEnvironment::addActiveObject(): "
2426 <<"id is not free ("<<object->getId()<<")"<<std::endl;
2430 infostream<<"ClientEnvironment::addActiveObject(): "
2431 <<"added (id="<<object->getId()<<")"<<std::endl;
2432 m_active_objects[object->getId()] = object;
2433 object->addToScene(m_smgr, m_texturesource, m_irr);
2434 { // Update lighting immediately
2439 v3s16 p = object->getLightPosition();
2440 MapNode n = m_map->getNodeNoEx(p, &pos_ok);
2442 light = n.getLightBlend(getDayNightRatio(), m_gamedef->ndef());
2444 light = blend_light(getDayNightRatio(), LIGHT_SUN, 0);
2446 object->updateLight(light);
2448 return object->getId();
2451 void ClientEnvironment::addActiveObject(u16 id, u8 type,
2452 const std::string &init_data)
2454 ClientActiveObject* obj =
2455 ClientActiveObject::create((ActiveObjectType) type, m_gamedef, this);
2458 infostream<<"ClientEnvironment::addActiveObject(): "
2459 <<"id="<<id<<" type="<<type<<": Couldn't create object"
2468 obj->initialize(init_data);
2470 catch(SerializationError &e)
2472 errorstream<<"ClientEnvironment::addActiveObject():"
2473 <<" id="<<id<<" type="<<type
2474 <<": SerializationError in initialize(): "
2476 <<": init_data="<<serializeJsonString(init_data)
2480 addActiveObject(obj);
2483 void ClientEnvironment::removeActiveObject(u16 id)
2485 verbosestream<<"ClientEnvironment::removeActiveObject(): "
2486 <<"id="<<id<<std::endl;
2487 ClientActiveObject* obj = getActiveObject(id);
2490 infostream<<"ClientEnvironment::removeActiveObject(): "
2491 <<"id="<<id<<" not found"<<std::endl;
2494 obj->removeFromScene(true);
2496 m_active_objects.erase(id);
2499 void ClientEnvironment::processActiveObjectMessage(u16 id, const std::string &data)
2501 ClientActiveObject *obj = getActiveObject(id);
2503 infostream << "ClientEnvironment::processActiveObjectMessage():"
2504 << " got message for id=" << id << ", which doesn't exist."
2510 obj->processMessage(data);
2511 } catch (SerializationError &e) {
2512 errorstream<<"ClientEnvironment::processActiveObjectMessage():"
2513 << " id=" << id << " type=" << obj->getType()
2514 << " SerializationError in processMessage(): " << e.what()
2520 Callbacks for activeobjects
2523 void ClientEnvironment::damageLocalPlayer(u8 damage, bool handle_hp)
2525 LocalPlayer *lplayer = getLocalPlayer();
2529 if (lplayer->hp > damage)
2530 lplayer->hp -= damage;
2535 ClientEnvEvent event;
2536 event.type = CEE_PLAYER_DAMAGE;
2537 event.player_damage.amount = damage;
2538 event.player_damage.send_to_server = handle_hp;
2539 m_client_event_queue.push_back(event);
2542 void ClientEnvironment::updateLocalPlayerBreath(u16 breath)
2544 ClientEnvEvent event;
2545 event.type = CEE_PLAYER_BREATH;
2546 event.player_breath.amount = breath;
2547 m_client_event_queue.push_back(event);
2551 Client likes to call these
2554 void ClientEnvironment::getActiveObjects(v3f origin, f32 max_d,
2555 std::vector<DistanceSortedActiveObject> &dest)
2557 for(std::map<u16, ClientActiveObject*>::iterator
2558 i = m_active_objects.begin();
2559 i != m_active_objects.end(); ++i)
2561 ClientActiveObject* obj = i->second;
2563 f32 d = (obj->getPosition() - origin).getLength();
2568 DistanceSortedActiveObject dso(obj, d);
2570 dest.push_back(dso);
2574 ClientEnvEvent ClientEnvironment::getClientEvent()
2576 ClientEnvEvent event;
2577 if(m_client_event_queue.empty())
2578 event.type = CEE_NONE;
2580 event = m_client_event_queue.front();
2581 m_client_event_queue.pop_front();
2586 #endif // #ifndef SERVER