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::vector<Player*>::iterator i = m_players.begin();
65 i != m_players.end(); ++i) {
70 void Environment::addPlayer(Player *player)
72 DSTACK(__FUNCTION_NAME);
74 Check that peer_ids are unique.
75 Also check that names are unique.
76 Exception: there can be multiple players with peer_id=0
78 // If peer id is non-zero, it has to be unique.
79 if(player->peer_id != 0)
80 assert(getPlayer(player->peer_id) == NULL);
81 // Name has to be unique.
82 assert(getPlayer(player->getName()) == NULL);
84 m_players.push_back(player);
87 void Environment::removePlayer(u16 peer_id)
89 DSTACK(__FUNCTION_NAME);
91 for(std::vector<Player*>::iterator i = m_players.begin();
92 i != m_players.end();)
95 if(player->peer_id == peer_id) {
97 i = m_players.erase(i);
104 void Environment::removePlayer(const char *name)
106 for (std::vector<Player*>::iterator it = m_players.begin();
107 it != m_players.end(); ++it) {
108 if (strcmp((*it)->getName(), name) == 0) {
116 Player * Environment::getPlayer(u16 peer_id)
118 for(std::vector<Player*>::iterator i = m_players.begin();
119 i != m_players.end(); ++i) {
121 if(player->peer_id == peer_id)
127 Player * Environment::getPlayer(const char *name)
129 for(std::vector<Player*>::iterator i = m_players.begin();
130 i != m_players.end(); ++i) {
132 if(strcmp(player->getName(), name) == 0)
138 Player * Environment::getRandomConnectedPlayer()
140 std::vector<Player*> connected_players = getPlayers(true);
141 u32 chosen_one = myrand() % connected_players.size();
143 for(std::vector<Player*>::iterator
144 i = connected_players.begin();
145 i != connected_players.end(); ++i) {
146 if(j == chosen_one) {
155 Player * Environment::getNearestConnectedPlayer(v3f pos)
157 std::vector<Player*> connected_players = getPlayers(true);
159 Player *nearest_player = NULL;
160 for(std::vector<Player*>::iterator
161 i = connected_players.begin();
162 i != connected_players.end(); ++i) {
164 f32 d = player->getPosition().getDistanceFrom(pos);
165 if(d < nearest_d || nearest_player == NULL) {
167 nearest_player = player;
170 return nearest_player;
173 std::vector<Player*> Environment::getPlayers()
178 std::vector<Player*> Environment::getPlayers(bool ignore_disconnected)
180 std::vector<Player*> newlist;
181 for(std::vector<Player*>::iterator
182 i = m_players.begin();
183 i != m_players.end(); ++i) {
186 if(ignore_disconnected) {
187 // Ignore disconnected players
188 if(player->peer_id == 0)
192 newlist.push_back(player);
197 u32 Environment::getDayNightRatio()
199 if(m_enable_day_night_ratio_override)
200 return m_day_night_ratio_override;
201 return time_to_daynight_ratio(m_time_of_day_f*24000, m_cache_enable_shaders);
204 void Environment::setTimeOfDaySpeed(float speed)
206 JMutexAutoLock(this->m_timeofday_lock);
207 m_time_of_day_speed = speed;
210 float Environment::getTimeOfDaySpeed()
212 JMutexAutoLock(this->m_timeofday_lock);
213 float retval = m_time_of_day_speed;
217 void Environment::setTimeOfDay(u32 time)
219 JMutexAutoLock(this->m_time_lock);
220 m_time_of_day = time;
221 m_time_of_day_f = (float)time / 24000.0;
224 u32 Environment::getTimeOfDay()
226 JMutexAutoLock(this->m_time_lock);
227 u32 retval = m_time_of_day;
231 float Environment::getTimeOfDayF()
233 JMutexAutoLock(this->m_time_lock);
234 float retval = m_time_of_day_f;
238 void Environment::stepTimeOfDay(float dtime)
240 // getTimeOfDaySpeed lock the value we need to prevent MT problems
241 float day_speed = getTimeOfDaySpeed();
243 m_time_counter += dtime;
244 f32 speed = day_speed * 24000./(24.*3600);
245 u32 units = (u32)(m_time_counter*speed);
249 if(m_time_of_day + units >= 24000)
251 m_time_of_day = (m_time_of_day + units) % 24000;
253 m_time_of_day_f = (float)m_time_of_day / 24000.0;
256 m_time_counter -= (f32)units / speed;
259 m_time_of_day_f += day_speed/24/3600*dtime;
260 if(m_time_of_day_f > 1.0)
261 m_time_of_day_f -= 1.0;
262 if(m_time_of_day_f < 0.0)
263 m_time_of_day_f += 1.0;
271 ABMWithState::ABMWithState(ActiveBlockModifier *abm_):
275 // Initialize timer to random value to spread processing
276 float itv = abm->getTriggerInterval();
277 itv = MYMAX(0.001, itv); // No less than 1ms
278 int minval = MYMAX(-0.51*itv, -60); // Clamp to
279 int maxval = MYMIN(0.51*itv, 60); // +-60 seconds
280 timer = myrand_range(minval, maxval);
287 void fillRadiusBlock(v3s16 p0, s16 r, std::set<v3s16> &list)
290 for(p.X=p0.X-r; p.X<=p0.X+r; p.X++)
291 for(p.Y=p0.Y-r; p.Y<=p0.Y+r; p.Y++)
292 for(p.Z=p0.Z-r; p.Z<=p0.Z+r; p.Z++)
299 void ActiveBlockList::update(std::vector<v3s16> &active_positions,
301 std::set<v3s16> &blocks_removed,
302 std::set<v3s16> &blocks_added)
307 std::set<v3s16> newlist = m_forceloaded_list;
308 for(std::vector<v3s16>::iterator i = active_positions.begin();
309 i != active_positions.end(); ++i)
311 fillRadiusBlock(*i, radius, newlist);
315 Find out which blocks on the old list are not on the new list
317 // Go through old list
318 for(std::set<v3s16>::iterator i = m_list.begin();
319 i != m_list.end(); ++i)
322 // If not on new list, it's been removed
323 if(newlist.find(p) == newlist.end())
324 blocks_removed.insert(p);
328 Find out which blocks on the new list are not on the old list
330 // Go through new list
331 for(std::set<v3s16>::iterator i = newlist.begin();
332 i != newlist.end(); ++i)
335 // If not on old list, it's been added
336 if(m_list.find(p) == m_list.end())
337 blocks_added.insert(p);
344 for(std::set<v3s16>::iterator i = newlist.begin();
345 i != newlist.end(); ++i)
356 ServerEnvironment::ServerEnvironment(ServerMap *map,
357 GameScripting *scriptIface, IGameDef *gamedef,
358 const std::string &path_world) :
360 m_script(scriptIface),
362 m_path_world(path_world),
363 m_send_recommended_timer(0),
364 m_active_block_interval_overload_skip(0),
366 m_game_time_fraction_counter(0),
367 m_recommended_send_interval(0.1),
368 m_max_lag_estimate(0.1)
372 ServerEnvironment::~ServerEnvironment()
374 // Clear active block list.
375 // This makes the next one delete all active objects.
376 m_active_blocks.clear();
378 // Convert all objects to static and delete the active objects
379 deactivateFarObjects(true);
384 // Delete ActiveBlockModifiers
385 for(std::vector<ABMWithState>::iterator
386 i = m_abms.begin(); i != m_abms.end(); ++i){
391 Map & ServerEnvironment::getMap()
396 ServerMap & ServerEnvironment::getServerMap()
401 bool ServerEnvironment::line_of_sight(v3f pos1, v3f pos2, float stepsize, v3s16 *p)
403 float distance = pos1.getDistanceFrom(pos2);
405 //calculate normalized direction vector
406 v3f normalized_vector = v3f((pos2.X - pos1.X)/distance,
407 (pos2.Y - pos1.Y)/distance,
408 (pos2.Z - pos1.Z)/distance);
410 //find out if there's a node on path between pos1 and pos2
411 for (float i = 1; i < distance; i += stepsize) {
412 v3s16 pos = floatToInt(v3f(normalized_vector.X * i,
413 normalized_vector.Y * i,
414 normalized_vector.Z * i) +pos1,BS);
416 MapNode n = getMap().getNodeNoEx(pos);
418 if(n.param0 != CONTENT_AIR) {
428 void ServerEnvironment::saveLoadedPlayers()
430 std::string players_path = m_path_world + DIR_DELIM "players";
431 fs::CreateDir(players_path);
433 for (std::vector<Player*>::iterator it = m_players.begin();
434 it != m_players.end();
436 RemotePlayer *player = static_cast<RemotePlayer*>(*it);
437 if (player->checkModified()) {
438 player->save(players_path);
443 void ServerEnvironment::savePlayer(const std::string &playername)
445 std::string players_path = m_path_world + DIR_DELIM "players";
446 fs::CreateDir(players_path);
448 RemotePlayer *player = static_cast<RemotePlayer*>(getPlayer(playername.c_str()));
450 player->save(players_path);
454 Player *ServerEnvironment::loadPlayer(const std::string &playername)
456 std::string players_path = m_path_world + DIR_DELIM "players" DIR_DELIM;
458 RemotePlayer *player = static_cast<RemotePlayer*>(getPlayer(playername.c_str()));
459 bool newplayer = false;
462 player = new RemotePlayer(m_gamedef, playername.c_str());
466 RemotePlayer testplayer(m_gamedef, "");
467 std::string path = players_path + playername;
468 for (u32 i = 0; i < PLAYER_FILE_ALTERNATE_TRIES; i++) {
469 // Open file and deserialize
470 std::ifstream is(path.c_str(), std::ios_base::binary);
474 testplayer.deSerialize(is, path);
476 if (testplayer.getName() == playername) {
477 *player = testplayer;
481 path = players_path + playername + itos(i);
484 infostream << "Player file for player " << playername
485 << " not found" << std::endl;
491 player->setModified(false);
495 void ServerEnvironment::saveMeta()
497 std::string path = m_path_world + DIR_DELIM "env_meta.txt";
499 // Open file and serialize
500 std::ostringstream ss(std::ios_base::binary);
503 args.setU64("game_time", m_game_time);
504 args.setU64("time_of_day", getTimeOfDay());
508 if(!fs::safeWriteToFile(path, ss.str()))
510 infostream<<"ServerEnvironment::saveMeta(): Failed to write "
512 throw SerializationError("Couldn't save env meta");
516 void ServerEnvironment::loadMeta()
518 std::string path = m_path_world + DIR_DELIM "env_meta.txt";
520 // Open file and deserialize
521 std::ifstream is(path.c_str(), std::ios_base::binary);
523 infostream << "ServerEnvironment::loadMeta(): Failed to open "
524 << path << std::endl;
525 throw SerializationError("Couldn't load env meta");
530 if (!args.parseConfigLines(is, "EnvArgsEnd")) {
531 throw SerializationError("ServerEnvironment::loadMeta(): "
532 "EnvArgsEnd not found!");
536 m_game_time = args.getU64("game_time");
537 } catch (SettingNotFoundException &e) {
538 // Getting this is crucial, otherwise timestamps are useless
539 throw SerializationError("Couldn't load env meta game_time");
543 m_time_of_day = args.getU64("time_of_day");
544 } catch (SettingNotFoundException &e) {
545 // This is not as important
546 m_time_of_day = 9000;
552 ActiveBlockModifier *abm;
554 std::set<content_t> required_neighbors;
560 ServerEnvironment *m_env;
561 std::map<content_t, std::vector<ActiveABM> > m_aabms;
563 ABMHandler(std::vector<ABMWithState> &abms,
564 float dtime_s, ServerEnvironment *env,
570 INodeDefManager *ndef = env->getGameDef()->ndef();
571 for(std::vector<ABMWithState>::iterator
572 i = abms.begin(); i != abms.end(); ++i) {
573 ActiveBlockModifier *abm = i->abm;
574 float trigger_interval = abm->getTriggerInterval();
575 if(trigger_interval < 0.001)
576 trigger_interval = 0.001;
577 float actual_interval = dtime_s;
580 if(i->timer < trigger_interval)
582 i->timer -= trigger_interval;
583 actual_interval = trigger_interval;
585 float intervals = actual_interval / trigger_interval;
588 float chance = abm->getTriggerChance();
593 aabm.chance = chance / intervals;
597 std::set<std::string> required_neighbors_s
598 = abm->getRequiredNeighbors();
599 for(std::set<std::string>::iterator
600 i = required_neighbors_s.begin();
601 i != required_neighbors_s.end(); i++)
603 ndef->getIds(*i, aabm.required_neighbors);
606 std::set<std::string> contents_s = abm->getTriggerContents();
607 for(std::set<std::string>::iterator
608 i = contents_s.begin(); i != contents_s.end(); i++)
610 std::set<content_t> ids;
611 ndef->getIds(*i, ids);
612 for(std::set<content_t>::const_iterator k = ids.begin();
616 std::map<content_t, std::vector<ActiveABM> >::iterator j;
618 if(j == m_aabms.end()){
619 std::vector<ActiveABM> aabmlist;
620 m_aabms[c] = aabmlist;
623 j->second.push_back(aabm);
628 // Find out how many objects the given block and its neighbours contain.
629 // Returns the number of objects in the block, and also in 'wider' the
630 // number of objects in the block and all its neighbours. The latter
631 // may an estimate if any neighbours are unloaded.
632 u32 countObjects(MapBlock *block, ServerMap * map, u32 &wider)
635 u32 wider_unknown_count = 0;
636 for(s16 x=-1; x<=1; x++)
637 for(s16 y=-1; y<=1; y++)
638 for(s16 z=-1; z<=1; z++)
640 MapBlock *block2 = map->getBlockNoCreateNoEx(
641 block->getPos() + v3s16(x,y,z));
643 wider_unknown_count++;
646 wider += block2->m_static_objects.m_active.size()
647 + block2->m_static_objects.m_stored.size();
650 u32 active_object_count = block->m_static_objects.m_active.size();
651 u32 wider_known_count = 3*3*3 - wider_unknown_count;
652 wider += wider_unknown_count * wider / wider_known_count;
653 return active_object_count;
656 void apply(MapBlock *block)
661 ServerMap *map = &m_env->getServerMap();
663 u32 active_object_count_wider;
664 u32 active_object_count = this->countObjects(block, map, active_object_count_wider);
665 m_env->m_added_objects = 0;
668 for(p0.X=0; p0.X<MAP_BLOCKSIZE; p0.X++)
669 for(p0.Y=0; p0.Y<MAP_BLOCKSIZE; p0.Y++)
670 for(p0.Z=0; p0.Z<MAP_BLOCKSIZE; p0.Z++)
672 MapNode n = block->getNodeNoEx(p0);
673 content_t c = n.getContent();
674 v3s16 p = p0 + block->getPosRelative();
676 std::map<content_t, std::vector<ActiveABM> >::iterator j;
678 if(j == m_aabms.end())
681 for(std::vector<ActiveABM>::iterator
682 i = j->second.begin(); i != j->second.end(); i++) {
683 if(myrand() % i->chance != 0)
687 if(!i->required_neighbors.empty())
690 for(p1.X = p.X-1; p1.X <= p.X+1; p1.X++)
691 for(p1.Y = p.Y-1; p1.Y <= p.Y+1; p1.Y++)
692 for(p1.Z = p.Z-1; p1.Z <= p.Z+1; p1.Z++)
696 MapNode n = map->getNodeNoEx(p1);
697 content_t c = n.getContent();
698 std::set<content_t>::const_iterator k;
699 k = i->required_neighbors.find(c);
700 if(k != i->required_neighbors.end()){
704 // No required neighbor found
709 // Call all the trigger variations
710 i->abm->trigger(m_env, p, n);
711 i->abm->trigger(m_env, p, n,
712 active_object_count, active_object_count_wider);
714 // Count surrounding objects again if the abms added any
715 if(m_env->m_added_objects > 0) {
716 active_object_count = countObjects(block, map, active_object_count_wider);
717 m_env->m_added_objects = 0;
724 void ServerEnvironment::activateBlock(MapBlock *block, u32 additional_dtime)
726 // Reset usage timer immediately, otherwise a block that becomes active
727 // again at around the same time as it would normally be unloaded will
728 // get unloaded incorrectly. (I think this still leaves a small possibility
729 // of a race condition between this and server::AsyncRunStep, which only
730 // some kind of synchronisation will fix, but it at least reduces the window
731 // of opportunity for it to break from seconds to nanoseconds)
732 block->resetUsageTimer();
734 // Get time difference
736 u32 stamp = block->getTimestamp();
737 if(m_game_time > stamp && stamp != BLOCK_TIMESTAMP_UNDEFINED)
738 dtime_s = m_game_time - block->getTimestamp();
739 dtime_s += additional_dtime;
741 /*infostream<<"ServerEnvironment::activateBlock(): block timestamp: "
742 <<stamp<<", game time: "<<m_game_time<<std::endl;*/
744 // Set current time as timestamp
745 block->setTimestampNoChangedFlag(m_game_time);
747 /*infostream<<"ServerEnvironment::activateBlock(): block is "
748 <<dtime_s<<" seconds old."<<std::endl;*/
750 // Activate stored objects
751 activateObjects(block, dtime_s);
754 std::map<v3s16, NodeTimer> elapsed_timers =
755 block->m_node_timers.step((float)dtime_s);
756 if(!elapsed_timers.empty()){
758 for(std::map<v3s16, NodeTimer>::iterator
759 i = elapsed_timers.begin();
760 i != elapsed_timers.end(); i++){
761 n = block->getNodeNoEx(i->first);
762 v3s16 p = i->first + block->getPosRelative();
763 if(m_script->node_on_timer(p,n,i->second.elapsed))
764 block->setNodeTimer(i->first,NodeTimer(i->second.timeout,0));
768 /* Handle ActiveBlockModifiers */
769 ABMHandler abmhandler(m_abms, dtime_s, this, false);
770 abmhandler.apply(block);
773 void ServerEnvironment::addActiveBlockModifier(ActiveBlockModifier *abm)
775 m_abms.push_back(ABMWithState(abm));
778 bool ServerEnvironment::setNode(v3s16 p, const MapNode &n)
780 INodeDefManager *ndef = m_gamedef->ndef();
781 MapNode n_old = m_map->getNodeNoEx(p);
784 if (ndef->get(n_old).has_on_destruct)
785 m_script->node_on_destruct(p, n_old);
788 if (!m_map->addNodeWithEvent(p, n))
791 // Update active VoxelManipulator if a mapgen thread
792 m_map->updateVManip(p);
794 // Call post-destructor
795 if (ndef->get(n_old).has_after_destruct)
796 m_script->node_after_destruct(p, n_old);
799 if (ndef->get(n).has_on_construct)
800 m_script->node_on_construct(p, n);
805 bool ServerEnvironment::removeNode(v3s16 p)
807 INodeDefManager *ndef = m_gamedef->ndef();
808 MapNode n_old = m_map->getNodeNoEx(p);
811 if (ndef->get(n_old).has_on_destruct)
812 m_script->node_on_destruct(p, n_old);
815 // This is slightly optimized compared to addNodeWithEvent(air)
816 if (!m_map->removeNodeWithEvent(p))
819 // Update active VoxelManipulator if a mapgen thread
820 m_map->updateVManip(p);
822 // Call post-destructor
823 if (ndef->get(n_old).has_after_destruct)
824 m_script->node_after_destruct(p, n_old);
826 // Air doesn't require constructor
830 bool ServerEnvironment::swapNode(v3s16 p, const MapNode &n)
832 if (!m_map->addNodeWithEvent(p, n, false))
835 // Update active VoxelManipulator if a mapgen thread
836 m_map->updateVManip(p);
841 std::set<u16> ServerEnvironment::getObjectsInsideRadius(v3f pos, float radius)
843 std::set<u16> objects;
844 for(std::map<u16, ServerActiveObject*>::iterator
845 i = m_active_objects.begin();
846 i != m_active_objects.end(); ++i)
848 ServerActiveObject* obj = i->second;
850 v3f objectpos = obj->getBasePosition();
851 if(objectpos.getDistanceFrom(pos) > radius)
858 void ServerEnvironment::clearAllObjects()
860 infostream<<"ServerEnvironment::clearAllObjects(): "
861 <<"Removing all active objects"<<std::endl;
862 std::vector<u16> objects_to_remove;
863 for(std::map<u16, ServerActiveObject*>::iterator
864 i = m_active_objects.begin();
865 i != m_active_objects.end(); ++i) {
866 ServerActiveObject* obj = i->second;
867 if(obj->getType() == ACTIVEOBJECT_TYPE_PLAYER)
870 // Delete static object if block is loaded
871 if(obj->m_static_exists){
872 MapBlock *block = m_map->getBlockNoCreateNoEx(obj->m_static_block);
874 block->m_static_objects.remove(id);
875 block->raiseModified(MOD_STATE_WRITE_NEEDED,
877 obj->m_static_exists = false;
880 // If known by some client, don't delete immediately
881 if(obj->m_known_by_count > 0){
882 obj->m_pending_deactivation = true;
883 obj->m_removed = true;
887 // Tell the object about removal
888 obj->removingFromEnvironment();
889 // Deregister in scripting api
890 m_script->removeObjectReference(obj);
892 // Delete active object
893 if(obj->environmentDeletes())
895 // Id to be removed from m_active_objects
896 objects_to_remove.push_back(id);
899 // Remove references from m_active_objects
900 for(std::vector<u16>::iterator i = objects_to_remove.begin();
901 i != objects_to_remove.end(); ++i) {
902 m_active_objects.erase(*i);
905 // Get list of loaded blocks
906 std::vector<v3s16> loaded_blocks;
907 infostream<<"ServerEnvironment::clearAllObjects(): "
908 <<"Listing all loaded blocks"<<std::endl;
909 m_map->listAllLoadedBlocks(loaded_blocks);
910 infostream<<"ServerEnvironment::clearAllObjects(): "
911 <<"Done listing all loaded blocks: "
912 <<loaded_blocks.size()<<std::endl;
914 // Get list of loadable blocks
915 std::vector<v3s16> loadable_blocks;
916 infostream<<"ServerEnvironment::clearAllObjects(): "
917 <<"Listing all loadable blocks"<<std::endl;
918 m_map->listAllLoadableBlocks(loadable_blocks);
919 infostream<<"ServerEnvironment::clearAllObjects(): "
920 <<"Done listing all loadable blocks: "
921 <<loadable_blocks.size()
922 <<", now clearing"<<std::endl;
924 // Grab a reference on each loaded block to avoid unloading it
925 for(std::vector<v3s16>::iterator i = loaded_blocks.begin();
926 i != loaded_blocks.end(); ++i) {
928 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
933 // Remove objects in all loadable blocks
934 u32 unload_interval = g_settings->getS32("max_clearobjects_extra_loaded_blocks");
935 unload_interval = MYMAX(unload_interval, 1);
936 u32 report_interval = loadable_blocks.size() / 10;
937 u32 num_blocks_checked = 0;
938 u32 num_blocks_cleared = 0;
939 u32 num_objs_cleared = 0;
940 for(std::vector<v3s16>::iterator i = loadable_blocks.begin();
941 i != loadable_blocks.end(); ++i) {
943 MapBlock *block = m_map->emergeBlock(p, false);
945 errorstream<<"ServerEnvironment::clearAllObjects(): "
946 <<"Failed to emerge block "<<PP(p)<<std::endl;
949 u32 num_stored = block->m_static_objects.m_stored.size();
950 u32 num_active = block->m_static_objects.m_active.size();
951 if(num_stored != 0 || num_active != 0){
952 block->m_static_objects.m_stored.clear();
953 block->m_static_objects.m_active.clear();
954 block->raiseModified(MOD_STATE_WRITE_NEEDED,
956 num_objs_cleared += num_stored + num_active;
957 num_blocks_cleared++;
959 num_blocks_checked++;
961 if(report_interval != 0 &&
962 num_blocks_checked % report_interval == 0){
963 float percent = 100.0 * (float)num_blocks_checked /
964 loadable_blocks.size();
965 infostream<<"ServerEnvironment::clearAllObjects(): "
966 <<"Cleared "<<num_objs_cleared<<" objects"
967 <<" in "<<num_blocks_cleared<<" blocks ("
968 <<percent<<"%)"<<std::endl;
970 if(num_blocks_checked % unload_interval == 0){
971 m_map->unloadUnreferencedBlocks();
974 m_map->unloadUnreferencedBlocks();
976 // Drop references that were added above
977 for(std::vector<v3s16>::iterator i = loaded_blocks.begin();
978 i != loaded_blocks.end(); ++i) {
980 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
985 infostream<<"ServerEnvironment::clearAllObjects(): "
986 <<"Finished: Cleared "<<num_objs_cleared<<" objects"
987 <<" in "<<num_blocks_cleared<<" blocks"<<std::endl;
990 void ServerEnvironment::step(float dtime)
992 DSTACK(__FUNCTION_NAME);
994 //TimeTaker timer("ServerEnv step");
996 /* Step time of day */
997 stepTimeOfDay(dtime);
1000 // NOTE: This is kind of funny on a singleplayer game, but doesn't
1001 // really matter that much.
1002 m_recommended_send_interval = g_settings->getFloat("dedicated_server_step");
1008 m_game_time_fraction_counter += dtime;
1009 u32 inc_i = (u32)m_game_time_fraction_counter;
1010 m_game_time += inc_i;
1011 m_game_time_fraction_counter -= (float)inc_i;
1018 ScopeProfiler sp(g_profiler, "SEnv: handle players avg", SPT_AVG);
1019 for(std::vector<Player*>::iterator i = m_players.begin();
1020 i != m_players.end(); ++i)
1022 Player *player = *i;
1024 // Ignore disconnected players
1025 if(player->peer_id == 0)
1029 player->move(dtime, this, 100*BS);
1034 Manage active block list
1036 if(m_active_blocks_management_interval.step(dtime, 2.0))
1038 ScopeProfiler sp(g_profiler, "SEnv: manage act. block list avg /2s", SPT_AVG);
1040 Get player block positions
1042 std::vector<v3s16> players_blockpos;
1043 for(std::vector<Player*>::iterator
1044 i = m_players.begin();
1045 i != m_players.end(); ++i) {
1046 Player *player = *i;
1047 // Ignore disconnected players
1048 if(player->peer_id == 0)
1051 v3s16 blockpos = getNodeBlockPos(
1052 floatToInt(player->getPosition(), BS));
1053 players_blockpos.push_back(blockpos);
1057 Update list of active blocks, collecting changes
1059 const s16 active_block_range = g_settings->getS16("active_block_range");
1060 std::set<v3s16> blocks_removed;
1061 std::set<v3s16> blocks_added;
1062 m_active_blocks.update(players_blockpos, active_block_range,
1063 blocks_removed, blocks_added);
1066 Handle removed blocks
1069 // Convert active objects that are no more in active blocks to static
1070 deactivateFarObjects(false);
1072 for(std::set<v3s16>::iterator
1073 i = blocks_removed.begin();
1074 i != blocks_removed.end(); ++i)
1078 /* infostream<<"Server: Block " << PP(p)
1079 << " became inactive"<<std::endl; */
1081 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1085 // Set current time as timestamp (and let it set ChangedFlag)
1086 block->setTimestamp(m_game_time);
1093 for(std::set<v3s16>::iterator
1094 i = blocks_added.begin();
1095 i != blocks_added.end(); ++i)
1099 MapBlock *block = m_map->getBlockOrEmerge(p);
1101 m_active_blocks.m_list.erase(p);
1105 activateBlock(block);
1106 /* infostream<<"Server: Block " << PP(p)
1107 << " became active"<<std::endl; */
1112 Mess around in active blocks
1114 if(m_active_blocks_nodemetadata_interval.step(dtime, 1.0))
1116 ScopeProfiler sp(g_profiler, "SEnv: mess in act. blocks avg /1s", SPT_AVG);
1120 for(std::set<v3s16>::iterator
1121 i = m_active_blocks.m_list.begin();
1122 i != m_active_blocks.m_list.end(); ++i)
1126 /*infostream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
1127 <<") being handled"<<std::endl;*/
1129 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1133 // Reset block usage timer
1134 block->resetUsageTimer();
1136 // Set current time as timestamp
1137 block->setTimestampNoChangedFlag(m_game_time);
1138 // If time has changed much from the one on disk,
1139 // set block to be saved when it is unloaded
1140 if(block->getTimestamp() > block->getDiskTimestamp() + 60)
1141 block->raiseModified(MOD_STATE_WRITE_AT_UNLOAD,
1142 "Timestamp older than 60s (step)");
1145 std::map<v3s16, NodeTimer> elapsed_timers =
1146 block->m_node_timers.step((float)dtime);
1147 if(!elapsed_timers.empty()){
1149 for(std::map<v3s16, NodeTimer>::iterator
1150 i = elapsed_timers.begin();
1151 i != elapsed_timers.end(); i++){
1152 n = block->getNodeNoEx(i->first);
1153 p = i->first + block->getPosRelative();
1154 if(m_script->node_on_timer(p,n,i->second.elapsed))
1155 block->setNodeTimer(i->first,NodeTimer(i->second.timeout,0));
1161 const float abm_interval = 1.0;
1162 if(m_active_block_modifier_interval.step(dtime, abm_interval))
1164 if(m_active_block_interval_overload_skip > 0){
1165 ScopeProfiler sp(g_profiler, "SEnv: ABM overload skips");
1166 m_active_block_interval_overload_skip--;
1169 ScopeProfiler sp(g_profiler, "SEnv: modify in blocks avg /1s", SPT_AVG);
1170 TimeTaker timer("modify in active blocks");
1172 // Initialize handling of ActiveBlockModifiers
1173 ABMHandler abmhandler(m_abms, abm_interval, this, true);
1175 for(std::set<v3s16>::iterator
1176 i = m_active_blocks.m_list.begin();
1177 i != m_active_blocks.m_list.end(); ++i)
1181 /*infostream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
1182 <<") being handled"<<std::endl;*/
1184 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1188 // Set current time as timestamp
1189 block->setTimestampNoChangedFlag(m_game_time);
1191 /* Handle ActiveBlockModifiers */
1192 abmhandler.apply(block);
1195 u32 time_ms = timer.stop(true);
1196 u32 max_time_ms = 200;
1197 if(time_ms > max_time_ms){
1198 infostream<<"WARNING: active block modifiers took "
1199 <<time_ms<<"ms (longer than "
1200 <<max_time_ms<<"ms)"<<std::endl;
1201 m_active_block_interval_overload_skip = (time_ms / max_time_ms) + 1;
1206 Step script environment (run global on_step())
1208 m_script->environment_Step(dtime);
1214 ScopeProfiler sp(g_profiler, "SEnv: step act. objs avg", SPT_AVG);
1215 //TimeTaker timer("Step active objects");
1217 g_profiler->avg("SEnv: num of objects", m_active_objects.size());
1219 // This helps the objects to send data at the same time
1220 bool send_recommended = false;
1221 m_send_recommended_timer += dtime;
1222 if(m_send_recommended_timer > getSendRecommendedInterval())
1224 m_send_recommended_timer -= getSendRecommendedInterval();
1225 send_recommended = true;
1228 for(std::map<u16, ServerActiveObject*>::iterator
1229 i = m_active_objects.begin();
1230 i != m_active_objects.end(); ++i)
1232 ServerActiveObject* obj = i->second;
1233 // Don't step if is to be removed or stored statically
1234 if(obj->m_removed || obj->m_pending_deactivation)
1237 obj->step(dtime, send_recommended);
1238 // Read messages from object
1239 while(!obj->m_messages_out.empty())
1241 m_active_object_messages.push_back(
1242 obj->m_messages_out.front());
1243 obj->m_messages_out.pop();
1249 Manage active objects
1251 if(m_object_management_interval.step(dtime, 0.5))
1253 ScopeProfiler sp(g_profiler, "SEnv: remove removed objs avg /.5s", SPT_AVG);
1255 Remove objects that satisfy (m_removed && m_known_by_count==0)
1257 removeRemovedObjects();
1261 ServerActiveObject* ServerEnvironment::getActiveObject(u16 id)
1263 std::map<u16, ServerActiveObject*>::iterator n;
1264 n = m_active_objects.find(id);
1265 if(n == m_active_objects.end())
1270 bool isFreeServerActiveObjectId(u16 id,
1271 std::map<u16, ServerActiveObject*> &objects)
1276 return objects.find(id) == objects.end();
1279 u16 getFreeServerActiveObjectId(
1280 std::map<u16, ServerActiveObject*> &objects)
1282 //try to reuse id's as late as possible
1283 static u16 last_used_id = 0;
1284 u16 startid = last_used_id;
1288 if(isFreeServerActiveObjectId(last_used_id, objects))
1289 return last_used_id;
1291 if(last_used_id == startid)
1296 u16 ServerEnvironment::addActiveObject(ServerActiveObject *object)
1300 u16 id = addActiveObjectRaw(object, true, 0);
1305 bool ServerEnvironment::addActiveObjectAsStatic(ServerActiveObject *obj)
1309 v3f objectpos = obj->getBasePosition();
1311 // The block in which the object resides in
1312 v3s16 blockpos_o = getNodeBlockPos(floatToInt(objectpos, BS));
1315 Update the static data
1318 // Create new static object
1319 std::string staticdata = obj->getStaticData();
1320 StaticObject s_obj(obj->getType(), objectpos, staticdata);
1321 // Add to the block where the object is located in
1322 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1323 // Get or generate the block
1324 MapBlock *block = m_map->emergeBlock(blockpos);
1326 bool succeeded = false;
1330 block->m_static_objects.insert(0, s_obj);
1331 block->raiseModified(MOD_STATE_WRITE_AT_UNLOAD,
1332 "addActiveObjectAsStatic");
1336 infostream<<"ServerEnvironment::addActiveObjectAsStatic: "
1337 <<"Could not find or generate "
1338 <<"a block for storing static object"<<std::endl;
1342 if(obj->environmentDeletes())
1350 Finds out what new objects have been added to
1351 inside a radius around a position
1353 void ServerEnvironment::getAddedActiveObjects(v3s16 pos, s16 radius,
1355 std::set<u16> ¤t_objects,
1356 std::set<u16> &added_objects)
1358 v3f pos_f = intToFloat(pos, BS);
1359 f32 radius_f = radius * BS;
1360 f32 player_radius_f = player_radius * BS;
1362 if (player_radius_f < 0)
1363 player_radius_f = 0;
1366 Go through the object list,
1367 - discard m_removed objects,
1368 - discard objects that are too far away,
1369 - discard objects that are found in current_objects.
1370 - add remaining objects to added_objects
1372 for(std::map<u16, ServerActiveObject*>::iterator
1373 i = m_active_objects.begin();
1374 i != m_active_objects.end(); ++i)
1378 ServerActiveObject *object = i->second;
1381 // Discard if removed or deactivating
1382 if(object->m_removed || object->m_pending_deactivation)
1385 f32 distance_f = object->getBasePosition().getDistanceFrom(pos_f);
1386 if (object->getType() == ACTIVEOBJECT_TYPE_PLAYER) {
1387 // Discard if too far
1388 if (distance_f > player_radius_f && player_radius_f != 0)
1390 } else if (distance_f > radius_f)
1393 // Discard if already on current_objects
1394 std::set<u16>::iterator n;
1395 n = current_objects.find(id);
1396 if(n != current_objects.end())
1398 // Add to added_objects
1399 added_objects.insert(id);
1404 Finds out what objects have been removed from
1405 inside a radius around a position
1407 void ServerEnvironment::getRemovedActiveObjects(v3s16 pos, s16 radius,
1409 std::set<u16> ¤t_objects,
1410 std::set<u16> &removed_objects)
1412 v3f pos_f = intToFloat(pos, BS);
1413 f32 radius_f = radius * BS;
1414 f32 player_radius_f = player_radius * BS;
1416 if (player_radius_f < 0)
1417 player_radius_f = 0;
1420 Go through current_objects; object is removed if:
1421 - object is not found in m_active_objects (this is actually an
1422 error condition; objects should be set m_removed=true and removed
1423 only after all clients have been informed about removal), or
1424 - object has m_removed=true, or
1425 - object is too far away
1427 for(std::set<u16>::iterator
1428 i = current_objects.begin();
1429 i != current_objects.end(); ++i)
1432 ServerActiveObject *object = getActiveObject(id);
1435 infostream<<"ServerEnvironment::getRemovedActiveObjects():"
1436 <<" object in current_objects is NULL"<<std::endl;
1437 removed_objects.insert(id);
1441 if(object->m_removed || object->m_pending_deactivation)
1443 removed_objects.insert(id);
1447 f32 distance_f = object->getBasePosition().getDistanceFrom(pos_f);
1448 if (object->getType() == ACTIVEOBJECT_TYPE_PLAYER) {
1449 if (distance_f <= player_radius_f || player_radius_f == 0)
1451 } else if (distance_f <= radius_f)
1454 // Object is no longer visible
1455 removed_objects.insert(id);
1459 ActiveObjectMessage ServerEnvironment::getActiveObjectMessage()
1461 if(m_active_object_messages.empty())
1462 return ActiveObjectMessage(0);
1464 ActiveObjectMessage message = m_active_object_messages.front();
1465 m_active_object_messages.pop_front();
1470 ************ Private methods *************
1473 u16 ServerEnvironment::addActiveObjectRaw(ServerActiveObject *object,
1474 bool set_changed, u32 dtime_s)
1477 if(object->getId() == 0){
1478 u16 new_id = getFreeServerActiveObjectId(m_active_objects);
1481 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1482 <<"no free ids available"<<std::endl;
1483 if(object->environmentDeletes())
1487 object->setId(new_id);
1490 verbosestream<<"ServerEnvironment::addActiveObjectRaw(): "
1491 <<"supplied with id "<<object->getId()<<std::endl;
1493 if(isFreeServerActiveObjectId(object->getId(), m_active_objects) == false)
1495 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1496 <<"id is not free ("<<object->getId()<<")"<<std::endl;
1497 if(object->environmentDeletes())
1501 /*infostream<<"ServerEnvironment::addActiveObjectRaw(): "
1502 <<"added (id="<<object->getId()<<")"<<std::endl;*/
1504 m_active_objects[object->getId()] = object;
1506 verbosestream<<"ServerEnvironment::addActiveObjectRaw(): "
1507 <<"Added id="<<object->getId()<<"; there are now "
1508 <<m_active_objects.size()<<" active objects."
1511 // Register reference in scripting api (must be done before post-init)
1512 m_script->addObjectReference(object);
1513 // Post-initialize object
1514 object->addedToEnvironment(dtime_s);
1516 // Add static data to block
1517 if(object->isStaticAllowed())
1519 // Add static object to active static list of the block
1520 v3f objectpos = object->getBasePosition();
1521 std::string staticdata = object->getStaticData();
1522 StaticObject s_obj(object->getType(), objectpos, staticdata);
1523 // Add to the block where the object is located in
1524 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1525 MapBlock *block = m_map->emergeBlock(blockpos);
1527 block->m_static_objects.m_active[object->getId()] = s_obj;
1528 object->m_static_exists = true;
1529 object->m_static_block = blockpos;
1532 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1533 "addActiveObjectRaw");
1535 v3s16 p = floatToInt(objectpos, BS);
1536 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1537 <<"could not emerge block for storing id="<<object->getId()
1538 <<" statically (pos="<<PP(p)<<")"<<std::endl;
1542 return object->getId();
1546 Remove objects that satisfy (m_removed && m_known_by_count==0)
1548 void ServerEnvironment::removeRemovedObjects()
1550 std::vector<u16> objects_to_remove;
1551 for(std::map<u16, ServerActiveObject*>::iterator
1552 i = m_active_objects.begin();
1553 i != m_active_objects.end(); ++i) {
1555 ServerActiveObject* obj = i->second;
1556 // This shouldn't happen but check it
1559 infostream<<"NULL object found in ServerEnvironment"
1560 <<" while finding removed objects. id="<<id<<std::endl;
1561 // Id to be removed from m_active_objects
1562 objects_to_remove.push_back(id);
1567 We will delete objects that are marked as removed or thatare
1568 waiting for deletion after deactivation
1570 if(obj->m_removed == false && obj->m_pending_deactivation == false)
1574 Delete static data from block if is marked as removed
1576 if(obj->m_static_exists && obj->m_removed)
1578 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
1580 block->m_static_objects.remove(id);
1581 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1582 "removeRemovedObjects/remove");
1583 obj->m_static_exists = false;
1585 infostream<<"Failed to emerge block from which an object to "
1586 <<"be removed was loaded from. id="<<id<<std::endl;
1590 // If m_known_by_count > 0, don't actually remove. On some future
1591 // invocation this will be 0, which is when removal will continue.
1592 if(obj->m_known_by_count > 0)
1596 Move static data from active to stored if not marked as removed
1598 if(obj->m_static_exists && !obj->m_removed){
1599 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
1601 std::map<u16, StaticObject>::iterator i =
1602 block->m_static_objects.m_active.find(id);
1603 if(i != block->m_static_objects.m_active.end()){
1604 block->m_static_objects.m_stored.push_back(i->second);
1605 block->m_static_objects.m_active.erase(id);
1606 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1607 "removeRemovedObjects/deactivate");
1610 infostream<<"Failed to emerge block from which an object to "
1611 <<"be deactivated was loaded from. id="<<id<<std::endl;
1615 // Tell the object about removal
1616 obj->removingFromEnvironment();
1617 // Deregister in scripting api
1618 m_script->removeObjectReference(obj);
1621 if(obj->environmentDeletes())
1624 // Id to be removed from m_active_objects
1625 objects_to_remove.push_back(id);
1627 // Remove references from m_active_objects
1628 for(std::vector<u16>::iterator i = objects_to_remove.begin();
1629 i != objects_to_remove.end(); ++i) {
1630 m_active_objects.erase(*i);
1634 static void print_hexdump(std::ostream &o, const std::string &data)
1636 const int linelength = 16;
1637 for(int l=0; ; l++){
1638 int i0 = linelength * l;
1639 bool at_end = false;
1640 int thislinelength = linelength;
1641 if(i0 + thislinelength > (int)data.size()){
1642 thislinelength = data.size() - i0;
1645 for(int di=0; di<linelength; di++){
1648 if(di<thislinelength)
1649 snprintf(buf, 4, "%.2x ", data[i]);
1651 snprintf(buf, 4, " ");
1655 for(int di=0; di<thislinelength; di++){
1669 Convert stored objects from blocks near the players to active.
1671 void ServerEnvironment::activateObjects(MapBlock *block, u32 dtime_s)
1676 // Ignore if no stored objects (to not set changed flag)
1677 if(block->m_static_objects.m_stored.empty())
1680 verbosestream<<"ServerEnvironment::activateObjects(): "
1681 <<"activating objects of block "<<PP(block->getPos())
1682 <<" ("<<block->m_static_objects.m_stored.size()
1683 <<" objects)"<<std::endl;
1684 bool large_amount = (block->m_static_objects.m_stored.size() > g_settings->getU16("max_objects_per_block"));
1686 errorstream<<"suspiciously large amount of objects detected: "
1687 <<block->m_static_objects.m_stored.size()<<" in "
1688 <<PP(block->getPos())
1689 <<"; removing all of them."<<std::endl;
1690 // Clear stored list
1691 block->m_static_objects.m_stored.clear();
1692 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1693 "stored list cleared in activateObjects due to "
1694 "large amount of objects");
1698 // Activate stored objects
1699 std::vector<StaticObject> new_stored;
1700 for (std::vector<StaticObject>::iterator
1701 i = block->m_static_objects.m_stored.begin();
1702 i != block->m_static_objects.m_stored.end(); ++i) {
1703 StaticObject &s_obj = *i;
1705 // Create an active object from the data
1706 ServerActiveObject *obj = ServerActiveObject::create
1707 ((ActiveObjectType) s_obj.type, this, 0, s_obj.pos, s_obj.data);
1708 // If couldn't create object, store static data back.
1710 errorstream<<"ServerEnvironment::activateObjects(): "
1711 <<"failed to create active object from static object "
1712 <<"in block "<<PP(s_obj.pos/BS)
1713 <<" type="<<(int)s_obj.type<<" data:"<<std::endl;
1714 print_hexdump(verbosestream, s_obj.data);
1716 new_stored.push_back(s_obj);
1719 verbosestream<<"ServerEnvironment::activateObjects(): "
1720 <<"activated static object pos="<<PP(s_obj.pos/BS)
1721 <<" type="<<(int)s_obj.type<<std::endl;
1722 // This will also add the object to the active static list
1723 addActiveObjectRaw(obj, false, dtime_s);
1725 // Clear stored list
1726 block->m_static_objects.m_stored.clear();
1727 // Add leftover failed stuff to stored list
1728 for(std::vector<StaticObject>::iterator
1729 i = new_stored.begin();
1730 i != new_stored.end(); ++i) {
1731 StaticObject &s_obj = *i;
1732 block->m_static_objects.m_stored.push_back(s_obj);
1735 // Turn the active counterparts of activated objects not pending for
1737 for(std::map<u16, StaticObject>::iterator
1738 i = block->m_static_objects.m_active.begin();
1739 i != block->m_static_objects.m_active.end(); ++i)
1742 ServerActiveObject *object = getActiveObject(id);
1744 object->m_pending_deactivation = false;
1748 Note: Block hasn't really been modified here.
1749 The objects have just been activated and moved from the stored
1750 static list to the active static list.
1751 As such, the block is essentially the same.
1752 Thus, do not call block->raiseModified(MOD_STATE_WRITE_NEEDED).
1753 Otherwise there would be a huge amount of unnecessary I/O.
1758 Convert objects that are not standing inside active blocks to static.
1760 If m_known_by_count != 0, active object is not deleted, but static
1761 data is still updated.
1763 If force_delete is set, active object is deleted nevertheless. It
1764 shall only be set so in the destructor of the environment.
1766 If block wasn't generated (not in memory or on disk),
1768 void ServerEnvironment::deactivateFarObjects(bool force_delete)
1770 std::vector<u16> objects_to_remove;
1771 for(std::map<u16, ServerActiveObject*>::iterator
1772 i = m_active_objects.begin();
1773 i != m_active_objects.end(); ++i) {
1774 ServerActiveObject* obj = i->second;
1777 // Do not deactivate if static data creation not allowed
1778 if(!force_delete && !obj->isStaticAllowed())
1781 // If pending deactivation, let removeRemovedObjects() do it
1782 if(!force_delete && obj->m_pending_deactivation)
1786 v3f objectpos = obj->getBasePosition();
1788 // The block in which the object resides in
1789 v3s16 blockpos_o = getNodeBlockPos(floatToInt(objectpos, BS));
1791 // If object's static data is stored in a deactivated block and object
1792 // is actually located in an active block, re-save to the block in
1793 // which the object is actually located in.
1795 obj->m_static_exists &&
1796 !m_active_blocks.contains(obj->m_static_block) &&
1797 m_active_blocks.contains(blockpos_o))
1799 v3s16 old_static_block = obj->m_static_block;
1801 // Save to block where object is located
1802 MapBlock *block = m_map->emergeBlock(blockpos_o, false);
1804 errorstream<<"ServerEnvironment::deactivateFarObjects(): "
1805 <<"Could not save object id="<<id
1806 <<" to it's current block "<<PP(blockpos_o)
1810 std::string staticdata_new = obj->getStaticData();
1811 StaticObject s_obj(obj->getType(), objectpos, staticdata_new);
1812 block->m_static_objects.insert(id, s_obj);
1813 obj->m_static_block = blockpos_o;
1814 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1815 "deactivateFarObjects: Static data moved in");
1817 // Delete from block where object was located
1818 block = m_map->emergeBlock(old_static_block, false);
1820 errorstream<<"ServerEnvironment::deactivateFarObjects(): "
1821 <<"Could not delete object id="<<id
1822 <<" from it's previous block "<<PP(old_static_block)
1826 block->m_static_objects.remove(id);
1827 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1828 "deactivateFarObjects: Static data moved out");
1832 // If block is active, don't remove
1833 if(!force_delete && m_active_blocks.contains(blockpos_o))
1836 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
1837 <<"deactivating object id="<<id<<" on inactive block "
1838 <<PP(blockpos_o)<<std::endl;
1840 // If known by some client, don't immediately delete.
1841 bool pending_delete = (obj->m_known_by_count > 0 && !force_delete);
1844 Update the static data
1847 if(obj->isStaticAllowed())
1849 // Create new static object
1850 std::string staticdata_new = obj->getStaticData();
1851 StaticObject s_obj(obj->getType(), objectpos, staticdata_new);
1853 bool stays_in_same_block = false;
1854 bool data_changed = true;
1856 if(obj->m_static_exists){
1857 if(obj->m_static_block == blockpos_o)
1858 stays_in_same_block = true;
1860 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
1862 std::map<u16, StaticObject>::iterator n =
1863 block->m_static_objects.m_active.find(id);
1864 if(n != block->m_static_objects.m_active.end()){
1865 StaticObject static_old = n->second;
1867 float save_movem = obj->getMinimumSavedMovement();
1869 if(static_old.data == staticdata_new &&
1870 (static_old.pos - objectpos).getLength() < save_movem)
1871 data_changed = false;
1873 errorstream<<"ServerEnvironment::deactivateFarObjects(): "
1874 <<"id="<<id<<" m_static_exists=true but "
1875 <<"static data doesn't actually exist in "
1876 <<PP(obj->m_static_block)<<std::endl;
1880 bool shall_be_written = (!stays_in_same_block || data_changed);
1882 // Delete old static object
1883 if(obj->m_static_exists)
1885 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
1888 block->m_static_objects.remove(id);
1889 obj->m_static_exists = false;
1890 // Only mark block as modified if data changed considerably
1891 if(shall_be_written)
1892 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1893 "deactivateFarObjects: Static data "
1894 "changed considerably");
1898 // Add to the block where the object is located in
1899 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1900 // Get or generate the block
1901 MapBlock *block = NULL;
1903 block = m_map->emergeBlock(blockpos);
1904 } catch(InvalidPositionException &e){
1905 // Handled via NULL pointer
1906 // NOTE: emergeBlock's failure is usually determined by it
1907 // actually returning NULL
1912 if(block->m_static_objects.m_stored.size() >= g_settings->getU16("max_objects_per_block")){
1913 errorstream<<"ServerEnv: Trying to store id="<<obj->getId()
1914 <<" statically but block "<<PP(blockpos)
1915 <<" already contains "
1916 <<block->m_static_objects.m_stored.size()
1918 <<" Forcing delete."<<std::endl;
1919 force_delete = true;
1921 // If static counterpart already exists in target block,
1923 // This shouldn't happen because the object is removed from
1924 // the previous block before this according to
1925 // obj->m_static_block, but happens rarely for some unknown
1926 // reason. Unsuccessful attempts have been made to find
1928 if(id && block->m_static_objects.m_active.find(id) != block->m_static_objects.m_active.end()){
1929 infostream<<"ServerEnv: WARNING: Performing hack #83274"
1931 block->m_static_objects.remove(id);
1933 // Store static data
1934 u16 store_id = pending_delete ? id : 0;
1935 block->m_static_objects.insert(store_id, s_obj);
1937 // Only mark block as modified if data changed considerably
1938 if(shall_be_written)
1939 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1940 "deactivateFarObjects: Static data "
1941 "changed considerably");
1943 obj->m_static_exists = true;
1944 obj->m_static_block = block->getPos();
1949 v3s16 p = floatToInt(objectpos, BS);
1950 errorstream<<"ServerEnv: Could not find or generate "
1951 <<"a block for storing id="<<obj->getId()
1952 <<" statically (pos="<<PP(p)<<")"<<std::endl;
1959 If known by some client, set pending deactivation.
1960 Otherwise delete it immediately.
1963 if(pending_delete && !force_delete)
1965 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
1966 <<"object id="<<id<<" is known by clients"
1967 <<"; not deleting yet"<<std::endl;
1969 obj->m_pending_deactivation = true;
1973 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
1974 <<"object id="<<id<<" is not known by clients"
1975 <<"; deleting"<<std::endl;
1977 // Tell the object about removal
1978 obj->removingFromEnvironment();
1979 // Deregister in scripting api
1980 m_script->removeObjectReference(obj);
1982 // Delete active object
1983 if(obj->environmentDeletes())
1985 // Id to be removed from m_active_objects
1986 objects_to_remove.push_back(id);
1989 // Remove references from m_active_objects
1990 for(std::vector<u16>::iterator i = objects_to_remove.begin();
1991 i != objects_to_remove.end(); ++i) {
1992 m_active_objects.erase(*i);
1999 #include "clientsimpleobject.h"
2005 ClientEnvironment::ClientEnvironment(ClientMap *map, scene::ISceneManager *smgr,
2006 ITextureSource *texturesource, IGameDef *gamedef,
2007 IrrlichtDevice *irr):
2010 m_texturesource(texturesource),
2015 memset(m_attachements, zero, sizeof(m_attachements));
2018 ClientEnvironment::~ClientEnvironment()
2020 // delete active objects
2021 for(std::map<u16, ClientActiveObject*>::iterator
2022 i = m_active_objects.begin();
2023 i != m_active_objects.end(); ++i)
2028 for(std::vector<ClientSimpleObject*>::iterator
2029 i = m_simple_objects.begin(); i != m_simple_objects.end(); ++i) {
2037 Map & ClientEnvironment::getMap()
2042 ClientMap & ClientEnvironment::getClientMap()
2047 void ClientEnvironment::addPlayer(Player *player)
2049 DSTACK(__FUNCTION_NAME);
2051 It is a failure if player is local and there already is a local
2054 assert(!(player->isLocal() == true && getLocalPlayer() != NULL));
2056 Environment::addPlayer(player);
2059 LocalPlayer * ClientEnvironment::getLocalPlayer()
2061 for(std::vector<Player*>::iterator i = m_players.begin();
2062 i != m_players.end(); ++i) {
2063 Player *player = *i;
2064 if(player->isLocal())
2065 return (LocalPlayer*)player;
2070 void ClientEnvironment::step(float dtime)
2072 DSTACK(__FUNCTION_NAME);
2074 /* Step time of day */
2075 stepTimeOfDay(dtime);
2077 // Get some settings
2078 bool fly_allowed = m_gamedef->checkLocalPrivilege("fly");
2079 bool free_move = fly_allowed && g_settings->getBool("free_move");
2082 LocalPlayer *lplayer = getLocalPlayer();
2084 // collision info queue
2085 std::vector<CollisionInfo> player_collisions;
2088 Get the speed the player is going
2090 bool is_climbing = lplayer->is_climbing;
2092 f32 player_speed = lplayer->getSpeed().getLength();
2095 Maximum position increment
2097 //f32 position_max_increment = 0.05*BS;
2098 f32 position_max_increment = 0.1*BS;
2100 // Maximum time increment (for collision detection etc)
2101 // time = distance / speed
2102 f32 dtime_max_increment = 1;
2103 if(player_speed > 0.001)
2104 dtime_max_increment = position_max_increment / player_speed;
2106 // Maximum time increment is 10ms or lower
2107 if(dtime_max_increment > 0.01)
2108 dtime_max_increment = 0.01;
2110 // Don't allow overly huge dtime
2114 f32 dtime_downcount = dtime;
2117 Stuff that has a maximum time increment
2126 if(dtime_downcount > dtime_max_increment)
2128 dtime_part = dtime_max_increment;
2129 dtime_downcount -= dtime_part;
2133 dtime_part = dtime_downcount;
2135 Setting this to 0 (no -=dtime_part) disables an infinite loop
2136 when dtime_part is so small that dtime_downcount -= dtime_part
2139 dtime_downcount = 0;
2148 if(free_move == false && is_climbing == false)
2151 v3f speed = lplayer->getSpeed();
2152 if(lplayer->in_liquid == false)
2153 speed.Y -= lplayer->movement_gravity * lplayer->physics_override_gravity * dtime_part * 2;
2155 // Liquid floating / sinking
2156 if(lplayer->in_liquid && !lplayer->swimming_vertical)
2157 speed.Y -= lplayer->movement_liquid_sink * dtime_part * 2;
2159 // Liquid resistance
2160 if(lplayer->in_liquid_stable || lplayer->in_liquid)
2162 // How much the node's viscosity blocks movement, ranges between 0 and 1
2163 // Should match the scale at which viscosity increase affects other liquid attributes
2164 const f32 viscosity_factor = 0.3;
2166 v3f d_wanted = -speed / lplayer->movement_liquid_fluidity;
2167 f32 dl = d_wanted.getLength();
2168 if(dl > lplayer->movement_liquid_fluidity_smooth)
2169 dl = lplayer->movement_liquid_fluidity_smooth;
2170 dl *= (lplayer->liquid_viscosity * viscosity_factor) + (1 - viscosity_factor);
2172 v3f d = d_wanted.normalize() * dl;
2176 if(speed.X > lplayer->movement_liquid_fluidity + lplayer->movement_liquid_fluidity_smooth) speed.X -= lplayer->movement_liquid_fluidity_smooth;
2177 if(speed.X < -lplayer->movement_liquid_fluidity - lplayer->movement_liquid_fluidity_smooth) speed.X += lplayer->movement_liquid_fluidity_smooth;
2178 if(speed.Y > lplayer->movement_liquid_fluidity + lplayer->movement_liquid_fluidity_smooth) speed.Y -= lplayer->movement_liquid_fluidity_smooth;
2179 if(speed.Y < -lplayer->movement_liquid_fluidity - lplayer->movement_liquid_fluidity_smooth) speed.Y += lplayer->movement_liquid_fluidity_smooth;
2180 if(speed.Z > lplayer->movement_liquid_fluidity + lplayer->movement_liquid_fluidity_smooth) speed.Z -= lplayer->movement_liquid_fluidity_smooth;
2181 if(speed.Z < -lplayer->movement_liquid_fluidity - lplayer->movement_liquid_fluidity_smooth) speed.Z += lplayer->movement_liquid_fluidity_smooth;
2185 lplayer->setSpeed(speed);
2190 This also does collision detection.
2192 lplayer->move(dtime_part, this, position_max_increment,
2193 &player_collisions);
2196 while(dtime_downcount > 0.001);
2198 //std::cout<<"Looped "<<loopcount<<" times."<<std::endl;
2200 for(std::vector<CollisionInfo>::iterator i = player_collisions.begin();
2201 i != player_collisions.end(); ++i) {
2202 CollisionInfo &info = *i;
2203 v3f speed_diff = info.new_speed - info.old_speed;;
2204 // Handle only fall damage
2205 // (because otherwise walking against something in fast_move kills you)
2206 if(speed_diff.Y < 0 || info.old_speed.Y >= 0)
2208 // Get rid of other components
2211 f32 pre_factor = 1; // 1 hp per node/s
2212 f32 tolerance = BS*14; // 5 without damage
2213 f32 post_factor = 1; // 1 hp per node/s
2214 if(info.type == COLLISION_NODE)
2216 const ContentFeatures &f = m_gamedef->ndef()->
2217 get(m_map->getNodeNoEx(info.node_p));
2218 // Determine fall damage multiplier
2219 int addp = itemgroup_get(f.groups, "fall_damage_add_percent");
2220 pre_factor = 1.0 + (float)addp/100.0;
2222 float speed = pre_factor * speed_diff.getLength();
2223 if(speed > tolerance)
2225 f32 damage_f = (speed - tolerance)/BS * post_factor;
2226 u16 damage = (u16)(damage_f+0.5);
2228 damageLocalPlayer(damage, true);
2229 MtEvent *e = new SimpleTriggerEvent("PlayerFallingDamage");
2230 m_gamedef->event()->put(e);
2236 A quick draft of lava damage
2238 if(m_lava_hurt_interval.step(dtime, 1.0))
2240 v3f pf = lplayer->getPosition();
2242 // Feet, middle and head
2243 v3s16 p1 = floatToInt(pf + v3f(0, BS*0.1, 0), BS);
2244 MapNode n1 = m_map->getNodeNoEx(p1);
2245 v3s16 p2 = floatToInt(pf + v3f(0, BS*0.8, 0), BS);
2246 MapNode n2 = m_map->getNodeNoEx(p2);
2247 v3s16 p3 = floatToInt(pf + v3f(0, BS*1.6, 0), BS);
2248 MapNode n3 = m_map->getNodeNoEx(p3);
2250 u32 damage_per_second = 0;
2251 damage_per_second = MYMAX(damage_per_second,
2252 m_gamedef->ndef()->get(n1).damage_per_second);
2253 damage_per_second = MYMAX(damage_per_second,
2254 m_gamedef->ndef()->get(n2).damage_per_second);
2255 damage_per_second = MYMAX(damage_per_second,
2256 m_gamedef->ndef()->get(n3).damage_per_second);
2258 if(damage_per_second != 0)
2260 damageLocalPlayer(damage_per_second, true);
2267 if(m_drowning_interval.step(dtime, 2.0))
2269 v3f pf = lplayer->getPosition();
2272 v3s16 p = floatToInt(pf + v3f(0, BS*1.6, 0), BS);
2273 MapNode n = m_map->getNodeNoEx(p);
2274 ContentFeatures c = m_gamedef->ndef()->get(n);
2275 u8 drowning_damage = c.drowning;
2276 if(drowning_damage > 0 && lplayer->hp > 0){
2277 u16 breath = lplayer->getBreath();
2284 lplayer->setBreath(breath);
2285 updateLocalPlayerBreath(breath);
2288 if(lplayer->getBreath() == 0 && drowning_damage > 0){
2289 damageLocalPlayer(drowning_damage, true);
2292 if(m_breathing_interval.step(dtime, 0.5))
2294 v3f pf = lplayer->getPosition();
2297 v3s16 p = floatToInt(pf + v3f(0, BS*1.6, 0), BS);
2298 MapNode n = m_map->getNodeNoEx(p);
2299 ContentFeatures c = m_gamedef->ndef()->get(n);
2301 lplayer->setBreath(11);
2303 else if(c.drowning == 0){
2304 u16 breath = lplayer->getBreath();
2307 lplayer->setBreath(breath);
2308 updateLocalPlayerBreath(breath);
2314 Stuff that can be done in an arbitarily large dtime
2316 for(std::vector<Player*>::iterator i = m_players.begin();
2317 i != m_players.end(); ++i) {
2318 Player *player = *i;
2321 Handle non-local players
2323 if(player->isLocal() == false) {
2325 player->move(dtime, this, 100*BS);
2330 // Update lighting on local player (used for wield item)
2331 u32 day_night_ratio = getDayNightRatio();
2335 // On InvalidPositionException, use this as default
2336 // (day: LIGHT_SUN, night: 0)
2337 MapNode node_at_lplayer(CONTENT_AIR, 0x0f, 0);
2339 v3s16 p = lplayer->getLightPosition();
2340 node_at_lplayer = m_map->getNodeNoEx(p);
2342 u16 light = getInteriorLight(node_at_lplayer, 0, m_gamedef->ndef());
2343 u8 day = light & 0xff;
2344 u8 night = (light >> 8) & 0xff;
2345 finalColorBlend(lplayer->light_color, day, night, day_night_ratio);
2349 Step active objects and update lighting of them
2352 g_profiler->avg("CEnv: num of objects", m_active_objects.size());
2353 bool update_lighting = m_active_object_light_update_interval.step(dtime, 0.21);
2354 for(std::map<u16, ClientActiveObject*>::iterator
2355 i = m_active_objects.begin();
2356 i != m_active_objects.end(); ++i)
2358 ClientActiveObject* obj = i->second;
2360 obj->step(dtime, this);
2369 v3s16 p = obj->getLightPosition();
2370 MapNode n = m_map->getNodeNoEx(p, &pos_ok);
2372 light = n.getLightBlend(day_night_ratio, m_gamedef->ndef());
2374 light = blend_light(day_night_ratio, LIGHT_SUN, 0);
2376 obj->updateLight(light);
2381 Step and handle simple objects
2383 g_profiler->avg("CEnv: num of simple objects", m_simple_objects.size());
2384 for(std::vector<ClientSimpleObject*>::iterator
2385 i = m_simple_objects.begin(); i != m_simple_objects.end();) {
2386 std::vector<ClientSimpleObject*>::iterator cur = i;
2387 ClientSimpleObject *simple = *cur;
2389 simple->step(dtime);
2390 if(simple->m_to_be_removed) {
2392 i = m_simple_objects.erase(cur);
2400 void ClientEnvironment::addSimpleObject(ClientSimpleObject *simple)
2402 m_simple_objects.push_back(simple);
2405 ClientActiveObject* ClientEnvironment::getActiveObject(u16 id)
2407 std::map<u16, ClientActiveObject*>::iterator n;
2408 n = m_active_objects.find(id);
2409 if(n == m_active_objects.end())
2414 bool isFreeClientActiveObjectId(u16 id,
2415 std::map<u16, ClientActiveObject*> &objects)
2420 return objects.find(id) == objects.end();
2423 u16 getFreeClientActiveObjectId(
2424 std::map<u16, ClientActiveObject*> &objects)
2426 //try to reuse id's as late as possible
2427 static u16 last_used_id = 0;
2428 u16 startid = last_used_id;
2432 if(isFreeClientActiveObjectId(last_used_id, objects))
2433 return last_used_id;
2435 if(last_used_id == startid)
2440 u16 ClientEnvironment::addActiveObject(ClientActiveObject *object)
2443 if(object->getId() == 0)
2445 u16 new_id = getFreeClientActiveObjectId(m_active_objects);
2448 infostream<<"ClientEnvironment::addActiveObject(): "
2449 <<"no free ids available"<<std::endl;
2453 object->setId(new_id);
2455 if(isFreeClientActiveObjectId(object->getId(), m_active_objects) == false)
2457 infostream<<"ClientEnvironment::addActiveObject(): "
2458 <<"id is not free ("<<object->getId()<<")"<<std::endl;
2462 infostream<<"ClientEnvironment::addActiveObject(): "
2463 <<"added (id="<<object->getId()<<")"<<std::endl;
2464 m_active_objects[object->getId()] = object;
2465 object->addToScene(m_smgr, m_texturesource, m_irr);
2466 { // Update lighting immediately
2471 v3s16 p = object->getLightPosition();
2472 MapNode n = m_map->getNodeNoEx(p, &pos_ok);
2474 light = n.getLightBlend(getDayNightRatio(), m_gamedef->ndef());
2476 light = blend_light(getDayNightRatio(), LIGHT_SUN, 0);
2478 object->updateLight(light);
2480 return object->getId();
2483 void ClientEnvironment::addActiveObject(u16 id, u8 type,
2484 const std::string &init_data)
2486 ClientActiveObject* obj =
2487 ClientActiveObject::create((ActiveObjectType) type, m_gamedef, this);
2490 infostream<<"ClientEnvironment::addActiveObject(): "
2491 <<"id="<<id<<" type="<<type<<": Couldn't create object"
2500 obj->initialize(init_data);
2502 catch(SerializationError &e)
2504 errorstream<<"ClientEnvironment::addActiveObject():"
2505 <<" id="<<id<<" type="<<type
2506 <<": SerializationError in initialize(): "
2508 <<": init_data="<<serializeJsonString(init_data)
2512 addActiveObject(obj);
2515 void ClientEnvironment::removeActiveObject(u16 id)
2517 verbosestream<<"ClientEnvironment::removeActiveObject(): "
2518 <<"id="<<id<<std::endl;
2519 ClientActiveObject* obj = getActiveObject(id);
2522 infostream<<"ClientEnvironment::removeActiveObject(): "
2523 <<"id="<<id<<" not found"<<std::endl;
2526 obj->removeFromScene(true);
2528 m_active_objects.erase(id);
2531 void ClientEnvironment::processActiveObjectMessage(u16 id,
2532 const std::string &data)
2534 ClientActiveObject* obj = getActiveObject(id);
2537 infostream<<"ClientEnvironment::processActiveObjectMessage():"
2538 <<" got message for id="<<id<<", which doesn't exist."
2544 obj->processMessage(data);
2546 catch(SerializationError &e)
2548 errorstream<<"ClientEnvironment::processActiveObjectMessage():"
2549 <<" id="<<id<<" type="<<obj->getType()
2550 <<" SerializationError in processMessage(),"
2551 <<" message="<<serializeJsonString(data)
2557 Callbacks for activeobjects
2560 void ClientEnvironment::damageLocalPlayer(u8 damage, bool handle_hp)
2562 LocalPlayer *lplayer = getLocalPlayer();
2566 if (lplayer->hp == 0) // Don't damage a dead player
2568 if(lplayer->hp > damage)
2569 lplayer->hp -= damage;
2574 ClientEnvEvent event;
2575 event.type = CEE_PLAYER_DAMAGE;
2576 event.player_damage.amount = damage;
2577 event.player_damage.send_to_server = handle_hp;
2578 m_client_event_queue.push_back(event);
2581 void ClientEnvironment::updateLocalPlayerBreath(u16 breath)
2583 ClientEnvEvent event;
2584 event.type = CEE_PLAYER_BREATH;
2585 event.player_breath.amount = breath;
2586 m_client_event_queue.push_back(event);
2590 Client likes to call these
2593 void ClientEnvironment::getActiveObjects(v3f origin, f32 max_d,
2594 std::vector<DistanceSortedActiveObject> &dest)
2596 for(std::map<u16, ClientActiveObject*>::iterator
2597 i = m_active_objects.begin();
2598 i != m_active_objects.end(); ++i)
2600 ClientActiveObject* obj = i->second;
2602 f32 d = (obj->getPosition() - origin).getLength();
2607 DistanceSortedActiveObject dso(obj, d);
2609 dest.push_back(dso);
2613 ClientEnvEvent ClientEnvironment::getClientEvent()
2615 ClientEnvEvent event;
2616 if(m_client_event_queue.empty())
2617 event.type = CEE_NONE;
2619 event = m_client_event_queue.front();
2620 m_client_event_queue.pop_front();
2625 #endif // #ifndef SERVER