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.pop_front());
1248 Manage active objects
1250 if(m_object_management_interval.step(dtime, 0.5))
1252 ScopeProfiler sp(g_profiler, "SEnv: remove removed objs avg /.5s", SPT_AVG);
1254 Remove objects that satisfy (m_removed && m_known_by_count==0)
1256 removeRemovedObjects();
1260 ServerActiveObject* ServerEnvironment::getActiveObject(u16 id)
1262 std::map<u16, ServerActiveObject*>::iterator n;
1263 n = m_active_objects.find(id);
1264 if(n == m_active_objects.end())
1269 bool isFreeServerActiveObjectId(u16 id,
1270 std::map<u16, ServerActiveObject*> &objects)
1275 return objects.find(id) == objects.end();
1278 u16 getFreeServerActiveObjectId(
1279 std::map<u16, ServerActiveObject*> &objects)
1281 //try to reuse id's as late as possible
1282 static u16 last_used_id = 0;
1283 u16 startid = last_used_id;
1287 if(isFreeServerActiveObjectId(last_used_id, objects))
1288 return last_used_id;
1290 if(last_used_id == startid)
1295 u16 ServerEnvironment::addActiveObject(ServerActiveObject *object)
1299 u16 id = addActiveObjectRaw(object, true, 0);
1304 bool ServerEnvironment::addActiveObjectAsStatic(ServerActiveObject *obj)
1308 v3f objectpos = obj->getBasePosition();
1310 // The block in which the object resides in
1311 v3s16 blockpos_o = getNodeBlockPos(floatToInt(objectpos, BS));
1314 Update the static data
1317 // Create new static object
1318 std::string staticdata = obj->getStaticData();
1319 StaticObject s_obj(obj->getType(), objectpos, staticdata);
1320 // Add to the block where the object is located in
1321 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1322 // Get or generate the block
1323 MapBlock *block = m_map->emergeBlock(blockpos);
1325 bool succeeded = false;
1329 block->m_static_objects.insert(0, s_obj);
1330 block->raiseModified(MOD_STATE_WRITE_AT_UNLOAD,
1331 "addActiveObjectAsStatic");
1335 infostream<<"ServerEnvironment::addActiveObjectAsStatic: "
1336 <<"Could not find or generate "
1337 <<"a block for storing static object"<<std::endl;
1341 if(obj->environmentDeletes())
1349 Finds out what new objects have been added to
1350 inside a radius around a position
1352 void ServerEnvironment::getAddedActiveObjects(v3s16 pos, s16 radius,
1354 std::set<u16> ¤t_objects,
1355 std::set<u16> &added_objects)
1357 v3f pos_f = intToFloat(pos, BS);
1358 f32 radius_f = radius * BS;
1359 f32 player_radius_f = player_radius * BS;
1361 if (player_radius_f < 0)
1362 player_radius_f = 0;
1365 Go through the object list,
1366 - discard m_removed objects,
1367 - discard objects that are too far away,
1368 - discard objects that are found in current_objects.
1369 - add remaining objects to added_objects
1371 for(std::map<u16, ServerActiveObject*>::iterator
1372 i = m_active_objects.begin();
1373 i != m_active_objects.end(); ++i)
1377 ServerActiveObject *object = i->second;
1380 // Discard if removed or deactivating
1381 if(object->m_removed || object->m_pending_deactivation)
1384 f32 distance_f = object->getBasePosition().getDistanceFrom(pos_f);
1385 if (object->getType() == ACTIVEOBJECT_TYPE_PLAYER) {
1386 // Discard if too far
1387 if (distance_f > player_radius_f && player_radius_f != 0)
1389 } else if (distance_f > radius_f)
1392 // Discard if already on current_objects
1393 std::set<u16>::iterator n;
1394 n = current_objects.find(id);
1395 if(n != current_objects.end())
1397 // Add to added_objects
1398 added_objects.insert(id);
1403 Finds out what objects have been removed from
1404 inside a radius around a position
1406 void ServerEnvironment::getRemovedActiveObjects(v3s16 pos, s16 radius,
1408 std::set<u16> ¤t_objects,
1409 std::set<u16> &removed_objects)
1411 v3f pos_f = intToFloat(pos, BS);
1412 f32 radius_f = radius * BS;
1413 f32 player_radius_f = player_radius * BS;
1415 if (player_radius_f < 0)
1416 player_radius_f = 0;
1419 Go through current_objects; object is removed if:
1420 - object is not found in m_active_objects (this is actually an
1421 error condition; objects should be set m_removed=true and removed
1422 only after all clients have been informed about removal), or
1423 - object has m_removed=true, or
1424 - object is too far away
1426 for(std::set<u16>::iterator
1427 i = current_objects.begin();
1428 i != current_objects.end(); ++i)
1431 ServerActiveObject *object = getActiveObject(id);
1434 infostream<<"ServerEnvironment::getRemovedActiveObjects():"
1435 <<" object in current_objects is NULL"<<std::endl;
1436 removed_objects.insert(id);
1440 if(object->m_removed || object->m_pending_deactivation)
1442 removed_objects.insert(id);
1446 f32 distance_f = object->getBasePosition().getDistanceFrom(pos_f);
1447 if (object->getType() == ACTIVEOBJECT_TYPE_PLAYER) {
1448 if (distance_f <= player_radius_f || player_radius_f == 0)
1450 } else if (distance_f <= radius_f)
1453 // Object is no longer visible
1454 removed_objects.insert(id);
1458 ActiveObjectMessage ServerEnvironment::getActiveObjectMessage()
1460 if(m_active_object_messages.empty())
1461 return ActiveObjectMessage(0);
1463 ActiveObjectMessage message = m_active_object_messages.front();
1464 m_active_object_messages.pop_front();
1469 ************ Private methods *************
1472 u16 ServerEnvironment::addActiveObjectRaw(ServerActiveObject *object,
1473 bool set_changed, u32 dtime_s)
1476 if(object->getId() == 0){
1477 u16 new_id = getFreeServerActiveObjectId(m_active_objects);
1480 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1481 <<"no free ids available"<<std::endl;
1482 if(object->environmentDeletes())
1486 object->setId(new_id);
1489 verbosestream<<"ServerEnvironment::addActiveObjectRaw(): "
1490 <<"supplied with id "<<object->getId()<<std::endl;
1492 if(isFreeServerActiveObjectId(object->getId(), m_active_objects) == false)
1494 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1495 <<"id is not free ("<<object->getId()<<")"<<std::endl;
1496 if(object->environmentDeletes())
1500 /*infostream<<"ServerEnvironment::addActiveObjectRaw(): "
1501 <<"added (id="<<object->getId()<<")"<<std::endl;*/
1503 m_active_objects[object->getId()] = object;
1505 verbosestream<<"ServerEnvironment::addActiveObjectRaw(): "
1506 <<"Added id="<<object->getId()<<"; there are now "
1507 <<m_active_objects.size()<<" active objects."
1510 // Register reference in scripting api (must be done before post-init)
1511 m_script->addObjectReference(object);
1512 // Post-initialize object
1513 object->addedToEnvironment(dtime_s);
1515 // Add static data to block
1516 if(object->isStaticAllowed())
1518 // Add static object to active static list of the block
1519 v3f objectpos = object->getBasePosition();
1520 std::string staticdata = object->getStaticData();
1521 StaticObject s_obj(object->getType(), objectpos, staticdata);
1522 // Add to the block where the object is located in
1523 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1524 MapBlock *block = m_map->emergeBlock(blockpos);
1526 block->m_static_objects.m_active[object->getId()] = s_obj;
1527 object->m_static_exists = true;
1528 object->m_static_block = blockpos;
1531 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1532 "addActiveObjectRaw");
1534 v3s16 p = floatToInt(objectpos, BS);
1535 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1536 <<"could not emerge block for storing id="<<object->getId()
1537 <<" statically (pos="<<PP(p)<<")"<<std::endl;
1541 return object->getId();
1545 Remove objects that satisfy (m_removed && m_known_by_count==0)
1547 void ServerEnvironment::removeRemovedObjects()
1549 std::vector<u16> objects_to_remove;
1550 for(std::map<u16, ServerActiveObject*>::iterator
1551 i = m_active_objects.begin();
1552 i != m_active_objects.end(); ++i) {
1554 ServerActiveObject* obj = i->second;
1555 // This shouldn't happen but check it
1558 infostream<<"NULL object found in ServerEnvironment"
1559 <<" while finding removed objects. id="<<id<<std::endl;
1560 // Id to be removed from m_active_objects
1561 objects_to_remove.push_back(id);
1566 We will delete objects that are marked as removed or thatare
1567 waiting for deletion after deactivation
1569 if(obj->m_removed == false && obj->m_pending_deactivation == false)
1573 Delete static data from block if is marked as removed
1575 if(obj->m_static_exists && obj->m_removed)
1577 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
1579 block->m_static_objects.remove(id);
1580 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1581 "removeRemovedObjects/remove");
1582 obj->m_static_exists = false;
1584 infostream<<"Failed to emerge block from which an object to "
1585 <<"be removed was loaded from. id="<<id<<std::endl;
1589 // If m_known_by_count > 0, don't actually remove. On some future
1590 // invocation this will be 0, which is when removal will continue.
1591 if(obj->m_known_by_count > 0)
1595 Move static data from active to stored if not marked as removed
1597 if(obj->m_static_exists && !obj->m_removed){
1598 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
1600 std::map<u16, StaticObject>::iterator i =
1601 block->m_static_objects.m_active.find(id);
1602 if(i != block->m_static_objects.m_active.end()){
1603 block->m_static_objects.m_stored.push_back(i->second);
1604 block->m_static_objects.m_active.erase(id);
1605 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1606 "removeRemovedObjects/deactivate");
1609 infostream<<"Failed to emerge block from which an object to "
1610 <<"be deactivated was loaded from. id="<<id<<std::endl;
1614 // Tell the object about removal
1615 obj->removingFromEnvironment();
1616 // Deregister in scripting api
1617 m_script->removeObjectReference(obj);
1620 if(obj->environmentDeletes())
1623 // Id to be removed from m_active_objects
1624 objects_to_remove.push_back(id);
1626 // Remove references from m_active_objects
1627 for(std::vector<u16>::iterator i = objects_to_remove.begin();
1628 i != objects_to_remove.end(); ++i) {
1629 m_active_objects.erase(*i);
1633 static void print_hexdump(std::ostream &o, const std::string &data)
1635 const int linelength = 16;
1636 for(int l=0; ; l++){
1637 int i0 = linelength * l;
1638 bool at_end = false;
1639 int thislinelength = linelength;
1640 if(i0 + thislinelength > (int)data.size()){
1641 thislinelength = data.size() - i0;
1644 for(int di=0; di<linelength; di++){
1647 if(di<thislinelength)
1648 snprintf(buf, 4, "%.2x ", data[i]);
1650 snprintf(buf, 4, " ");
1654 for(int di=0; di<thislinelength; di++){
1668 Convert stored objects from blocks near the players to active.
1670 void ServerEnvironment::activateObjects(MapBlock *block, u32 dtime_s)
1675 // Ignore if no stored objects (to not set changed flag)
1676 if(block->m_static_objects.m_stored.empty())
1678 verbosestream<<"ServerEnvironment::activateObjects(): "
1679 <<"activating objects of block "<<PP(block->getPos())
1680 <<" ("<<block->m_static_objects.m_stored.size()
1681 <<" objects)"<<std::endl;
1682 bool large_amount = (block->m_static_objects.m_stored.size() > g_settings->getU16("max_objects_per_block"));
1684 errorstream<<"suspiciously large amount of objects detected: "
1685 <<block->m_static_objects.m_stored.size()<<" in "
1686 <<PP(block->getPos())
1687 <<"; removing all of them."<<std::endl;
1688 // Clear stored list
1689 block->m_static_objects.m_stored.clear();
1690 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1691 "stored list cleared in activateObjects due to "
1692 "large amount of objects");
1696 // Activate stored objects
1697 std::vector<StaticObject> new_stored;
1698 for(std::list<StaticObject>::iterator
1699 i = block->m_static_objects.m_stored.begin();
1700 i != block->m_static_objects.m_stored.end(); ++i) {
1701 StaticObject &s_obj = *i;
1703 // Create an active object from the data
1704 ServerActiveObject *obj = ServerActiveObject::create
1705 ((ActiveObjectType) s_obj.type, this, 0, s_obj.pos, s_obj.data);
1706 // If couldn't create object, store static data back.
1708 errorstream<<"ServerEnvironment::activateObjects(): "
1709 <<"failed to create active object from static object "
1710 <<"in block "<<PP(s_obj.pos/BS)
1711 <<" type="<<(int)s_obj.type<<" data:"<<std::endl;
1712 print_hexdump(verbosestream, s_obj.data);
1714 new_stored.push_back(s_obj);
1717 verbosestream<<"ServerEnvironment::activateObjects(): "
1718 <<"activated static object pos="<<PP(s_obj.pos/BS)
1719 <<" type="<<(int)s_obj.type<<std::endl;
1720 // This will also add the object to the active static list
1721 addActiveObjectRaw(obj, false, dtime_s);
1723 // Clear stored list
1724 block->m_static_objects.m_stored.clear();
1725 // Add leftover failed stuff to stored list
1726 for(std::vector<StaticObject>::iterator
1727 i = new_stored.begin();
1728 i != new_stored.end(); ++i) {
1729 StaticObject &s_obj = *i;
1730 block->m_static_objects.m_stored.push_back(s_obj);
1733 // Turn the active counterparts of activated objects not pending for
1735 for(std::map<u16, StaticObject>::iterator
1736 i = block->m_static_objects.m_active.begin();
1737 i != block->m_static_objects.m_active.end(); ++i)
1740 ServerActiveObject *object = getActiveObject(id);
1742 object->m_pending_deactivation = false;
1746 Note: Block hasn't really been modified here.
1747 The objects have just been activated and moved from the stored
1748 static list to the active static list.
1749 As such, the block is essentially the same.
1750 Thus, do not call block->raiseModified(MOD_STATE_WRITE_NEEDED).
1751 Otherwise there would be a huge amount of unnecessary I/O.
1756 Convert objects that are not standing inside active blocks to static.
1758 If m_known_by_count != 0, active object is not deleted, but static
1759 data is still updated.
1761 If force_delete is set, active object is deleted nevertheless. It
1762 shall only be set so in the destructor of the environment.
1764 If block wasn't generated (not in memory or on disk),
1766 void ServerEnvironment::deactivateFarObjects(bool force_delete)
1768 std::vector<u16> objects_to_remove;
1769 for(std::map<u16, ServerActiveObject*>::iterator
1770 i = m_active_objects.begin();
1771 i != m_active_objects.end(); ++i) {
1772 ServerActiveObject* obj = i->second;
1775 // Do not deactivate if static data creation not allowed
1776 if(!force_delete && !obj->isStaticAllowed())
1779 // If pending deactivation, let removeRemovedObjects() do it
1780 if(!force_delete && obj->m_pending_deactivation)
1784 v3f objectpos = obj->getBasePosition();
1786 // The block in which the object resides in
1787 v3s16 blockpos_o = getNodeBlockPos(floatToInt(objectpos, BS));
1789 // If object's static data is stored in a deactivated block and object
1790 // is actually located in an active block, re-save to the block in
1791 // which the object is actually located in.
1793 obj->m_static_exists &&
1794 !m_active_blocks.contains(obj->m_static_block) &&
1795 m_active_blocks.contains(blockpos_o))
1797 v3s16 old_static_block = obj->m_static_block;
1799 // Save to block where object is located
1800 MapBlock *block = m_map->emergeBlock(blockpos_o, false);
1802 errorstream<<"ServerEnvironment::deactivateFarObjects(): "
1803 <<"Could not save object id="<<id
1804 <<" to it's current block "<<PP(blockpos_o)
1808 std::string staticdata_new = obj->getStaticData();
1809 StaticObject s_obj(obj->getType(), objectpos, staticdata_new);
1810 block->m_static_objects.insert(id, s_obj);
1811 obj->m_static_block = blockpos_o;
1812 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1813 "deactivateFarObjects: Static data moved in");
1815 // Delete from block where object was located
1816 block = m_map->emergeBlock(old_static_block, false);
1818 errorstream<<"ServerEnvironment::deactivateFarObjects(): "
1819 <<"Could not delete object id="<<id
1820 <<" from it's previous block "<<PP(old_static_block)
1824 block->m_static_objects.remove(id);
1825 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1826 "deactivateFarObjects: Static data moved out");
1830 // If block is active, don't remove
1831 if(!force_delete && m_active_blocks.contains(blockpos_o))
1834 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
1835 <<"deactivating object id="<<id<<" on inactive block "
1836 <<PP(blockpos_o)<<std::endl;
1838 // If known by some client, don't immediately delete.
1839 bool pending_delete = (obj->m_known_by_count > 0 && !force_delete);
1842 Update the static data
1845 if(obj->isStaticAllowed())
1847 // Create new static object
1848 std::string staticdata_new = obj->getStaticData();
1849 StaticObject s_obj(obj->getType(), objectpos, staticdata_new);
1851 bool stays_in_same_block = false;
1852 bool data_changed = true;
1854 if(obj->m_static_exists){
1855 if(obj->m_static_block == blockpos_o)
1856 stays_in_same_block = true;
1858 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
1860 std::map<u16, StaticObject>::iterator n =
1861 block->m_static_objects.m_active.find(id);
1862 if(n != block->m_static_objects.m_active.end()){
1863 StaticObject static_old = n->second;
1865 float save_movem = obj->getMinimumSavedMovement();
1867 if(static_old.data == staticdata_new &&
1868 (static_old.pos - objectpos).getLength() < save_movem)
1869 data_changed = false;
1871 errorstream<<"ServerEnvironment::deactivateFarObjects(): "
1872 <<"id="<<id<<" m_static_exists=true but "
1873 <<"static data doesn't actually exist in "
1874 <<PP(obj->m_static_block)<<std::endl;
1878 bool shall_be_written = (!stays_in_same_block || data_changed);
1880 // Delete old static object
1881 if(obj->m_static_exists)
1883 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
1886 block->m_static_objects.remove(id);
1887 obj->m_static_exists = false;
1888 // Only mark block as modified if data changed considerably
1889 if(shall_be_written)
1890 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1891 "deactivateFarObjects: Static data "
1892 "changed considerably");
1896 // Add to the block where the object is located in
1897 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1898 // Get or generate the block
1899 MapBlock *block = NULL;
1901 block = m_map->emergeBlock(blockpos);
1902 } catch(InvalidPositionException &e){
1903 // Handled via NULL pointer
1904 // NOTE: emergeBlock's failure is usually determined by it
1905 // actually returning NULL
1910 if(block->m_static_objects.m_stored.size() >= g_settings->getU16("max_objects_per_block")){
1911 errorstream<<"ServerEnv: Trying to store id="<<obj->getId()
1912 <<" statically but block "<<PP(blockpos)
1913 <<" already contains "
1914 <<block->m_static_objects.m_stored.size()
1916 <<" Forcing delete."<<std::endl;
1917 force_delete = true;
1919 // If static counterpart already exists in target block,
1921 // This shouldn't happen because the object is removed from
1922 // the previous block before this according to
1923 // obj->m_static_block, but happens rarely for some unknown
1924 // reason. Unsuccessful attempts have been made to find
1926 if(id && block->m_static_objects.m_active.find(id) != block->m_static_objects.m_active.end()){
1927 infostream<<"ServerEnv: WARNING: Performing hack #83274"
1929 block->m_static_objects.remove(id);
1931 // Store static data
1932 u16 store_id = pending_delete ? id : 0;
1933 block->m_static_objects.insert(store_id, s_obj);
1935 // Only mark block as modified if data changed considerably
1936 if(shall_be_written)
1937 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1938 "deactivateFarObjects: Static data "
1939 "changed considerably");
1941 obj->m_static_exists = true;
1942 obj->m_static_block = block->getPos();
1947 v3s16 p = floatToInt(objectpos, BS);
1948 errorstream<<"ServerEnv: Could not find or generate "
1949 <<"a block for storing id="<<obj->getId()
1950 <<" statically (pos="<<PP(p)<<")"<<std::endl;
1957 If known by some client, set pending deactivation.
1958 Otherwise delete it immediately.
1961 if(pending_delete && !force_delete)
1963 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
1964 <<"object id="<<id<<" is known by clients"
1965 <<"; not deleting yet"<<std::endl;
1967 obj->m_pending_deactivation = true;
1971 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
1972 <<"object id="<<id<<" is not known by clients"
1973 <<"; deleting"<<std::endl;
1975 // Tell the object about removal
1976 obj->removingFromEnvironment();
1977 // Deregister in scripting api
1978 m_script->removeObjectReference(obj);
1980 // Delete active object
1981 if(obj->environmentDeletes())
1983 // Id to be removed from m_active_objects
1984 objects_to_remove.push_back(id);
1987 // Remove references from m_active_objects
1988 for(std::vector<u16>::iterator i = objects_to_remove.begin();
1989 i != objects_to_remove.end(); ++i) {
1990 m_active_objects.erase(*i);
1997 #include "clientsimpleobject.h"
2003 ClientEnvironment::ClientEnvironment(ClientMap *map, scene::ISceneManager *smgr,
2004 ITextureSource *texturesource, IGameDef *gamedef,
2005 IrrlichtDevice *irr):
2008 m_texturesource(texturesource),
2013 memset(m_attachements, zero, sizeof(m_attachements));
2016 ClientEnvironment::~ClientEnvironment()
2018 // delete active objects
2019 for(std::map<u16, ClientActiveObject*>::iterator
2020 i = m_active_objects.begin();
2021 i != m_active_objects.end(); ++i)
2026 for(std::list<ClientSimpleObject*>::iterator
2027 i = m_simple_objects.begin(); i != m_simple_objects.end(); ++i)
2036 Map & ClientEnvironment::getMap()
2041 ClientMap & ClientEnvironment::getClientMap()
2046 void ClientEnvironment::addPlayer(Player *player)
2048 DSTACK(__FUNCTION_NAME);
2050 It is a failure if player is local and there already is a local
2053 assert(!(player->isLocal() == true && getLocalPlayer() != NULL));
2055 Environment::addPlayer(player);
2058 LocalPlayer * ClientEnvironment::getLocalPlayer()
2060 for(std::vector<Player*>::iterator i = m_players.begin();
2061 i != m_players.end(); ++i) {
2062 Player *player = *i;
2063 if(player->isLocal())
2064 return (LocalPlayer*)player;
2069 void ClientEnvironment::step(float dtime)
2071 DSTACK(__FUNCTION_NAME);
2073 /* Step time of day */
2074 stepTimeOfDay(dtime);
2076 // Get some settings
2077 bool fly_allowed = m_gamedef->checkLocalPrivilege("fly");
2078 bool free_move = fly_allowed && g_settings->getBool("free_move");
2081 LocalPlayer *lplayer = getLocalPlayer();
2083 // collision info queue
2084 std::vector<CollisionInfo> player_collisions;
2087 Get the speed the player is going
2089 bool is_climbing = lplayer->is_climbing;
2091 f32 player_speed = lplayer->getSpeed().getLength();
2094 Maximum position increment
2096 //f32 position_max_increment = 0.05*BS;
2097 f32 position_max_increment = 0.1*BS;
2099 // Maximum time increment (for collision detection etc)
2100 // time = distance / speed
2101 f32 dtime_max_increment = 1;
2102 if(player_speed > 0.001)
2103 dtime_max_increment = position_max_increment / player_speed;
2105 // Maximum time increment is 10ms or lower
2106 if(dtime_max_increment > 0.01)
2107 dtime_max_increment = 0.01;
2109 // Don't allow overly huge dtime
2113 f32 dtime_downcount = dtime;
2116 Stuff that has a maximum time increment
2125 if(dtime_downcount > dtime_max_increment)
2127 dtime_part = dtime_max_increment;
2128 dtime_downcount -= dtime_part;
2132 dtime_part = dtime_downcount;
2134 Setting this to 0 (no -=dtime_part) disables an infinite loop
2135 when dtime_part is so small that dtime_downcount -= dtime_part
2138 dtime_downcount = 0;
2147 if(free_move == false && is_climbing == false)
2150 v3f speed = lplayer->getSpeed();
2151 if(lplayer->in_liquid == false)
2152 speed.Y -= lplayer->movement_gravity * lplayer->physics_override_gravity * dtime_part * 2;
2154 // Liquid floating / sinking
2155 if(lplayer->in_liquid && !lplayer->swimming_vertical)
2156 speed.Y -= lplayer->movement_liquid_sink * dtime_part * 2;
2158 // Liquid resistance
2159 if(lplayer->in_liquid_stable || lplayer->in_liquid)
2161 // How much the node's viscosity blocks movement, ranges between 0 and 1
2162 // Should match the scale at which viscosity increase affects other liquid attributes
2163 const f32 viscosity_factor = 0.3;
2165 v3f d_wanted = -speed / lplayer->movement_liquid_fluidity;
2166 f32 dl = d_wanted.getLength();
2167 if(dl > lplayer->movement_liquid_fluidity_smooth)
2168 dl = lplayer->movement_liquid_fluidity_smooth;
2169 dl *= (lplayer->liquid_viscosity * viscosity_factor) + (1 - viscosity_factor);
2171 v3f d = d_wanted.normalize() * dl;
2175 if(speed.X > lplayer->movement_liquid_fluidity + lplayer->movement_liquid_fluidity_smooth) speed.X -= lplayer->movement_liquid_fluidity_smooth;
2176 if(speed.X < -lplayer->movement_liquid_fluidity - lplayer->movement_liquid_fluidity_smooth) speed.X += lplayer->movement_liquid_fluidity_smooth;
2177 if(speed.Y > lplayer->movement_liquid_fluidity + lplayer->movement_liquid_fluidity_smooth) speed.Y -= 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.Z > lplayer->movement_liquid_fluidity + lplayer->movement_liquid_fluidity_smooth) speed.Z -= 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;
2184 lplayer->setSpeed(speed);
2189 This also does collision detection.
2191 lplayer->move(dtime_part, this, position_max_increment,
2192 &player_collisions);
2195 while(dtime_downcount > 0.001);
2197 //std::cout<<"Looped "<<loopcount<<" times."<<std::endl;
2199 for(std::vector<CollisionInfo>::iterator i = player_collisions.begin();
2200 i != player_collisions.end(); ++i) {
2201 CollisionInfo &info = *i;
2202 v3f speed_diff = info.new_speed - info.old_speed;;
2203 // Handle only fall damage
2204 // (because otherwise walking against something in fast_move kills you)
2205 if(speed_diff.Y < 0 || info.old_speed.Y >= 0)
2207 // Get rid of other components
2210 f32 pre_factor = 1; // 1 hp per node/s
2211 f32 tolerance = BS*14; // 5 without damage
2212 f32 post_factor = 1; // 1 hp per node/s
2213 if(info.type == COLLISION_NODE)
2215 const ContentFeatures &f = m_gamedef->ndef()->
2216 get(m_map->getNodeNoEx(info.node_p));
2217 // Determine fall damage multiplier
2218 int addp = itemgroup_get(f.groups, "fall_damage_add_percent");
2219 pre_factor = 1.0 + (float)addp/100.0;
2221 float speed = pre_factor * speed_diff.getLength();
2222 if(speed > tolerance)
2224 f32 damage_f = (speed - tolerance)/BS * post_factor;
2225 u16 damage = (u16)(damage_f+0.5);
2227 damageLocalPlayer(damage, true);
2228 MtEvent *e = new SimpleTriggerEvent("PlayerFallingDamage");
2229 m_gamedef->event()->put(e);
2235 A quick draft of lava damage
2237 if(m_lava_hurt_interval.step(dtime, 1.0))
2239 v3f pf = lplayer->getPosition();
2241 // Feet, middle and head
2242 v3s16 p1 = floatToInt(pf + v3f(0, BS*0.1, 0), BS);
2243 MapNode n1 = m_map->getNodeNoEx(p1);
2244 v3s16 p2 = floatToInt(pf + v3f(0, BS*0.8, 0), BS);
2245 MapNode n2 = m_map->getNodeNoEx(p2);
2246 v3s16 p3 = floatToInt(pf + v3f(0, BS*1.6, 0), BS);
2247 MapNode n3 = m_map->getNodeNoEx(p3);
2249 u32 damage_per_second = 0;
2250 damage_per_second = MYMAX(damage_per_second,
2251 m_gamedef->ndef()->get(n1).damage_per_second);
2252 damage_per_second = MYMAX(damage_per_second,
2253 m_gamedef->ndef()->get(n2).damage_per_second);
2254 damage_per_second = MYMAX(damage_per_second,
2255 m_gamedef->ndef()->get(n3).damage_per_second);
2257 if(damage_per_second != 0)
2259 damageLocalPlayer(damage_per_second, true);
2266 if(m_drowning_interval.step(dtime, 2.0))
2268 v3f pf = lplayer->getPosition();
2271 v3s16 p = floatToInt(pf + v3f(0, BS*1.6, 0), BS);
2272 MapNode n = m_map->getNodeNoEx(p);
2273 ContentFeatures c = m_gamedef->ndef()->get(n);
2274 u8 drowning_damage = c.drowning;
2275 if(drowning_damage > 0 && lplayer->hp > 0){
2276 u16 breath = lplayer->getBreath();
2283 lplayer->setBreath(breath);
2284 updateLocalPlayerBreath(breath);
2287 if(lplayer->getBreath() == 0 && drowning_damage > 0){
2288 damageLocalPlayer(drowning_damage, true);
2291 if(m_breathing_interval.step(dtime, 0.5))
2293 v3f pf = lplayer->getPosition();
2296 v3s16 p = floatToInt(pf + v3f(0, BS*1.6, 0), BS);
2297 MapNode n = m_map->getNodeNoEx(p);
2298 ContentFeatures c = m_gamedef->ndef()->get(n);
2300 lplayer->setBreath(11);
2302 else if(c.drowning == 0){
2303 u16 breath = lplayer->getBreath();
2306 lplayer->setBreath(breath);
2307 updateLocalPlayerBreath(breath);
2313 Stuff that can be done in an arbitarily large dtime
2315 for(std::vector<Player*>::iterator i = m_players.begin();
2316 i != m_players.end(); ++i) {
2317 Player *player = *i;
2320 Handle non-local players
2322 if(player->isLocal() == false) {
2324 player->move(dtime, this, 100*BS);
2329 // Update lighting on local player (used for wield item)
2330 u32 day_night_ratio = getDayNightRatio();
2334 // On InvalidPositionException, use this as default
2335 // (day: LIGHT_SUN, night: 0)
2336 MapNode node_at_lplayer(CONTENT_AIR, 0x0f, 0);
2338 v3s16 p = lplayer->getLightPosition();
2339 node_at_lplayer = m_map->getNodeNoEx(p);
2341 u16 light = getInteriorLight(node_at_lplayer, 0, m_gamedef->ndef());
2342 u8 day = light & 0xff;
2343 u8 night = (light >> 8) & 0xff;
2344 finalColorBlend(lplayer->light_color, day, night, day_night_ratio);
2348 Step active objects and update lighting of them
2351 g_profiler->avg("CEnv: num of objects", m_active_objects.size());
2352 bool update_lighting = m_active_object_light_update_interval.step(dtime, 0.21);
2353 for(std::map<u16, ClientActiveObject*>::iterator
2354 i = m_active_objects.begin();
2355 i != m_active_objects.end(); ++i)
2357 ClientActiveObject* obj = i->second;
2359 obj->step(dtime, this);
2368 v3s16 p = obj->getLightPosition();
2369 MapNode n = m_map->getNodeNoEx(p, &pos_ok);
2371 light = n.getLightBlend(day_night_ratio, m_gamedef->ndef());
2373 light = blend_light(day_night_ratio, LIGHT_SUN, 0);
2375 obj->updateLight(light);
2380 Step and handle simple objects
2382 g_profiler->avg("CEnv: num of simple objects", m_simple_objects.size());
2383 for(std::list<ClientSimpleObject*>::iterator
2384 i = m_simple_objects.begin(); i != m_simple_objects.end();)
2386 ClientSimpleObject *simple = *i;
2387 std::list<ClientSimpleObject*>::iterator cur = i;
2389 simple->step(dtime);
2390 if(simple->m_to_be_removed){
2392 m_simple_objects.erase(cur);
2397 void ClientEnvironment::addSimpleObject(ClientSimpleObject *simple)
2399 m_simple_objects.push_back(simple);
2402 ClientActiveObject* ClientEnvironment::getActiveObject(u16 id)
2404 std::map<u16, ClientActiveObject*>::iterator n;
2405 n = m_active_objects.find(id);
2406 if(n == m_active_objects.end())
2411 bool isFreeClientActiveObjectId(u16 id,
2412 std::map<u16, ClientActiveObject*> &objects)
2417 return objects.find(id) == objects.end();
2420 u16 getFreeClientActiveObjectId(
2421 std::map<u16, ClientActiveObject*> &objects)
2423 //try to reuse id's as late as possible
2424 static u16 last_used_id = 0;
2425 u16 startid = last_used_id;
2429 if(isFreeClientActiveObjectId(last_used_id, objects))
2430 return last_used_id;
2432 if(last_used_id == startid)
2437 u16 ClientEnvironment::addActiveObject(ClientActiveObject *object)
2440 if(object->getId() == 0)
2442 u16 new_id = getFreeClientActiveObjectId(m_active_objects);
2445 infostream<<"ClientEnvironment::addActiveObject(): "
2446 <<"no free ids available"<<std::endl;
2450 object->setId(new_id);
2452 if(isFreeClientActiveObjectId(object->getId(), m_active_objects) == false)
2454 infostream<<"ClientEnvironment::addActiveObject(): "
2455 <<"id is not free ("<<object->getId()<<")"<<std::endl;
2459 infostream<<"ClientEnvironment::addActiveObject(): "
2460 <<"added (id="<<object->getId()<<")"<<std::endl;
2461 m_active_objects[object->getId()] = object;
2462 object->addToScene(m_smgr, m_texturesource, m_irr);
2463 { // Update lighting immediately
2468 v3s16 p = object->getLightPosition();
2469 MapNode n = m_map->getNodeNoEx(p, &pos_ok);
2471 light = n.getLightBlend(getDayNightRatio(), m_gamedef->ndef());
2473 light = blend_light(getDayNightRatio(), LIGHT_SUN, 0);
2475 object->updateLight(light);
2477 return object->getId();
2480 void ClientEnvironment::addActiveObject(u16 id, u8 type,
2481 const std::string &init_data)
2483 ClientActiveObject* obj =
2484 ClientActiveObject::create((ActiveObjectType) type, m_gamedef, this);
2487 infostream<<"ClientEnvironment::addActiveObject(): "
2488 <<"id="<<id<<" type="<<type<<": Couldn't create object"
2497 obj->initialize(init_data);
2499 catch(SerializationError &e)
2501 errorstream<<"ClientEnvironment::addActiveObject():"
2502 <<" id="<<id<<" type="<<type
2503 <<": SerializationError in initialize(): "
2505 <<": init_data="<<serializeJsonString(init_data)
2509 addActiveObject(obj);
2512 void ClientEnvironment::removeActiveObject(u16 id)
2514 verbosestream<<"ClientEnvironment::removeActiveObject(): "
2515 <<"id="<<id<<std::endl;
2516 ClientActiveObject* obj = getActiveObject(id);
2519 infostream<<"ClientEnvironment::removeActiveObject(): "
2520 <<"id="<<id<<" not found"<<std::endl;
2523 obj->removeFromScene(true);
2525 m_active_objects.erase(id);
2528 void ClientEnvironment::processActiveObjectMessage(u16 id,
2529 const std::string &data)
2531 ClientActiveObject* obj = getActiveObject(id);
2534 infostream<<"ClientEnvironment::processActiveObjectMessage():"
2535 <<" got message for id="<<id<<", which doesn't exist."
2541 obj->processMessage(data);
2543 catch(SerializationError &e)
2545 errorstream<<"ClientEnvironment::processActiveObjectMessage():"
2546 <<" id="<<id<<" type="<<obj->getType()
2547 <<" SerializationError in processMessage(),"
2548 <<" message="<<serializeJsonString(data)
2554 Callbacks for activeobjects
2557 void ClientEnvironment::damageLocalPlayer(u8 damage, bool handle_hp)
2559 LocalPlayer *lplayer = getLocalPlayer();
2563 if (lplayer->hp == 0) // Don't damage a dead player
2565 if(lplayer->hp > damage)
2566 lplayer->hp -= damage;
2571 ClientEnvEvent event;
2572 event.type = CEE_PLAYER_DAMAGE;
2573 event.player_damage.amount = damage;
2574 event.player_damage.send_to_server = handle_hp;
2575 m_client_event_queue.push_back(event);
2578 void ClientEnvironment::updateLocalPlayerBreath(u16 breath)
2580 ClientEnvEvent event;
2581 event.type = CEE_PLAYER_BREATH;
2582 event.player_breath.amount = breath;
2583 m_client_event_queue.push_back(event);
2587 Client likes to call these
2590 void ClientEnvironment::getActiveObjects(v3f origin, f32 max_d,
2591 std::vector<DistanceSortedActiveObject> &dest)
2593 for(std::map<u16, ClientActiveObject*>::iterator
2594 i = m_active_objects.begin();
2595 i != m_active_objects.end(); ++i)
2597 ClientActiveObject* obj = i->second;
2599 f32 d = (obj->getPosition() - origin).getLength();
2604 DistanceSortedActiveObject dso(obj, d);
2606 dest.push_back(dso);
2610 ClientEnvEvent ClientEnvironment::getClientEvent()
2612 ClientEnvEvent event;
2613 if(m_client_event_queue.empty())
2614 event.type = CEE_NONE;
2616 event = m_client_event_queue.front();
2617 m_client_event_queue.pop_front();
2622 #endif // #ifndef SERVER