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_lock);
207 m_time_of_day_speed = speed;
210 float Environment::getTimeOfDaySpeed()
212 JMutexAutoLock(this->m_lock);
213 float retval = m_time_of_day_speed;
217 void Environment::stepTimeOfDay(float dtime)
221 JMutexAutoLock(this->m_lock);
222 day_speed = m_time_of_day_speed;
225 m_time_counter += dtime;
226 f32 speed = day_speed * 24000./(24.*3600);
227 u32 units = (u32)(m_time_counter*speed);
231 if(m_time_of_day + units >= 24000)
233 m_time_of_day = (m_time_of_day + units) % 24000;
235 m_time_of_day_f = (float)m_time_of_day / 24000.0;
238 m_time_counter -= (f32)units / speed;
241 m_time_of_day_f += day_speed/24/3600*dtime;
242 if(m_time_of_day_f > 1.0)
243 m_time_of_day_f -= 1.0;
244 if(m_time_of_day_f < 0.0)
245 m_time_of_day_f += 1.0;
253 ABMWithState::ABMWithState(ActiveBlockModifier *abm_):
257 // Initialize timer to random value to spread processing
258 float itv = abm->getTriggerInterval();
259 itv = MYMAX(0.001, itv); // No less than 1ms
260 int minval = MYMAX(-0.51*itv, -60); // Clamp to
261 int maxval = MYMIN(0.51*itv, 60); // +-60 seconds
262 timer = myrand_range(minval, maxval);
269 void fillRadiusBlock(v3s16 p0, s16 r, std::set<v3s16> &list)
272 for(p.X=p0.X-r; p.X<=p0.X+r; p.X++)
273 for(p.Y=p0.Y-r; p.Y<=p0.Y+r; p.Y++)
274 for(p.Z=p0.Z-r; p.Z<=p0.Z+r; p.Z++)
281 void ActiveBlockList::update(std::vector<v3s16> &active_positions,
283 std::set<v3s16> &blocks_removed,
284 std::set<v3s16> &blocks_added)
289 std::set<v3s16> newlist = m_forceloaded_list;
290 for(std::vector<v3s16>::iterator i = active_positions.begin();
291 i != active_positions.end(); ++i)
293 fillRadiusBlock(*i, radius, newlist);
297 Find out which blocks on the old list are not on the new list
299 // Go through old list
300 for(std::set<v3s16>::iterator i = m_list.begin();
301 i != m_list.end(); ++i)
304 // If not on new list, it's been removed
305 if(newlist.find(p) == newlist.end())
306 blocks_removed.insert(p);
310 Find out which blocks on the new list are not on the old list
312 // Go through new list
313 for(std::set<v3s16>::iterator i = newlist.begin();
314 i != newlist.end(); ++i)
317 // If not on old list, it's been added
318 if(m_list.find(p) == m_list.end())
319 blocks_added.insert(p);
326 for(std::set<v3s16>::iterator i = newlist.begin();
327 i != newlist.end(); ++i)
338 ServerEnvironment::ServerEnvironment(ServerMap *map,
339 GameScripting *scriptIface, IGameDef *gamedef,
340 const std::string &path_world) :
342 m_script(scriptIface),
344 m_path_world(path_world),
345 m_send_recommended_timer(0),
346 m_active_block_interval_overload_skip(0),
348 m_game_time_fraction_counter(0),
349 m_recommended_send_interval(0.1),
350 m_max_lag_estimate(0.1)
354 ServerEnvironment::~ServerEnvironment()
356 // Clear active block list.
357 // This makes the next one delete all active objects.
358 m_active_blocks.clear();
360 // Convert all objects to static and delete the active objects
361 deactivateFarObjects(true);
366 // Delete ActiveBlockModifiers
367 for(std::list<ABMWithState>::iterator
368 i = m_abms.begin(); i != m_abms.end(); ++i){
373 Map & ServerEnvironment::getMap()
378 ServerMap & ServerEnvironment::getServerMap()
383 bool ServerEnvironment::line_of_sight(v3f pos1, v3f pos2, float stepsize, v3s16 *p)
385 float distance = pos1.getDistanceFrom(pos2);
387 //calculate normalized direction vector
388 v3f normalized_vector = v3f((pos2.X - pos1.X)/distance,
389 (pos2.Y - pos1.Y)/distance,
390 (pos2.Z - pos1.Z)/distance);
392 //find out if there's a node on path between pos1 and pos2
393 for (float i = 1; i < distance; i += stepsize) {
394 v3s16 pos = floatToInt(v3f(normalized_vector.X * i,
395 normalized_vector.Y * i,
396 normalized_vector.Z * i) +pos1,BS);
398 MapNode n = getMap().getNodeNoEx(pos);
400 if(n.param0 != CONTENT_AIR) {
410 void ServerEnvironment::saveLoadedPlayers()
412 std::string players_path = m_path_world + DIR_DELIM "players";
413 fs::CreateDir(players_path);
415 for (std::vector<Player*>::iterator it = m_players.begin();
416 it != m_players.end();
418 RemotePlayer *player = static_cast<RemotePlayer*>(*it);
419 if (player->checkModified()) {
420 player->save(players_path);
425 void ServerEnvironment::savePlayer(const std::string &playername)
427 std::string players_path = m_path_world + DIR_DELIM "players";
428 fs::CreateDir(players_path);
430 RemotePlayer *player = static_cast<RemotePlayer*>(getPlayer(playername.c_str()));
432 player->save(players_path);
436 Player *ServerEnvironment::loadPlayer(const std::string &playername)
438 std::string players_path = m_path_world + DIR_DELIM "players" DIR_DELIM;
440 RemotePlayer *player = static_cast<RemotePlayer*>(getPlayer(playername.c_str()));
441 bool newplayer = false;
444 player = new RemotePlayer(m_gamedef, playername.c_str());
448 RemotePlayer testplayer(m_gamedef, "");
449 std::string path = players_path + playername;
450 for (u32 i = 0; i < PLAYER_FILE_ALTERNATE_TRIES; i++) {
451 // Open file and deserialize
452 std::ifstream is(path.c_str(), std::ios_base::binary);
456 testplayer.deSerialize(is, path);
458 if (testplayer.getName() == playername) {
459 *player = testplayer;
463 path = players_path + playername + itos(i);
466 infostream << "Player file for player " << playername
467 << " not found" << std::endl;
473 player->setModified(false);
477 void ServerEnvironment::saveMeta()
479 std::string path = m_path_world + DIR_DELIM "env_meta.txt";
481 // Open file and serialize
482 std::ostringstream ss(std::ios_base::binary);
485 args.setU64("game_time", m_game_time);
486 args.setU64("time_of_day", getTimeOfDay());
490 if(!fs::safeWriteToFile(path, ss.str()))
492 infostream<<"ServerEnvironment::saveMeta(): Failed to write "
494 throw SerializationError("Couldn't save env meta");
498 void ServerEnvironment::loadMeta()
500 std::string path = m_path_world + DIR_DELIM "env_meta.txt";
502 // Open file and deserialize
503 std::ifstream is(path.c_str(), std::ios_base::binary);
505 infostream << "ServerEnvironment::loadMeta(): Failed to open "
506 << path << std::endl;
507 throw SerializationError("Couldn't load env meta");
512 if (!args.parseConfigLines(is, "EnvArgsEnd")) {
513 throw SerializationError("ServerEnvironment::loadMeta(): "
514 "EnvArgsEnd not found!");
518 m_game_time = args.getU64("game_time");
519 } catch (SettingNotFoundException &e) {
520 // Getting this is crucial, otherwise timestamps are useless
521 throw SerializationError("Couldn't load env meta game_time");
525 m_time_of_day = args.getU64("time_of_day");
526 } catch (SettingNotFoundException &e) {
527 // This is not as important
528 m_time_of_day = 9000;
534 ActiveBlockModifier *abm;
536 std::set<content_t> required_neighbors;
542 ServerEnvironment *m_env;
543 std::map<content_t, std::vector<ActiveABM> > m_aabms;
545 ABMHandler(std::list<ABMWithState> &abms,
546 float dtime_s, ServerEnvironment *env,
552 INodeDefManager *ndef = env->getGameDef()->ndef();
553 for(std::list<ABMWithState>::iterator
554 i = abms.begin(); i != abms.end(); ++i){
555 ActiveBlockModifier *abm = i->abm;
556 float trigger_interval = abm->getTriggerInterval();
557 if(trigger_interval < 0.001)
558 trigger_interval = 0.001;
559 float actual_interval = dtime_s;
562 if(i->timer < trigger_interval)
564 i->timer -= trigger_interval;
565 actual_interval = trigger_interval;
567 float intervals = actual_interval / trigger_interval;
570 float chance = abm->getTriggerChance();
575 aabm.chance = chance / intervals;
579 std::set<std::string> required_neighbors_s
580 = abm->getRequiredNeighbors();
581 for(std::set<std::string>::iterator
582 i = required_neighbors_s.begin();
583 i != required_neighbors_s.end(); i++)
585 ndef->getIds(*i, aabm.required_neighbors);
588 std::set<std::string> contents_s = abm->getTriggerContents();
589 for(std::set<std::string>::iterator
590 i = contents_s.begin(); i != contents_s.end(); i++)
592 std::set<content_t> ids;
593 ndef->getIds(*i, ids);
594 for(std::set<content_t>::const_iterator k = ids.begin();
598 std::map<content_t, std::vector<ActiveABM> >::iterator j;
600 if(j == m_aabms.end()){
601 std::vector<ActiveABM> aabmlist;
602 m_aabms[c] = aabmlist;
605 j->second.push_back(aabm);
610 // Find out how many objects the given block and its neighbours contain.
611 // Returns the number of objects in the block, and also in 'wider' the
612 // number of objects in the block and all its neighbours. The latter
613 // may an estimate if any neighbours are unloaded.
614 u32 countObjects(MapBlock *block, ServerMap * map, u32 &wider)
617 u32 wider_unknown_count = 0;
618 for(s16 x=-1; x<=1; x++)
619 for(s16 y=-1; y<=1; y++)
620 for(s16 z=-1; z<=1; z++)
622 MapBlock *block2 = map->getBlockNoCreateNoEx(
623 block->getPos() + v3s16(x,y,z));
625 wider_unknown_count++;
628 wider += block2->m_static_objects.m_active.size()
629 + block2->m_static_objects.m_stored.size();
632 u32 active_object_count = block->m_static_objects.m_active.size();
633 u32 wider_known_count = 3*3*3 - wider_unknown_count;
634 wider += wider_unknown_count * wider / wider_known_count;
635 return active_object_count;
638 void apply(MapBlock *block)
643 ServerMap *map = &m_env->getServerMap();
645 u32 active_object_count_wider;
646 u32 active_object_count = this->countObjects(block, map, active_object_count_wider);
647 m_env->m_added_objects = 0;
650 for(p0.X=0; p0.X<MAP_BLOCKSIZE; p0.X++)
651 for(p0.Y=0; p0.Y<MAP_BLOCKSIZE; p0.Y++)
652 for(p0.Z=0; p0.Z<MAP_BLOCKSIZE; p0.Z++)
654 MapNode n = block->getNodeNoEx(p0);
655 content_t c = n.getContent();
656 v3s16 p = p0 + block->getPosRelative();
658 std::map<content_t, std::vector<ActiveABM> >::iterator j;
660 if(j == m_aabms.end())
663 for(std::vector<ActiveABM>::iterator
664 i = j->second.begin(); i != j->second.end(); i++) {
665 if(myrand() % i->chance != 0)
669 if(!i->required_neighbors.empty())
672 for(p1.X = p.X-1; p1.X <= p.X+1; p1.X++)
673 for(p1.Y = p.Y-1; p1.Y <= p.Y+1; p1.Y++)
674 for(p1.Z = p.Z-1; p1.Z <= p.Z+1; p1.Z++)
678 MapNode n = map->getNodeNoEx(p1);
679 content_t c = n.getContent();
680 std::set<content_t>::const_iterator k;
681 k = i->required_neighbors.find(c);
682 if(k != i->required_neighbors.end()){
686 // No required neighbor found
691 // Call all the trigger variations
692 i->abm->trigger(m_env, p, n);
693 i->abm->trigger(m_env, p, n,
694 active_object_count, active_object_count_wider);
696 // Count surrounding objects again if the abms added any
697 if(m_env->m_added_objects > 0) {
698 active_object_count = countObjects(block, map, active_object_count_wider);
699 m_env->m_added_objects = 0;
706 void ServerEnvironment::activateBlock(MapBlock *block, u32 additional_dtime)
708 // Reset usage timer immediately, otherwise a block that becomes active
709 // again at around the same time as it would normally be unloaded will
710 // get unloaded incorrectly. (I think this still leaves a small possibility
711 // of a race condition between this and server::AsyncRunStep, which only
712 // some kind of synchronisation will fix, but it at least reduces the window
713 // of opportunity for it to break from seconds to nanoseconds)
714 block->resetUsageTimer();
716 // Get time difference
718 u32 stamp = block->getTimestamp();
719 if(m_game_time > stamp && stamp != BLOCK_TIMESTAMP_UNDEFINED)
720 dtime_s = m_game_time - block->getTimestamp();
721 dtime_s += additional_dtime;
723 /*infostream<<"ServerEnvironment::activateBlock(): block timestamp: "
724 <<stamp<<", game time: "<<m_game_time<<std::endl;*/
726 // Set current time as timestamp
727 block->setTimestampNoChangedFlag(m_game_time);
729 /*infostream<<"ServerEnvironment::activateBlock(): block is "
730 <<dtime_s<<" seconds old."<<std::endl;*/
732 // Activate stored objects
733 activateObjects(block, dtime_s);
736 std::map<v3s16, NodeTimer> elapsed_timers =
737 block->m_node_timers.step((float)dtime_s);
738 if(!elapsed_timers.empty()){
740 for(std::map<v3s16, NodeTimer>::iterator
741 i = elapsed_timers.begin();
742 i != elapsed_timers.end(); i++){
743 n = block->getNodeNoEx(i->first);
744 v3s16 p = i->first + block->getPosRelative();
745 if(m_script->node_on_timer(p,n,i->second.elapsed))
746 block->setNodeTimer(i->first,NodeTimer(i->second.timeout,0));
750 /* Handle ActiveBlockModifiers */
751 ABMHandler abmhandler(m_abms, dtime_s, this, false);
752 abmhandler.apply(block);
755 void ServerEnvironment::addActiveBlockModifier(ActiveBlockModifier *abm)
757 m_abms.push_back(ABMWithState(abm));
760 bool ServerEnvironment::setNode(v3s16 p, const MapNode &n)
762 INodeDefManager *ndef = m_gamedef->ndef();
763 MapNode n_old = m_map->getNodeNoEx(p);
766 if (ndef->get(n_old).has_on_destruct)
767 m_script->node_on_destruct(p, n_old);
770 if (!m_map->addNodeWithEvent(p, n))
773 // Update active VoxelManipulator if a mapgen thread
774 m_map->updateVManip(p);
776 // Call post-destructor
777 if (ndef->get(n_old).has_after_destruct)
778 m_script->node_after_destruct(p, n_old);
781 if (ndef->get(n).has_on_construct)
782 m_script->node_on_construct(p, n);
787 bool ServerEnvironment::removeNode(v3s16 p)
789 INodeDefManager *ndef = m_gamedef->ndef();
790 MapNode n_old = m_map->getNodeNoEx(p);
793 if (ndef->get(n_old).has_on_destruct)
794 m_script->node_on_destruct(p, n_old);
797 // This is slightly optimized compared to addNodeWithEvent(air)
798 if (!m_map->removeNodeWithEvent(p))
801 // Update active VoxelManipulator if a mapgen thread
802 m_map->updateVManip(p);
804 // Call post-destructor
805 if (ndef->get(n_old).has_after_destruct)
806 m_script->node_after_destruct(p, n_old);
808 // Air doesn't require constructor
812 bool ServerEnvironment::swapNode(v3s16 p, const MapNode &n)
814 if (!m_map->addNodeWithEvent(p, n, false))
817 // Update active VoxelManipulator if a mapgen thread
818 m_map->updateVManip(p);
823 std::set<u16> ServerEnvironment::getObjectsInsideRadius(v3f pos, float radius)
825 std::set<u16> objects;
826 for(std::map<u16, ServerActiveObject*>::iterator
827 i = m_active_objects.begin();
828 i != m_active_objects.end(); ++i)
830 ServerActiveObject* obj = i->second;
832 v3f objectpos = obj->getBasePosition();
833 if(objectpos.getDistanceFrom(pos) > radius)
840 void ServerEnvironment::clearAllObjects()
842 infostream<<"ServerEnvironment::clearAllObjects(): "
843 <<"Removing all active objects"<<std::endl;
844 std::vector<u16> objects_to_remove;
845 for(std::map<u16, ServerActiveObject*>::iterator
846 i = m_active_objects.begin();
847 i != m_active_objects.end(); ++i) {
848 ServerActiveObject* obj = i->second;
849 if(obj->getType() == ACTIVEOBJECT_TYPE_PLAYER)
852 // Delete static object if block is loaded
853 if(obj->m_static_exists){
854 MapBlock *block = m_map->getBlockNoCreateNoEx(obj->m_static_block);
856 block->m_static_objects.remove(id);
857 block->raiseModified(MOD_STATE_WRITE_NEEDED,
859 obj->m_static_exists = false;
862 // If known by some client, don't delete immediately
863 if(obj->m_known_by_count > 0){
864 obj->m_pending_deactivation = true;
865 obj->m_removed = true;
869 // Tell the object about removal
870 obj->removingFromEnvironment();
871 // Deregister in scripting api
872 m_script->removeObjectReference(obj);
874 // Delete active object
875 if(obj->environmentDeletes())
877 // Id to be removed from m_active_objects
878 objects_to_remove.push_back(id);
881 // Remove references from m_active_objects
882 for(std::vector<u16>::iterator i = objects_to_remove.begin();
883 i != objects_to_remove.end(); ++i) {
884 m_active_objects.erase(*i);
887 // Get list of loaded blocks
888 std::vector<v3s16> loaded_blocks;
889 infostream<<"ServerEnvironment::clearAllObjects(): "
890 <<"Listing all loaded blocks"<<std::endl;
891 m_map->listAllLoadedBlocks(loaded_blocks);
892 infostream<<"ServerEnvironment::clearAllObjects(): "
893 <<"Done listing all loaded blocks: "
894 <<loaded_blocks.size()<<std::endl;
896 // Get list of loadable blocks
897 std::vector<v3s16> loadable_blocks;
898 infostream<<"ServerEnvironment::clearAllObjects(): "
899 <<"Listing all loadable blocks"<<std::endl;
900 m_map->listAllLoadableBlocks(loadable_blocks);
901 infostream<<"ServerEnvironment::clearAllObjects(): "
902 <<"Done listing all loadable blocks: "
903 <<loadable_blocks.size()
904 <<", now clearing"<<std::endl;
906 // Grab a reference on each loaded block to avoid unloading it
907 for(std::vector<v3s16>::iterator i = loaded_blocks.begin();
908 i != loaded_blocks.end(); ++i) {
910 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
915 // Remove objects in all loadable blocks
916 u32 unload_interval = g_settings->getS32("max_clearobjects_extra_loaded_blocks");
917 unload_interval = MYMAX(unload_interval, 1);
918 u32 report_interval = loadable_blocks.size() / 10;
919 u32 num_blocks_checked = 0;
920 u32 num_blocks_cleared = 0;
921 u32 num_objs_cleared = 0;
922 for(std::vector<v3s16>::iterator i = loadable_blocks.begin();
923 i != loadable_blocks.end(); ++i) {
925 MapBlock *block = m_map->emergeBlock(p, false);
927 errorstream<<"ServerEnvironment::clearAllObjects(): "
928 <<"Failed to emerge block "<<PP(p)<<std::endl;
931 u32 num_stored = block->m_static_objects.m_stored.size();
932 u32 num_active = block->m_static_objects.m_active.size();
933 if(num_stored != 0 || num_active != 0){
934 block->m_static_objects.m_stored.clear();
935 block->m_static_objects.m_active.clear();
936 block->raiseModified(MOD_STATE_WRITE_NEEDED,
938 num_objs_cleared += num_stored + num_active;
939 num_blocks_cleared++;
941 num_blocks_checked++;
943 if(report_interval != 0 &&
944 num_blocks_checked % report_interval == 0){
945 float percent = 100.0 * (float)num_blocks_checked /
946 loadable_blocks.size();
947 infostream<<"ServerEnvironment::clearAllObjects(): "
948 <<"Cleared "<<num_objs_cleared<<" objects"
949 <<" in "<<num_blocks_cleared<<" blocks ("
950 <<percent<<"%)"<<std::endl;
952 if(num_blocks_checked % unload_interval == 0){
953 m_map->unloadUnreferencedBlocks();
956 m_map->unloadUnreferencedBlocks();
958 // Drop references that were added above
959 for(std::vector<v3s16>::iterator i = loaded_blocks.begin();
960 i != loaded_blocks.end(); ++i) {
962 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
967 infostream<<"ServerEnvironment::clearAllObjects(): "
968 <<"Finished: Cleared "<<num_objs_cleared<<" objects"
969 <<" in "<<num_blocks_cleared<<" blocks"<<std::endl;
972 void ServerEnvironment::step(float dtime)
974 DSTACK(__FUNCTION_NAME);
976 //TimeTaker timer("ServerEnv step");
978 /* Step time of day */
979 stepTimeOfDay(dtime);
982 // NOTE: This is kind of funny on a singleplayer game, but doesn't
983 // really matter that much.
984 m_recommended_send_interval = g_settings->getFloat("dedicated_server_step");
990 m_game_time_fraction_counter += dtime;
991 u32 inc_i = (u32)m_game_time_fraction_counter;
992 m_game_time += inc_i;
993 m_game_time_fraction_counter -= (float)inc_i;
1000 ScopeProfiler sp(g_profiler, "SEnv: handle players avg", SPT_AVG);
1001 for(std::vector<Player*>::iterator i = m_players.begin();
1002 i != m_players.end(); ++i)
1004 Player *player = *i;
1006 // Ignore disconnected players
1007 if(player->peer_id == 0)
1011 player->move(dtime, this, 100*BS);
1016 Manage active block list
1018 if(m_active_blocks_management_interval.step(dtime, 2.0))
1020 ScopeProfiler sp(g_profiler, "SEnv: manage act. block list avg /2s", SPT_AVG);
1022 Get player block positions
1024 std::vector<v3s16> players_blockpos;
1025 for(std::vector<Player*>::iterator
1026 i = m_players.begin();
1027 i != m_players.end(); ++i) {
1028 Player *player = *i;
1029 // Ignore disconnected players
1030 if(player->peer_id == 0)
1033 v3s16 blockpos = getNodeBlockPos(
1034 floatToInt(player->getPosition(), BS));
1035 players_blockpos.push_back(blockpos);
1039 Update list of active blocks, collecting changes
1041 const s16 active_block_range = g_settings->getS16("active_block_range");
1042 std::set<v3s16> blocks_removed;
1043 std::set<v3s16> blocks_added;
1044 m_active_blocks.update(players_blockpos, active_block_range,
1045 blocks_removed, blocks_added);
1048 Handle removed blocks
1051 // Convert active objects that are no more in active blocks to static
1052 deactivateFarObjects(false);
1054 for(std::set<v3s16>::iterator
1055 i = blocks_removed.begin();
1056 i != blocks_removed.end(); ++i)
1060 /* infostream<<"Server: Block " << PP(p)
1061 << " became inactive"<<std::endl; */
1063 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1067 // Set current time as timestamp (and let it set ChangedFlag)
1068 block->setTimestamp(m_game_time);
1075 for(std::set<v3s16>::iterator
1076 i = blocks_added.begin();
1077 i != blocks_added.end(); ++i)
1081 MapBlock *block = m_map->getBlockOrEmerge(p);
1083 m_active_blocks.m_list.erase(p);
1087 activateBlock(block);
1088 /* infostream<<"Server: Block " << PP(p)
1089 << " became active"<<std::endl; */
1094 Mess around in active blocks
1096 if(m_active_blocks_nodemetadata_interval.step(dtime, 1.0))
1098 ScopeProfiler sp(g_profiler, "SEnv: mess in act. blocks avg /1s", SPT_AVG);
1102 for(std::set<v3s16>::iterator
1103 i = m_active_blocks.m_list.begin();
1104 i != m_active_blocks.m_list.end(); ++i)
1108 /*infostream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
1109 <<") being handled"<<std::endl;*/
1111 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1115 // Reset block usage timer
1116 block->resetUsageTimer();
1118 // Set current time as timestamp
1119 block->setTimestampNoChangedFlag(m_game_time);
1120 // If time has changed much from the one on disk,
1121 // set block to be saved when it is unloaded
1122 if(block->getTimestamp() > block->getDiskTimestamp() + 60)
1123 block->raiseModified(MOD_STATE_WRITE_AT_UNLOAD,
1124 "Timestamp older than 60s (step)");
1127 std::map<v3s16, NodeTimer> elapsed_timers =
1128 block->m_node_timers.step((float)dtime);
1129 if(!elapsed_timers.empty()){
1131 for(std::map<v3s16, NodeTimer>::iterator
1132 i = elapsed_timers.begin();
1133 i != elapsed_timers.end(); i++){
1134 n = block->getNodeNoEx(i->first);
1135 p = i->first + block->getPosRelative();
1136 if(m_script->node_on_timer(p,n,i->second.elapsed))
1137 block->setNodeTimer(i->first,NodeTimer(i->second.timeout,0));
1143 const float abm_interval = 1.0;
1144 if(m_active_block_modifier_interval.step(dtime, abm_interval))
1146 if(m_active_block_interval_overload_skip > 0){
1147 ScopeProfiler sp(g_profiler, "SEnv: ABM overload skips");
1148 m_active_block_interval_overload_skip--;
1151 ScopeProfiler sp(g_profiler, "SEnv: modify in blocks avg /1s", SPT_AVG);
1152 TimeTaker timer("modify in active blocks");
1154 // Initialize handling of ActiveBlockModifiers
1155 ABMHandler abmhandler(m_abms, abm_interval, this, true);
1157 for(std::set<v3s16>::iterator
1158 i = m_active_blocks.m_list.begin();
1159 i != m_active_blocks.m_list.end(); ++i)
1163 /*infostream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
1164 <<") being handled"<<std::endl;*/
1166 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1170 // Set current time as timestamp
1171 block->setTimestampNoChangedFlag(m_game_time);
1173 /* Handle ActiveBlockModifiers */
1174 abmhandler.apply(block);
1177 u32 time_ms = timer.stop(true);
1178 u32 max_time_ms = 200;
1179 if(time_ms > max_time_ms){
1180 infostream<<"WARNING: active block modifiers took "
1181 <<time_ms<<"ms (longer than "
1182 <<max_time_ms<<"ms)"<<std::endl;
1183 m_active_block_interval_overload_skip = (time_ms / max_time_ms) + 1;
1188 Step script environment (run global on_step())
1190 m_script->environment_Step(dtime);
1196 ScopeProfiler sp(g_profiler, "SEnv: step act. objs avg", SPT_AVG);
1197 //TimeTaker timer("Step active objects");
1199 g_profiler->avg("SEnv: num of objects", m_active_objects.size());
1201 // This helps the objects to send data at the same time
1202 bool send_recommended = false;
1203 m_send_recommended_timer += dtime;
1204 if(m_send_recommended_timer > getSendRecommendedInterval())
1206 m_send_recommended_timer -= getSendRecommendedInterval();
1207 send_recommended = true;
1210 for(std::map<u16, ServerActiveObject*>::iterator
1211 i = m_active_objects.begin();
1212 i != m_active_objects.end(); ++i)
1214 ServerActiveObject* obj = i->second;
1215 // Don't step if is to be removed or stored statically
1216 if(obj->m_removed || obj->m_pending_deactivation)
1219 obj->step(dtime, send_recommended);
1220 // Read messages from object
1221 while(!obj->m_messages_out.empty())
1223 m_active_object_messages.push_back(
1224 obj->m_messages_out.pop_front());
1230 Manage active objects
1232 if(m_object_management_interval.step(dtime, 0.5))
1234 ScopeProfiler sp(g_profiler, "SEnv: remove removed objs avg /.5s", SPT_AVG);
1236 Remove objects that satisfy (m_removed && m_known_by_count==0)
1238 removeRemovedObjects();
1242 ServerActiveObject* ServerEnvironment::getActiveObject(u16 id)
1244 std::map<u16, ServerActiveObject*>::iterator n;
1245 n = m_active_objects.find(id);
1246 if(n == m_active_objects.end())
1251 bool isFreeServerActiveObjectId(u16 id,
1252 std::map<u16, ServerActiveObject*> &objects)
1257 return objects.find(id) == objects.end();
1260 u16 getFreeServerActiveObjectId(
1261 std::map<u16, ServerActiveObject*> &objects)
1263 //try to reuse id's as late as possible
1264 static u16 last_used_id = 0;
1265 u16 startid = last_used_id;
1269 if(isFreeServerActiveObjectId(last_used_id, objects))
1270 return last_used_id;
1272 if(last_used_id == startid)
1277 u16 ServerEnvironment::addActiveObject(ServerActiveObject *object)
1281 u16 id = addActiveObjectRaw(object, true, 0);
1286 bool ServerEnvironment::addActiveObjectAsStatic(ServerActiveObject *obj)
1290 v3f objectpos = obj->getBasePosition();
1292 // The block in which the object resides in
1293 v3s16 blockpos_o = getNodeBlockPos(floatToInt(objectpos, BS));
1296 Update the static data
1299 // Create new static object
1300 std::string staticdata = obj->getStaticData();
1301 StaticObject s_obj(obj->getType(), objectpos, staticdata);
1302 // Add to the block where the object is located in
1303 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1304 // Get or generate the block
1305 MapBlock *block = m_map->emergeBlock(blockpos);
1307 bool succeeded = false;
1311 block->m_static_objects.insert(0, s_obj);
1312 block->raiseModified(MOD_STATE_WRITE_AT_UNLOAD,
1313 "addActiveObjectAsStatic");
1317 infostream<<"ServerEnvironment::addActiveObjectAsStatic: "
1318 <<"Could not find or generate "
1319 <<"a block for storing static object"<<std::endl;
1323 if(obj->environmentDeletes())
1331 Finds out what new objects have been added to
1332 inside a radius around a position
1334 void ServerEnvironment::getAddedActiveObjects(v3s16 pos, s16 radius,
1336 std::set<u16> ¤t_objects,
1337 std::set<u16> &added_objects)
1339 v3f pos_f = intToFloat(pos, BS);
1340 f32 radius_f = radius * BS;
1341 f32 player_radius_f = player_radius * BS;
1343 if (player_radius_f < 0)
1344 player_radius_f = 0;
1347 Go through the object list,
1348 - discard m_removed objects,
1349 - discard objects that are too far away,
1350 - discard objects that are found in current_objects.
1351 - add remaining objects to added_objects
1353 for(std::map<u16, ServerActiveObject*>::iterator
1354 i = m_active_objects.begin();
1355 i != m_active_objects.end(); ++i)
1359 ServerActiveObject *object = i->second;
1362 // Discard if removed or deactivating
1363 if(object->m_removed || object->m_pending_deactivation)
1366 f32 distance_f = object->getBasePosition().getDistanceFrom(pos_f);
1367 if (object->getType() == ACTIVEOBJECT_TYPE_PLAYER) {
1368 // Discard if too far
1369 if (distance_f > player_radius_f && player_radius_f != 0)
1371 } else if (distance_f > radius_f)
1374 // Discard if already on current_objects
1375 std::set<u16>::iterator n;
1376 n = current_objects.find(id);
1377 if(n != current_objects.end())
1379 // Add to added_objects
1380 added_objects.insert(id);
1385 Finds out what objects have been removed from
1386 inside a radius around a position
1388 void ServerEnvironment::getRemovedActiveObjects(v3s16 pos, s16 radius,
1390 std::set<u16> ¤t_objects,
1391 std::set<u16> &removed_objects)
1393 v3f pos_f = intToFloat(pos, BS);
1394 f32 radius_f = radius * BS;
1395 f32 player_radius_f = player_radius * BS;
1397 if (player_radius_f < 0)
1398 player_radius_f = 0;
1401 Go through current_objects; object is removed if:
1402 - object is not found in m_active_objects (this is actually an
1403 error condition; objects should be set m_removed=true and removed
1404 only after all clients have been informed about removal), or
1405 - object has m_removed=true, or
1406 - object is too far away
1408 for(std::set<u16>::iterator
1409 i = current_objects.begin();
1410 i != current_objects.end(); ++i)
1413 ServerActiveObject *object = getActiveObject(id);
1416 infostream<<"ServerEnvironment::getRemovedActiveObjects():"
1417 <<" object in current_objects is NULL"<<std::endl;
1418 removed_objects.insert(id);
1422 if(object->m_removed || object->m_pending_deactivation)
1424 removed_objects.insert(id);
1428 f32 distance_f = object->getBasePosition().getDistanceFrom(pos_f);
1429 if (object->getType() == ACTIVEOBJECT_TYPE_PLAYER) {
1430 if (distance_f <= player_radius_f || player_radius_f == 0)
1432 } else if (distance_f <= radius_f)
1435 // Object is no longer visible
1436 removed_objects.insert(id);
1440 ActiveObjectMessage ServerEnvironment::getActiveObjectMessage()
1442 if(m_active_object_messages.empty())
1443 return ActiveObjectMessage(0);
1445 ActiveObjectMessage message = m_active_object_messages.front();
1446 m_active_object_messages.pop_front();
1451 ************ Private methods *************
1454 u16 ServerEnvironment::addActiveObjectRaw(ServerActiveObject *object,
1455 bool set_changed, u32 dtime_s)
1458 if(object->getId() == 0){
1459 u16 new_id = getFreeServerActiveObjectId(m_active_objects);
1462 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1463 <<"no free ids available"<<std::endl;
1464 if(object->environmentDeletes())
1468 object->setId(new_id);
1471 verbosestream<<"ServerEnvironment::addActiveObjectRaw(): "
1472 <<"supplied with id "<<object->getId()<<std::endl;
1474 if(isFreeServerActiveObjectId(object->getId(), m_active_objects) == false)
1476 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1477 <<"id is not free ("<<object->getId()<<")"<<std::endl;
1478 if(object->environmentDeletes())
1482 /*infostream<<"ServerEnvironment::addActiveObjectRaw(): "
1483 <<"added (id="<<object->getId()<<")"<<std::endl;*/
1485 m_active_objects[object->getId()] = object;
1487 verbosestream<<"ServerEnvironment::addActiveObjectRaw(): "
1488 <<"Added id="<<object->getId()<<"; there are now "
1489 <<m_active_objects.size()<<" active objects."
1492 // Register reference in scripting api (must be done before post-init)
1493 m_script->addObjectReference(object);
1494 // Post-initialize object
1495 object->addedToEnvironment(dtime_s);
1497 // Add static data to block
1498 if(object->isStaticAllowed())
1500 // Add static object to active static list of the block
1501 v3f objectpos = object->getBasePosition();
1502 std::string staticdata = object->getStaticData();
1503 StaticObject s_obj(object->getType(), objectpos, staticdata);
1504 // Add to the block where the object is located in
1505 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1506 MapBlock *block = m_map->emergeBlock(blockpos);
1508 block->m_static_objects.m_active[object->getId()] = s_obj;
1509 object->m_static_exists = true;
1510 object->m_static_block = blockpos;
1513 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1514 "addActiveObjectRaw");
1516 v3s16 p = floatToInt(objectpos, BS);
1517 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1518 <<"could not emerge block for storing id="<<object->getId()
1519 <<" statically (pos="<<PP(p)<<")"<<std::endl;
1523 return object->getId();
1527 Remove objects that satisfy (m_removed && m_known_by_count==0)
1529 void ServerEnvironment::removeRemovedObjects()
1531 std::vector<u16> objects_to_remove;
1532 for(std::map<u16, ServerActiveObject*>::iterator
1533 i = m_active_objects.begin();
1534 i != m_active_objects.end(); ++i) {
1536 ServerActiveObject* obj = i->second;
1537 // This shouldn't happen but check it
1540 infostream<<"NULL object found in ServerEnvironment"
1541 <<" while finding removed objects. id="<<id<<std::endl;
1542 // Id to be removed from m_active_objects
1543 objects_to_remove.push_back(id);
1548 We will delete objects that are marked as removed or thatare
1549 waiting for deletion after deactivation
1551 if(obj->m_removed == false && obj->m_pending_deactivation == false)
1555 Delete static data from block if is marked as removed
1557 if(obj->m_static_exists && obj->m_removed)
1559 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
1561 block->m_static_objects.remove(id);
1562 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1563 "removeRemovedObjects/remove");
1564 obj->m_static_exists = false;
1566 infostream<<"Failed to emerge block from which an object to "
1567 <<"be removed was loaded from. id="<<id<<std::endl;
1571 // If m_known_by_count > 0, don't actually remove. On some future
1572 // invocation this will be 0, which is when removal will continue.
1573 if(obj->m_known_by_count > 0)
1577 Move static data from active to stored if not marked as removed
1579 if(obj->m_static_exists && !obj->m_removed){
1580 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
1582 std::map<u16, StaticObject>::iterator i =
1583 block->m_static_objects.m_active.find(id);
1584 if(i != block->m_static_objects.m_active.end()){
1585 block->m_static_objects.m_stored.push_back(i->second);
1586 block->m_static_objects.m_active.erase(id);
1587 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1588 "removeRemovedObjects/deactivate");
1591 infostream<<"Failed to emerge block from which an object to "
1592 <<"be deactivated was loaded from. id="<<id<<std::endl;
1596 // Tell the object about removal
1597 obj->removingFromEnvironment();
1598 // Deregister in scripting api
1599 m_script->removeObjectReference(obj);
1602 if(obj->environmentDeletes())
1605 // Id to be removed from m_active_objects
1606 objects_to_remove.push_back(id);
1608 // Remove references from m_active_objects
1609 for(std::vector<u16>::iterator i = objects_to_remove.begin();
1610 i != objects_to_remove.end(); ++i) {
1611 m_active_objects.erase(*i);
1615 static void print_hexdump(std::ostream &o, const std::string &data)
1617 const int linelength = 16;
1618 for(int l=0; ; l++){
1619 int i0 = linelength * l;
1620 bool at_end = false;
1621 int thislinelength = linelength;
1622 if(i0 + thislinelength > (int)data.size()){
1623 thislinelength = data.size() - i0;
1626 for(int di=0; di<linelength; di++){
1629 if(di<thislinelength)
1630 snprintf(buf, 4, "%.2x ", data[i]);
1632 snprintf(buf, 4, " ");
1636 for(int di=0; di<thislinelength; di++){
1650 Convert stored objects from blocks near the players to active.
1652 void ServerEnvironment::activateObjects(MapBlock *block, u32 dtime_s)
1657 // Ignore if no stored objects (to not set changed flag)
1658 if(block->m_static_objects.m_stored.empty())
1660 verbosestream<<"ServerEnvironment::activateObjects(): "
1661 <<"activating objects of block "<<PP(block->getPos())
1662 <<" ("<<block->m_static_objects.m_stored.size()
1663 <<" objects)"<<std::endl;
1664 bool large_amount = (block->m_static_objects.m_stored.size() > g_settings->getU16("max_objects_per_block"));
1666 errorstream<<"suspiciously large amount of objects detected: "
1667 <<block->m_static_objects.m_stored.size()<<" in "
1668 <<PP(block->getPos())
1669 <<"; removing all of them."<<std::endl;
1670 // Clear stored list
1671 block->m_static_objects.m_stored.clear();
1672 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1673 "stored list cleared in activateObjects due to "
1674 "large amount of objects");
1678 // Activate stored objects
1679 std::vector<StaticObject> new_stored;
1680 for(std::list<StaticObject>::iterator
1681 i = block->m_static_objects.m_stored.begin();
1682 i != block->m_static_objects.m_stored.end(); ++i) {
1683 StaticObject &s_obj = *i;
1685 // Create an active object from the data
1686 ServerActiveObject *obj = ServerActiveObject::create
1687 ((ActiveObjectType) s_obj.type, this, 0, s_obj.pos, s_obj.data);
1688 // If couldn't create object, store static data back.
1690 errorstream<<"ServerEnvironment::activateObjects(): "
1691 <<"failed to create active object from static object "
1692 <<"in block "<<PP(s_obj.pos/BS)
1693 <<" type="<<(int)s_obj.type<<" data:"<<std::endl;
1694 print_hexdump(verbosestream, s_obj.data);
1696 new_stored.push_back(s_obj);
1699 verbosestream<<"ServerEnvironment::activateObjects(): "
1700 <<"activated static object pos="<<PP(s_obj.pos/BS)
1701 <<" type="<<(int)s_obj.type<<std::endl;
1702 // This will also add the object to the active static list
1703 addActiveObjectRaw(obj, false, dtime_s);
1705 // Clear stored list
1706 block->m_static_objects.m_stored.clear();
1707 // Add leftover failed stuff to stored list
1708 for(std::vector<StaticObject>::iterator
1709 i = new_stored.begin();
1710 i != new_stored.end(); ++i) {
1711 StaticObject &s_obj = *i;
1712 block->m_static_objects.m_stored.push_back(s_obj);
1715 // Turn the active counterparts of activated objects not pending for
1717 for(std::map<u16, StaticObject>::iterator
1718 i = block->m_static_objects.m_active.begin();
1719 i != block->m_static_objects.m_active.end(); ++i)
1722 ServerActiveObject *object = getActiveObject(id);
1724 object->m_pending_deactivation = false;
1728 Note: Block hasn't really been modified here.
1729 The objects have just been activated and moved from the stored
1730 static list to the active static list.
1731 As such, the block is essentially the same.
1732 Thus, do not call block->raiseModified(MOD_STATE_WRITE_NEEDED).
1733 Otherwise there would be a huge amount of unnecessary I/O.
1738 Convert objects that are not standing inside active blocks to static.
1740 If m_known_by_count != 0, active object is not deleted, but static
1741 data is still updated.
1743 If force_delete is set, active object is deleted nevertheless. It
1744 shall only be set so in the destructor of the environment.
1746 If block wasn't generated (not in memory or on disk),
1748 void ServerEnvironment::deactivateFarObjects(bool force_delete)
1750 std::vector<u16> objects_to_remove;
1751 for(std::map<u16, ServerActiveObject*>::iterator
1752 i = m_active_objects.begin();
1753 i != m_active_objects.end(); ++i) {
1754 ServerActiveObject* obj = i->second;
1757 // Do not deactivate if static data creation not allowed
1758 if(!force_delete && !obj->isStaticAllowed())
1761 // If pending deactivation, let removeRemovedObjects() do it
1762 if(!force_delete && obj->m_pending_deactivation)
1766 v3f objectpos = obj->getBasePosition();
1768 // The block in which the object resides in
1769 v3s16 blockpos_o = getNodeBlockPos(floatToInt(objectpos, BS));
1771 // If object's static data is stored in a deactivated block and object
1772 // is actually located in an active block, re-save to the block in
1773 // which the object is actually located in.
1775 obj->m_static_exists &&
1776 !m_active_blocks.contains(obj->m_static_block) &&
1777 m_active_blocks.contains(blockpos_o))
1779 v3s16 old_static_block = obj->m_static_block;
1781 // Save to block where object is located
1782 MapBlock *block = m_map->emergeBlock(blockpos_o, false);
1784 errorstream<<"ServerEnvironment::deactivateFarObjects(): "
1785 <<"Could not save object id="<<id
1786 <<" to it's current block "<<PP(blockpos_o)
1790 std::string staticdata_new = obj->getStaticData();
1791 StaticObject s_obj(obj->getType(), objectpos, staticdata_new);
1792 block->m_static_objects.insert(id, s_obj);
1793 obj->m_static_block = blockpos_o;
1794 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1795 "deactivateFarObjects: Static data moved in");
1797 // Delete from block where object was located
1798 block = m_map->emergeBlock(old_static_block, false);
1800 errorstream<<"ServerEnvironment::deactivateFarObjects(): "
1801 <<"Could not delete object id="<<id
1802 <<" from it's previous block "<<PP(old_static_block)
1806 block->m_static_objects.remove(id);
1807 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1808 "deactivateFarObjects: Static data moved out");
1812 // If block is active, don't remove
1813 if(!force_delete && m_active_blocks.contains(blockpos_o))
1816 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
1817 <<"deactivating object id="<<id<<" on inactive block "
1818 <<PP(blockpos_o)<<std::endl;
1820 // If known by some client, don't immediately delete.
1821 bool pending_delete = (obj->m_known_by_count > 0 && !force_delete);
1824 Update the static data
1827 if(obj->isStaticAllowed())
1829 // Create new static object
1830 std::string staticdata_new = obj->getStaticData();
1831 StaticObject s_obj(obj->getType(), objectpos, staticdata_new);
1833 bool stays_in_same_block = false;
1834 bool data_changed = true;
1836 if(obj->m_static_exists){
1837 if(obj->m_static_block == blockpos_o)
1838 stays_in_same_block = true;
1840 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
1842 std::map<u16, StaticObject>::iterator n =
1843 block->m_static_objects.m_active.find(id);
1844 if(n != block->m_static_objects.m_active.end()){
1845 StaticObject static_old = n->second;
1847 float save_movem = obj->getMinimumSavedMovement();
1849 if(static_old.data == staticdata_new &&
1850 (static_old.pos - objectpos).getLength() < save_movem)
1851 data_changed = false;
1853 errorstream<<"ServerEnvironment::deactivateFarObjects(): "
1854 <<"id="<<id<<" m_static_exists=true but "
1855 <<"static data doesn't actually exist in "
1856 <<PP(obj->m_static_block)<<std::endl;
1860 bool shall_be_written = (!stays_in_same_block || data_changed);
1862 // Delete old static object
1863 if(obj->m_static_exists)
1865 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
1868 block->m_static_objects.remove(id);
1869 obj->m_static_exists = false;
1870 // Only mark block as modified if data changed considerably
1871 if(shall_be_written)
1872 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1873 "deactivateFarObjects: Static data "
1874 "changed considerably");
1878 // Add to the block where the object is located in
1879 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1880 // Get or generate the block
1881 MapBlock *block = NULL;
1883 block = m_map->emergeBlock(blockpos);
1884 } catch(InvalidPositionException &e){
1885 // Handled via NULL pointer
1886 // NOTE: emergeBlock's failure is usually determined by it
1887 // actually returning NULL
1892 if(block->m_static_objects.m_stored.size() >= g_settings->getU16("max_objects_per_block")){
1893 errorstream<<"ServerEnv: Trying to store id="<<obj->getId()
1894 <<" statically but block "<<PP(blockpos)
1895 <<" already contains "
1896 <<block->m_static_objects.m_stored.size()
1898 <<" Forcing delete."<<std::endl;
1899 force_delete = true;
1901 // If static counterpart already exists in target block,
1903 // This shouldn't happen because the object is removed from
1904 // the previous block before this according to
1905 // obj->m_static_block, but happens rarely for some unknown
1906 // reason. Unsuccessful attempts have been made to find
1908 if(id && block->m_static_objects.m_active.find(id) != block->m_static_objects.m_active.end()){
1909 infostream<<"ServerEnv: WARNING: Performing hack #83274"
1911 block->m_static_objects.remove(id);
1913 // Store static data
1914 u16 store_id = pending_delete ? id : 0;
1915 block->m_static_objects.insert(store_id, s_obj);
1917 // Only mark block as modified if data changed considerably
1918 if(shall_be_written)
1919 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1920 "deactivateFarObjects: Static data "
1921 "changed considerably");
1923 obj->m_static_exists = true;
1924 obj->m_static_block = block->getPos();
1929 v3s16 p = floatToInt(objectpos, BS);
1930 errorstream<<"ServerEnv: Could not find or generate "
1931 <<"a block for storing id="<<obj->getId()
1932 <<" statically (pos="<<PP(p)<<")"<<std::endl;
1939 If known by some client, set pending deactivation.
1940 Otherwise delete it immediately.
1943 if(pending_delete && !force_delete)
1945 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
1946 <<"object id="<<id<<" is known by clients"
1947 <<"; not deleting yet"<<std::endl;
1949 obj->m_pending_deactivation = true;
1953 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
1954 <<"object id="<<id<<" is not known by clients"
1955 <<"; deleting"<<std::endl;
1957 // Tell the object about removal
1958 obj->removingFromEnvironment();
1959 // Deregister in scripting api
1960 m_script->removeObjectReference(obj);
1962 // Delete active object
1963 if(obj->environmentDeletes())
1965 // Id to be removed from m_active_objects
1966 objects_to_remove.push_back(id);
1969 // Remove references from m_active_objects
1970 for(std::vector<u16>::iterator i = objects_to_remove.begin();
1971 i != objects_to_remove.end(); ++i) {
1972 m_active_objects.erase(*i);
1979 #include "clientsimpleobject.h"
1985 ClientEnvironment::ClientEnvironment(ClientMap *map, scene::ISceneManager *smgr,
1986 ITextureSource *texturesource, IGameDef *gamedef,
1987 IrrlichtDevice *irr):
1990 m_texturesource(texturesource),
1995 memset(m_attachements, zero, sizeof(m_attachements));
1998 ClientEnvironment::~ClientEnvironment()
2000 // delete active objects
2001 for(std::map<u16, ClientActiveObject*>::iterator
2002 i = m_active_objects.begin();
2003 i != m_active_objects.end(); ++i)
2008 for(std::list<ClientSimpleObject*>::iterator
2009 i = m_simple_objects.begin(); i != m_simple_objects.end(); ++i)
2018 Map & ClientEnvironment::getMap()
2023 ClientMap & ClientEnvironment::getClientMap()
2028 void ClientEnvironment::addPlayer(Player *player)
2030 DSTACK(__FUNCTION_NAME);
2032 It is a failure if player is local and there already is a local
2035 assert(!(player->isLocal() == true && getLocalPlayer() != NULL));
2037 Environment::addPlayer(player);
2040 LocalPlayer * ClientEnvironment::getLocalPlayer()
2042 for(std::vector<Player*>::iterator i = m_players.begin();
2043 i != m_players.end(); ++i) {
2044 Player *player = *i;
2045 if(player->isLocal())
2046 return (LocalPlayer*)player;
2051 void ClientEnvironment::step(float dtime)
2053 DSTACK(__FUNCTION_NAME);
2055 /* Step time of day */
2056 stepTimeOfDay(dtime);
2058 // Get some settings
2059 bool fly_allowed = m_gamedef->checkLocalPrivilege("fly");
2060 bool free_move = fly_allowed && g_settings->getBool("free_move");
2063 LocalPlayer *lplayer = getLocalPlayer();
2065 // collision info queue
2066 std::list<CollisionInfo> player_collisions;
2069 Get the speed the player is going
2071 bool is_climbing = lplayer->is_climbing;
2073 f32 player_speed = lplayer->getSpeed().getLength();
2076 Maximum position increment
2078 //f32 position_max_increment = 0.05*BS;
2079 f32 position_max_increment = 0.1*BS;
2081 // Maximum time increment (for collision detection etc)
2082 // time = distance / speed
2083 f32 dtime_max_increment = 1;
2084 if(player_speed > 0.001)
2085 dtime_max_increment = position_max_increment / player_speed;
2087 // Maximum time increment is 10ms or lower
2088 if(dtime_max_increment > 0.01)
2089 dtime_max_increment = 0.01;
2091 // Don't allow overly huge dtime
2095 f32 dtime_downcount = dtime;
2098 Stuff that has a maximum time increment
2107 if(dtime_downcount > dtime_max_increment)
2109 dtime_part = dtime_max_increment;
2110 dtime_downcount -= dtime_part;
2114 dtime_part = dtime_downcount;
2116 Setting this to 0 (no -=dtime_part) disables an infinite loop
2117 when dtime_part is so small that dtime_downcount -= dtime_part
2120 dtime_downcount = 0;
2129 if(free_move == false && is_climbing == false)
2132 v3f speed = lplayer->getSpeed();
2133 if(lplayer->in_liquid == false)
2134 speed.Y -= lplayer->movement_gravity * lplayer->physics_override_gravity * dtime_part * 2;
2136 // Liquid floating / sinking
2137 if(lplayer->in_liquid && !lplayer->swimming_vertical)
2138 speed.Y -= lplayer->movement_liquid_sink * dtime_part * 2;
2140 // Liquid resistance
2141 if(lplayer->in_liquid_stable || lplayer->in_liquid)
2143 // How much the node's viscosity blocks movement, ranges between 0 and 1
2144 // Should match the scale at which viscosity increase affects other liquid attributes
2145 const f32 viscosity_factor = 0.3;
2147 v3f d_wanted = -speed / lplayer->movement_liquid_fluidity;
2148 f32 dl = d_wanted.getLength();
2149 if(dl > lplayer->movement_liquid_fluidity_smooth)
2150 dl = lplayer->movement_liquid_fluidity_smooth;
2151 dl *= (lplayer->liquid_viscosity * viscosity_factor) + (1 - viscosity_factor);
2153 v3f d = d_wanted.normalize() * dl;
2157 if(speed.X > lplayer->movement_liquid_fluidity + lplayer->movement_liquid_fluidity_smooth) speed.X -= lplayer->movement_liquid_fluidity_smooth;
2158 if(speed.X < -lplayer->movement_liquid_fluidity - lplayer->movement_liquid_fluidity_smooth) speed.X += lplayer->movement_liquid_fluidity_smooth;
2159 if(speed.Y > lplayer->movement_liquid_fluidity + lplayer->movement_liquid_fluidity_smooth) speed.Y -= lplayer->movement_liquid_fluidity_smooth;
2160 if(speed.Y < -lplayer->movement_liquid_fluidity - lplayer->movement_liquid_fluidity_smooth) speed.Y += lplayer->movement_liquid_fluidity_smooth;
2161 if(speed.Z > lplayer->movement_liquid_fluidity + lplayer->movement_liquid_fluidity_smooth) speed.Z -= lplayer->movement_liquid_fluidity_smooth;
2162 if(speed.Z < -lplayer->movement_liquid_fluidity - lplayer->movement_liquid_fluidity_smooth) speed.Z += lplayer->movement_liquid_fluidity_smooth;
2166 lplayer->setSpeed(speed);
2171 This also does collision detection.
2173 lplayer->move(dtime_part, this, position_max_increment,
2174 &player_collisions);
2177 while(dtime_downcount > 0.001);
2179 //std::cout<<"Looped "<<loopcount<<" times."<<std::endl;
2181 for(std::list<CollisionInfo>::iterator
2182 i = player_collisions.begin();
2183 i != player_collisions.end(); ++i)
2185 CollisionInfo &info = *i;
2186 v3f speed_diff = info.new_speed - info.old_speed;;
2187 // Handle only fall damage
2188 // (because otherwise walking against something in fast_move kills you)
2189 if(speed_diff.Y < 0 || info.old_speed.Y >= 0)
2191 // Get rid of other components
2194 f32 pre_factor = 1; // 1 hp per node/s
2195 f32 tolerance = BS*14; // 5 without damage
2196 f32 post_factor = 1; // 1 hp per node/s
2197 if(info.type == COLLISION_NODE)
2199 const ContentFeatures &f = m_gamedef->ndef()->
2200 get(m_map->getNodeNoEx(info.node_p));
2201 // Determine fall damage multiplier
2202 int addp = itemgroup_get(f.groups, "fall_damage_add_percent");
2203 pre_factor = 1.0 + (float)addp/100.0;
2205 float speed = pre_factor * speed_diff.getLength();
2206 if(speed > tolerance)
2208 f32 damage_f = (speed - tolerance)/BS * post_factor;
2209 u16 damage = (u16)(damage_f+0.5);
2211 damageLocalPlayer(damage, true);
2212 MtEvent *e = new SimpleTriggerEvent("PlayerFallingDamage");
2213 m_gamedef->event()->put(e);
2219 A quick draft of lava damage
2221 if(m_lava_hurt_interval.step(dtime, 1.0))
2223 v3f pf = lplayer->getPosition();
2225 // Feet, middle and head
2226 v3s16 p1 = floatToInt(pf + v3f(0, BS*0.1, 0), BS);
2227 MapNode n1 = m_map->getNodeNoEx(p1);
2228 v3s16 p2 = floatToInt(pf + v3f(0, BS*0.8, 0), BS);
2229 MapNode n2 = m_map->getNodeNoEx(p2);
2230 v3s16 p3 = floatToInt(pf + v3f(0, BS*1.6, 0), BS);
2231 MapNode n3 = m_map->getNodeNoEx(p3);
2233 u32 damage_per_second = 0;
2234 damage_per_second = MYMAX(damage_per_second,
2235 m_gamedef->ndef()->get(n1).damage_per_second);
2236 damage_per_second = MYMAX(damage_per_second,
2237 m_gamedef->ndef()->get(n2).damage_per_second);
2238 damage_per_second = MYMAX(damage_per_second,
2239 m_gamedef->ndef()->get(n3).damage_per_second);
2241 if(damage_per_second != 0)
2243 damageLocalPlayer(damage_per_second, true);
2250 if(m_drowning_interval.step(dtime, 2.0))
2252 v3f pf = lplayer->getPosition();
2255 v3s16 p = floatToInt(pf + v3f(0, BS*1.6, 0), BS);
2256 MapNode n = m_map->getNodeNoEx(p);
2257 ContentFeatures c = m_gamedef->ndef()->get(n);
2258 u8 drowning_damage = c.drowning;
2259 if(drowning_damage > 0 && lplayer->hp > 0){
2260 u16 breath = lplayer->getBreath();
2267 lplayer->setBreath(breath);
2268 updateLocalPlayerBreath(breath);
2271 if(lplayer->getBreath() == 0 && drowning_damage > 0){
2272 damageLocalPlayer(drowning_damage, true);
2275 if(m_breathing_interval.step(dtime, 0.5))
2277 v3f pf = lplayer->getPosition();
2280 v3s16 p = floatToInt(pf + v3f(0, BS*1.6, 0), BS);
2281 MapNode n = m_map->getNodeNoEx(p);
2282 ContentFeatures c = m_gamedef->ndef()->get(n);
2284 lplayer->setBreath(11);
2286 else if(c.drowning == 0){
2287 u16 breath = lplayer->getBreath();
2290 lplayer->setBreath(breath);
2291 updateLocalPlayerBreath(breath);
2297 Stuff that can be done in an arbitarily large dtime
2299 for(std::vector<Player*>::iterator i = m_players.begin();
2300 i != m_players.end(); ++i) {
2301 Player *player = *i;
2304 Handle non-local players
2306 if(player->isLocal() == false) {
2308 player->move(dtime, this, 100*BS);
2313 // Update lighting on local player (used for wield item)
2314 u32 day_night_ratio = getDayNightRatio();
2318 // On InvalidPositionException, use this as default
2319 // (day: LIGHT_SUN, night: 0)
2320 MapNode node_at_lplayer(CONTENT_AIR, 0x0f, 0);
2322 v3s16 p = lplayer->getLightPosition();
2323 node_at_lplayer = m_map->getNodeNoEx(p);
2325 u16 light = getInteriorLight(node_at_lplayer, 0, m_gamedef->ndef());
2326 u8 day = light & 0xff;
2327 u8 night = (light >> 8) & 0xff;
2328 finalColorBlend(lplayer->light_color, day, night, day_night_ratio);
2332 Step active objects and update lighting of them
2335 g_profiler->avg("CEnv: num of objects", m_active_objects.size());
2336 bool update_lighting = m_active_object_light_update_interval.step(dtime, 0.21);
2337 for(std::map<u16, ClientActiveObject*>::iterator
2338 i = m_active_objects.begin();
2339 i != m_active_objects.end(); ++i)
2341 ClientActiveObject* obj = i->second;
2343 obj->step(dtime, this);
2352 v3s16 p = obj->getLightPosition();
2353 MapNode n = m_map->getNodeNoEx(p, &pos_ok);
2355 light = n.getLightBlend(day_night_ratio, m_gamedef->ndef());
2357 light = blend_light(day_night_ratio, LIGHT_SUN, 0);
2359 obj->updateLight(light);
2364 Step and handle simple objects
2366 g_profiler->avg("CEnv: num of simple objects", m_simple_objects.size());
2367 for(std::list<ClientSimpleObject*>::iterator
2368 i = m_simple_objects.begin(); i != m_simple_objects.end();)
2370 ClientSimpleObject *simple = *i;
2371 std::list<ClientSimpleObject*>::iterator cur = i;
2373 simple->step(dtime);
2374 if(simple->m_to_be_removed){
2376 m_simple_objects.erase(cur);
2381 void ClientEnvironment::addSimpleObject(ClientSimpleObject *simple)
2383 m_simple_objects.push_back(simple);
2386 ClientActiveObject* ClientEnvironment::getActiveObject(u16 id)
2388 std::map<u16, ClientActiveObject*>::iterator n;
2389 n = m_active_objects.find(id);
2390 if(n == m_active_objects.end())
2395 bool isFreeClientActiveObjectId(u16 id,
2396 std::map<u16, ClientActiveObject*> &objects)
2401 return objects.find(id) == objects.end();
2404 u16 getFreeClientActiveObjectId(
2405 std::map<u16, ClientActiveObject*> &objects)
2407 //try to reuse id's as late as possible
2408 static u16 last_used_id = 0;
2409 u16 startid = last_used_id;
2413 if(isFreeClientActiveObjectId(last_used_id, objects))
2414 return last_used_id;
2416 if(last_used_id == startid)
2421 u16 ClientEnvironment::addActiveObject(ClientActiveObject *object)
2424 if(object->getId() == 0)
2426 u16 new_id = getFreeClientActiveObjectId(m_active_objects);
2429 infostream<<"ClientEnvironment::addActiveObject(): "
2430 <<"no free ids available"<<std::endl;
2434 object->setId(new_id);
2436 if(isFreeClientActiveObjectId(object->getId(), m_active_objects) == false)
2438 infostream<<"ClientEnvironment::addActiveObject(): "
2439 <<"id is not free ("<<object->getId()<<")"<<std::endl;
2443 infostream<<"ClientEnvironment::addActiveObject(): "
2444 <<"added (id="<<object->getId()<<")"<<std::endl;
2445 m_active_objects[object->getId()] = object;
2446 object->addToScene(m_smgr, m_texturesource, m_irr);
2447 { // Update lighting immediately
2452 v3s16 p = object->getLightPosition();
2453 MapNode n = m_map->getNodeNoEx(p, &pos_ok);
2455 light = n.getLightBlend(getDayNightRatio(), m_gamedef->ndef());
2457 light = blend_light(getDayNightRatio(), LIGHT_SUN, 0);
2459 object->updateLight(light);
2461 return object->getId();
2464 void ClientEnvironment::addActiveObject(u16 id, u8 type,
2465 const std::string &init_data)
2467 ClientActiveObject* obj =
2468 ClientActiveObject::create((ActiveObjectType) type, m_gamedef, this);
2471 infostream<<"ClientEnvironment::addActiveObject(): "
2472 <<"id="<<id<<" type="<<type<<": Couldn't create object"
2481 obj->initialize(init_data);
2483 catch(SerializationError &e)
2485 errorstream<<"ClientEnvironment::addActiveObject():"
2486 <<" id="<<id<<" type="<<type
2487 <<": SerializationError in initialize(): "
2489 <<": init_data="<<serializeJsonString(init_data)
2493 addActiveObject(obj);
2496 void ClientEnvironment::removeActiveObject(u16 id)
2498 verbosestream<<"ClientEnvironment::removeActiveObject(): "
2499 <<"id="<<id<<std::endl;
2500 ClientActiveObject* obj = getActiveObject(id);
2503 infostream<<"ClientEnvironment::removeActiveObject(): "
2504 <<"id="<<id<<" not found"<<std::endl;
2507 obj->removeFromScene(true);
2509 m_active_objects.erase(id);
2512 void ClientEnvironment::processActiveObjectMessage(u16 id,
2513 const std::string &data)
2515 ClientActiveObject* obj = getActiveObject(id);
2518 infostream<<"ClientEnvironment::processActiveObjectMessage():"
2519 <<" got message for id="<<id<<", which doesn't exist."
2525 obj->processMessage(data);
2527 catch(SerializationError &e)
2529 errorstream<<"ClientEnvironment::processActiveObjectMessage():"
2530 <<" id="<<id<<" type="<<obj->getType()
2531 <<" SerializationError in processMessage(),"
2532 <<" message="<<serializeJsonString(data)
2538 Callbacks for activeobjects
2541 void ClientEnvironment::damageLocalPlayer(u8 damage, bool handle_hp)
2543 LocalPlayer *lplayer = getLocalPlayer();
2547 if (lplayer->hp == 0) // Don't damage a dead player
2549 if(lplayer->hp > damage)
2550 lplayer->hp -= damage;
2555 ClientEnvEvent event;
2556 event.type = CEE_PLAYER_DAMAGE;
2557 event.player_damage.amount = damage;
2558 event.player_damage.send_to_server = handle_hp;
2559 m_client_event_queue.push_back(event);
2562 void ClientEnvironment::updateLocalPlayerBreath(u16 breath)
2564 ClientEnvEvent event;
2565 event.type = CEE_PLAYER_BREATH;
2566 event.player_breath.amount = breath;
2567 m_client_event_queue.push_back(event);
2571 Client likes to call these
2574 void ClientEnvironment::getActiveObjects(v3f origin, f32 max_d,
2575 std::vector<DistanceSortedActiveObject> &dest)
2577 for(std::map<u16, ClientActiveObject*>::iterator
2578 i = m_active_objects.begin();
2579 i != m_active_objects.end(); ++i)
2581 ClientActiveObject* obj = i->second;
2583 f32 d = (obj->getPosition() - origin).getLength();
2588 DistanceSortedActiveObject dso(obj, d);
2590 dest.push_back(dso);
2594 ClientEnvEvent ClientEnvironment::getClientEvent()
2596 ClientEnvEvent event;
2597 if(m_client_event_queue.empty())
2598 event.type = CEE_NONE;
2600 event = m_client_event_queue.front();
2601 m_client_event_queue.pop_front();
2606 #endif // #ifndef SERVER