3 Copyright (C) 2010-2013 celeron55, Perttu Ahola <celeron55@gmail.com>
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU Lesser General Public License as published by
7 the Free Software Foundation; either version 2.1 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU Lesser General Public License for more details.
15 You should have received a copy of the GNU Lesser General Public License along
16 with this program; if not, write to the Free Software Foundation, Inc.,
17 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
21 #include "environment.h"
24 #include "collision.h"
25 #include "content_mapnode.h"
27 #include "serverobject.h"
28 #include "content_sao.h"
32 #include "scripting_game.h"
34 #include "nodemetadata.h"
37 #include "clientmap.h"
38 #include "localplayer.h"
39 #include "mapblock_mesh.h"
43 #include "daynightratio.h"
46 #include "util/serialize.h"
47 #include "threading/mutex_auto_lock.h"
49 #define PP(x) "("<<(x).X<<","<<(x).Y<<","<<(x).Z<<")"
51 Environment::Environment():
53 m_time_of_day_f(9000./24000),
54 m_time_of_day_speed(0),
56 m_enable_day_night_ratio_override(false),
57 m_day_night_ratio_override(0.0f)
59 m_cache_enable_shaders = g_settings->getBool("enable_shaders");
62 Environment::~Environment()
65 for(std::vector<Player*>::iterator i = m_players.begin();
66 i != m_players.end(); ++i) {
71 void Environment::addPlayer(Player *player)
73 DSTACK(__FUNCTION_NAME);
75 Check that peer_ids are unique.
76 Also check that names are unique.
77 Exception: there can be multiple players with peer_id=0
79 // If peer id is non-zero, it has to be unique.
80 if(player->peer_id != 0)
81 FATAL_ERROR_IF(getPlayer(player->peer_id) != NULL, "Peer id not unique");
82 // Name has to be unique.
83 FATAL_ERROR_IF(getPlayer(player->getName()) != NULL, "Player name not unique");
85 m_players.push_back(player);
88 void Environment::removePlayer(Player* player)
90 for (std::vector<Player*>::iterator it = m_players.begin();
91 it != m_players.end(); ++it) {
92 if ((*it) == player) {
100 Player * Environment::getPlayer(u16 peer_id)
102 for(std::vector<Player*>::iterator i = m_players.begin();
103 i != m_players.end(); ++i) {
105 if(player->peer_id == peer_id)
111 Player * Environment::getPlayer(const char *name)
113 for(std::vector<Player*>::iterator i = m_players.begin();
114 i != m_players.end(); ++i) {
116 if(strcmp(player->getName(), name) == 0)
122 Player * Environment::getRandomConnectedPlayer()
124 std::vector<Player*> connected_players = getPlayers(true);
125 u32 chosen_one = myrand() % connected_players.size();
127 for(std::vector<Player*>::iterator
128 i = connected_players.begin();
129 i != connected_players.end(); ++i) {
130 if(j == chosen_one) {
139 Player * Environment::getNearestConnectedPlayer(v3f pos)
141 std::vector<Player*> connected_players = getPlayers(true);
143 Player *nearest_player = NULL;
144 for(std::vector<Player*>::iterator
145 i = connected_players.begin();
146 i != connected_players.end(); ++i) {
148 f32 d = player->getPosition().getDistanceFrom(pos);
149 if(d < nearest_d || nearest_player == NULL) {
151 nearest_player = player;
154 return nearest_player;
157 std::vector<Player*> Environment::getPlayers()
162 std::vector<Player*> Environment::getPlayers(bool ignore_disconnected)
164 std::vector<Player*> newlist;
165 for(std::vector<Player*>::iterator
166 i = m_players.begin();
167 i != m_players.end(); ++i) {
170 if(ignore_disconnected) {
171 // Ignore disconnected players
172 if(player->peer_id == 0)
176 newlist.push_back(player);
181 u32 Environment::getDayNightRatio()
183 if(m_enable_day_night_ratio_override)
184 return m_day_night_ratio_override;
185 return time_to_daynight_ratio(m_time_of_day_f*24000, m_cache_enable_shaders);
188 void Environment::setTimeOfDaySpeed(float speed)
190 MutexAutoLock(this->m_timeofday_lock);
191 m_time_of_day_speed = speed;
194 float Environment::getTimeOfDaySpeed()
196 MutexAutoLock(this->m_timeofday_lock);
197 float retval = m_time_of_day_speed;
201 void Environment::setTimeOfDay(u32 time)
203 MutexAutoLock(this->m_time_lock);
204 m_time_of_day = time;
205 m_time_of_day_f = (float)time / 24000.0;
208 u32 Environment::getTimeOfDay()
210 MutexAutoLock(this->m_time_lock);
211 u32 retval = m_time_of_day;
215 float Environment::getTimeOfDayF()
217 MutexAutoLock(this->m_time_lock);
218 float retval = m_time_of_day_f;
222 void Environment::stepTimeOfDay(float dtime)
224 // getTimeOfDaySpeed lock the value we need to prevent MT problems
225 float day_speed = getTimeOfDaySpeed();
227 m_time_counter += dtime;
228 f32 speed = day_speed * 24000./(24.*3600);
229 u32 units = (u32)(m_time_counter*speed);
233 if(m_time_of_day + units >= 24000)
235 m_time_of_day = (m_time_of_day + units) % 24000;
237 m_time_of_day_f = (float)m_time_of_day / 24000.0;
240 m_time_counter -= (f32)units / speed;
243 m_time_of_day_f += day_speed/24/3600*dtime;
244 if(m_time_of_day_f > 1.0)
245 m_time_of_day_f -= 1.0;
246 if(m_time_of_day_f < 0.0)
247 m_time_of_day_f += 1.0;
255 ABMWithState::ABMWithState(ActiveBlockModifier *abm_):
259 // Initialize timer to random value to spread processing
260 float itv = abm->getTriggerInterval();
261 itv = MYMAX(0.001, itv); // No less than 1ms
262 int minval = MYMAX(-0.51*itv, -60); // Clamp to
263 int maxval = MYMIN(0.51*itv, 60); // +-60 seconds
264 timer = myrand_range(minval, maxval);
271 void fillRadiusBlock(v3s16 p0, s16 r, std::set<v3s16> &list)
274 for(p.X=p0.X-r; p.X<=p0.X+r; p.X++)
275 for(p.Y=p0.Y-r; p.Y<=p0.Y+r; p.Y++)
276 for(p.Z=p0.Z-r; p.Z<=p0.Z+r; p.Z++)
283 void ActiveBlockList::update(std::vector<v3s16> &active_positions,
285 std::set<v3s16> &blocks_removed,
286 std::set<v3s16> &blocks_added)
291 std::set<v3s16> newlist = m_forceloaded_list;
292 for(std::vector<v3s16>::iterator i = active_positions.begin();
293 i != active_positions.end(); ++i)
295 fillRadiusBlock(*i, radius, newlist);
299 Find out which blocks on the old list are not on the new list
301 // Go through old list
302 for(std::set<v3s16>::iterator i = m_list.begin();
303 i != m_list.end(); ++i)
306 // If not on new list, it's been removed
307 if(newlist.find(p) == newlist.end())
308 blocks_removed.insert(p);
312 Find out which blocks on the new list are not on the old list
314 // Go through new list
315 for(std::set<v3s16>::iterator i = newlist.begin();
316 i != newlist.end(); ++i)
319 // If not on old list, it's been added
320 if(m_list.find(p) == m_list.end())
321 blocks_added.insert(p);
328 for(std::set<v3s16>::iterator i = newlist.begin();
329 i != newlist.end(); ++i)
340 ServerEnvironment::ServerEnvironment(ServerMap *map,
341 GameScripting *scriptIface, IGameDef *gamedef,
342 const std::string &path_world) :
344 m_script(scriptIface),
346 m_path_world(path_world),
347 m_send_recommended_timer(0),
348 m_active_block_interval_overload_skip(0),
350 m_game_time_fraction_counter(0),
351 m_recommended_send_interval(0.1),
352 m_max_lag_estimate(0.1)
356 ServerEnvironment::~ServerEnvironment()
358 // Clear active block list.
359 // This makes the next one delete all active objects.
360 m_active_blocks.clear();
362 // Convert all objects to static and delete the active objects
363 deactivateFarObjects(true);
368 // Delete ActiveBlockModifiers
369 for(std::vector<ABMWithState>::iterator
370 i = m_abms.begin(); i != m_abms.end(); ++i){
375 Map & ServerEnvironment::getMap()
380 ServerMap & ServerEnvironment::getServerMap()
385 bool ServerEnvironment::line_of_sight(v3f pos1, v3f pos2, float stepsize, v3s16 *p)
387 float distance = pos1.getDistanceFrom(pos2);
389 //calculate normalized direction vector
390 v3f normalized_vector = v3f((pos2.X - pos1.X)/distance,
391 (pos2.Y - pos1.Y)/distance,
392 (pos2.Z - pos1.Z)/distance);
394 //find out if there's a node on path between pos1 and pos2
395 for (float i = 1; i < distance; i += stepsize) {
396 v3s16 pos = floatToInt(v3f(normalized_vector.X * i,
397 normalized_vector.Y * i,
398 normalized_vector.Z * i) +pos1,BS);
400 MapNode n = getMap().getNodeNoEx(pos);
402 if(n.param0 != CONTENT_AIR) {
412 void ServerEnvironment::kickAllPlayers(AccessDeniedCode reason,
413 const std::string &str_reason, bool reconnect)
415 for (std::vector<Player*>::iterator it = m_players.begin();
416 it != m_players.end();
418 ((Server*)m_gamedef)->DenyAccessVerCompliant((*it)->peer_id,
419 (*it)->protocol_version, (AccessDeniedCode)reason,
420 str_reason, reconnect);
424 void ServerEnvironment::saveLoadedPlayers()
426 std::string players_path = m_path_world + DIR_DELIM "players";
427 fs::CreateDir(players_path);
429 for (std::vector<Player*>::iterator it = m_players.begin();
430 it != m_players.end();
432 RemotePlayer *player = static_cast<RemotePlayer*>(*it);
433 if (player->checkModified()) {
434 player->save(players_path);
439 void ServerEnvironment::savePlayer(RemotePlayer *player)
441 std::string players_path = m_path_world + DIR_DELIM "players";
442 fs::CreateDir(players_path);
444 player->save(players_path);
447 Player *ServerEnvironment::loadPlayer(const std::string &playername)
449 bool newplayer = false;
451 std::string players_path = m_path_world + DIR_DELIM "players" DIR_DELIM;
452 std::string path = players_path + playername;
454 RemotePlayer *player = static_cast<RemotePlayer *>(getPlayer(playername.c_str()));
456 player = new RemotePlayer(m_gamedef, "");
460 for (u32 i = 0; i < PLAYER_FILE_ALTERNATE_TRIES; i++) {
461 //// Open file and deserialize
462 std::ifstream is(path.c_str(), std::ios_base::binary);
465 player->deSerialize(is, path);
468 if (player->getName() == playername) {
473 path = players_path + playername + itos(i);
477 infostream << "Player file for player " << playername
478 << " not found" << std::endl;
486 player->setModified(false);
490 void ServerEnvironment::saveMeta()
492 std::string path = m_path_world + DIR_DELIM "env_meta.txt";
494 // Open file and serialize
495 std::ostringstream ss(std::ios_base::binary);
498 args.setU64("game_time", m_game_time);
499 args.setU64("time_of_day", getTimeOfDay());
503 if(!fs::safeWriteToFile(path, ss.str()))
505 infostream<<"ServerEnvironment::saveMeta(): Failed to write "
507 throw SerializationError("Couldn't save env meta");
511 void ServerEnvironment::loadMeta()
513 std::string path = m_path_world + DIR_DELIM "env_meta.txt";
515 // Open file and deserialize
516 std::ifstream is(path.c_str(), std::ios_base::binary);
518 infostream << "ServerEnvironment::loadMeta(): Failed to open "
519 << path << std::endl;
520 throw SerializationError("Couldn't load env meta");
525 if (!args.parseConfigLines(is, "EnvArgsEnd")) {
526 throw SerializationError("ServerEnvironment::loadMeta(): "
527 "EnvArgsEnd not found!");
531 m_game_time = args.getU64("game_time");
532 } catch (SettingNotFoundException &e) {
533 // Getting this is crucial, otherwise timestamps are useless
534 throw SerializationError("Couldn't load env meta game_time");
538 m_time_of_day = args.getU64("time_of_day");
539 } catch (SettingNotFoundException &e) {
540 // This is not as important
541 m_time_of_day = 9000;
547 ActiveBlockModifier *abm;
549 std::set<content_t> required_neighbors;
555 ServerEnvironment *m_env;
556 std::map<content_t, std::vector<ActiveABM> > m_aabms;
558 ABMHandler(std::vector<ABMWithState> &abms,
559 float dtime_s, ServerEnvironment *env,
565 INodeDefManager *ndef = env->getGameDef()->ndef();
566 for(std::vector<ABMWithState>::iterator
567 i = abms.begin(); i != abms.end(); ++i) {
568 ActiveBlockModifier *abm = i->abm;
569 float trigger_interval = abm->getTriggerInterval();
570 if(trigger_interval < 0.001)
571 trigger_interval = 0.001;
572 float actual_interval = dtime_s;
575 if(i->timer < trigger_interval)
577 i->timer -= trigger_interval;
578 actual_interval = trigger_interval;
580 float intervals = actual_interval / trigger_interval;
583 float chance = abm->getTriggerChance();
588 aabm.chance = chance / intervals;
592 std::set<std::string> required_neighbors_s
593 = abm->getRequiredNeighbors();
594 for(std::set<std::string>::iterator
595 i = required_neighbors_s.begin();
596 i != required_neighbors_s.end(); ++i)
598 ndef->getIds(*i, aabm.required_neighbors);
601 std::set<std::string> contents_s = abm->getTriggerContents();
602 for(std::set<std::string>::iterator
603 i = contents_s.begin(); i != contents_s.end(); ++i)
605 std::set<content_t> ids;
606 ndef->getIds(*i, ids);
607 for(std::set<content_t>::const_iterator k = ids.begin();
611 std::map<content_t, std::vector<ActiveABM> >::iterator j;
613 if(j == m_aabms.end()){
614 std::vector<ActiveABM> aabmlist;
615 m_aabms[c] = aabmlist;
618 j->second.push_back(aabm);
623 // Find out how many objects the given block and its neighbours contain.
624 // Returns the number of objects in the block, and also in 'wider' the
625 // number of objects in the block and all its neighbours. The latter
626 // may an estimate if any neighbours are unloaded.
627 u32 countObjects(MapBlock *block, ServerMap * map, u32 &wider)
630 u32 wider_unknown_count = 0;
631 for(s16 x=-1; x<=1; x++)
632 for(s16 y=-1; y<=1; y++)
633 for(s16 z=-1; z<=1; z++)
635 MapBlock *block2 = map->getBlockNoCreateNoEx(
636 block->getPos() + v3s16(x,y,z));
638 wider_unknown_count++;
641 wider += block2->m_static_objects.m_active.size()
642 + block2->m_static_objects.m_stored.size();
645 u32 active_object_count = block->m_static_objects.m_active.size();
646 u32 wider_known_count = 3*3*3 - wider_unknown_count;
647 wider += wider_unknown_count * wider / wider_known_count;
648 return active_object_count;
651 void apply(MapBlock *block)
656 ServerMap *map = &m_env->getServerMap();
658 u32 active_object_count_wider;
659 u32 active_object_count = this->countObjects(block, map, active_object_count_wider);
660 m_env->m_added_objects = 0;
663 for(p0.X=0; p0.X<MAP_BLOCKSIZE; p0.X++)
664 for(p0.Y=0; p0.Y<MAP_BLOCKSIZE; p0.Y++)
665 for(p0.Z=0; p0.Z<MAP_BLOCKSIZE; p0.Z++)
667 MapNode n = block->getNodeNoEx(p0);
668 content_t c = n.getContent();
669 v3s16 p = p0 + block->getPosRelative();
671 std::map<content_t, std::vector<ActiveABM> >::iterator j;
673 if(j == m_aabms.end())
676 for(std::vector<ActiveABM>::iterator
677 i = j->second.begin(); i != j->second.end(); ++i) {
678 if(myrand() % i->chance != 0)
682 if(!i->required_neighbors.empty())
685 for(p1.X = p.X-1; p1.X <= p.X+1; p1.X++)
686 for(p1.Y = p.Y-1; p1.Y <= p.Y+1; p1.Y++)
687 for(p1.Z = p.Z-1; p1.Z <= p.Z+1; p1.Z++)
691 MapNode n = map->getNodeNoEx(p1);
692 content_t c = n.getContent();
693 std::set<content_t>::const_iterator k;
694 k = i->required_neighbors.find(c);
695 if(k != i->required_neighbors.end()){
699 // No required neighbor found
704 // Call all the trigger variations
705 i->abm->trigger(m_env, p, n);
706 i->abm->trigger(m_env, p, n,
707 active_object_count, active_object_count_wider);
709 // Count surrounding objects again if the abms added any
710 if(m_env->m_added_objects > 0) {
711 active_object_count = countObjects(block, map, active_object_count_wider);
712 m_env->m_added_objects = 0;
719 void ServerEnvironment::activateBlock(MapBlock *block, u32 additional_dtime)
721 // Reset usage timer immediately, otherwise a block that becomes active
722 // again at around the same time as it would normally be unloaded will
723 // get unloaded incorrectly. (I think this still leaves a small possibility
724 // of a race condition between this and server::AsyncRunStep, which only
725 // some kind of synchronisation will fix, but it at least reduces the window
726 // of opportunity for it to break from seconds to nanoseconds)
727 block->resetUsageTimer();
729 // Get time difference
731 u32 stamp = block->getTimestamp();
732 if(m_game_time > stamp && stamp != BLOCK_TIMESTAMP_UNDEFINED)
733 dtime_s = m_game_time - block->getTimestamp();
734 dtime_s += additional_dtime;
736 /*infostream<<"ServerEnvironment::activateBlock(): block timestamp: "
737 <<stamp<<", game time: "<<m_game_time<<std::endl;*/
739 // Set current time as timestamp
740 block->setTimestampNoChangedFlag(m_game_time);
742 /*infostream<<"ServerEnvironment::activateBlock(): block is "
743 <<dtime_s<<" seconds old."<<std::endl;*/
745 // Activate stored objects
746 activateObjects(block, dtime_s);
749 std::map<v3s16, NodeTimer> elapsed_timers =
750 block->m_node_timers.step((float)dtime_s);
751 if(!elapsed_timers.empty()){
753 for(std::map<v3s16, NodeTimer>::iterator
754 i = elapsed_timers.begin();
755 i != elapsed_timers.end(); ++i){
756 n = block->getNodeNoEx(i->first);
757 v3s16 p = i->first + block->getPosRelative();
758 if(m_script->node_on_timer(p,n,i->second.elapsed))
759 block->setNodeTimer(i->first,NodeTimer(i->second.timeout,0));
763 /* Handle ActiveBlockModifiers */
764 ABMHandler abmhandler(m_abms, dtime_s, this, false);
765 abmhandler.apply(block);
768 void ServerEnvironment::addActiveBlockModifier(ActiveBlockModifier *abm)
770 m_abms.push_back(ABMWithState(abm));
773 bool ServerEnvironment::setNode(v3s16 p, const MapNode &n)
775 INodeDefManager *ndef = m_gamedef->ndef();
776 MapNode n_old = m_map->getNodeNoEx(p);
779 if (ndef->get(n_old).has_on_destruct)
780 m_script->node_on_destruct(p, n_old);
783 if (!m_map->addNodeWithEvent(p, n))
786 // Update active VoxelManipulator if a mapgen thread
787 m_map->updateVManip(p);
789 // Call post-destructor
790 if (ndef->get(n_old).has_after_destruct)
791 m_script->node_after_destruct(p, n_old);
794 if (ndef->get(n).has_on_construct)
795 m_script->node_on_construct(p, n);
800 bool ServerEnvironment::removeNode(v3s16 p)
802 INodeDefManager *ndef = m_gamedef->ndef();
803 MapNode n_old = m_map->getNodeNoEx(p);
806 if (ndef->get(n_old).has_on_destruct)
807 m_script->node_on_destruct(p, n_old);
810 // This is slightly optimized compared to addNodeWithEvent(air)
811 if (!m_map->removeNodeWithEvent(p))
814 // Update active VoxelManipulator if a mapgen thread
815 m_map->updateVManip(p);
817 // Call post-destructor
818 if (ndef->get(n_old).has_after_destruct)
819 m_script->node_after_destruct(p, n_old);
821 // Air doesn't require constructor
825 bool ServerEnvironment::swapNode(v3s16 p, const MapNode &n)
827 if (!m_map->addNodeWithEvent(p, n, false))
830 // Update active VoxelManipulator if a mapgen thread
831 m_map->updateVManip(p);
836 void ServerEnvironment::getObjectsInsideRadius(std::vector<u16> &objects, v3f pos, float radius)
838 for(std::map<u16, ServerActiveObject*>::iterator
839 i = m_active_objects.begin();
840 i != m_active_objects.end(); ++i)
842 ServerActiveObject* obj = i->second;
844 v3f objectpos = obj->getBasePosition();
845 if(objectpos.getDistanceFrom(pos) > radius)
847 objects.push_back(id);
851 void ServerEnvironment::clearAllObjects()
853 infostream<<"ServerEnvironment::clearAllObjects(): "
854 <<"Removing all active objects"<<std::endl;
855 std::vector<u16> objects_to_remove;
856 for(std::map<u16, ServerActiveObject*>::iterator
857 i = m_active_objects.begin();
858 i != m_active_objects.end(); ++i) {
859 ServerActiveObject* obj = i->second;
860 if(obj->getType() == ACTIVEOBJECT_TYPE_PLAYER)
863 // Delete static object if block is loaded
864 if(obj->m_static_exists){
865 MapBlock *block = m_map->getBlockNoCreateNoEx(obj->m_static_block);
867 block->m_static_objects.remove(id);
868 block->raiseModified(MOD_STATE_WRITE_NEEDED,
869 MOD_REASON_CLEAR_ALL_OBJECTS);
870 obj->m_static_exists = false;
873 // If known by some client, don't delete immediately
874 if(obj->m_known_by_count > 0){
875 obj->m_pending_deactivation = true;
876 obj->m_removed = true;
880 // Tell the object about removal
881 obj->removingFromEnvironment();
882 // Deregister in scripting api
883 m_script->removeObjectReference(obj);
885 // Delete active object
886 if(obj->environmentDeletes())
888 // Id to be removed from m_active_objects
889 objects_to_remove.push_back(id);
892 // Remove references from m_active_objects
893 for(std::vector<u16>::iterator i = objects_to_remove.begin();
894 i != objects_to_remove.end(); ++i) {
895 m_active_objects.erase(*i);
898 // Get list of loaded blocks
899 std::vector<v3s16> loaded_blocks;
900 infostream<<"ServerEnvironment::clearAllObjects(): "
901 <<"Listing all loaded blocks"<<std::endl;
902 m_map->listAllLoadedBlocks(loaded_blocks);
903 infostream<<"ServerEnvironment::clearAllObjects(): "
904 <<"Done listing all loaded blocks: "
905 <<loaded_blocks.size()<<std::endl;
907 // Get list of loadable blocks
908 std::vector<v3s16> loadable_blocks;
909 infostream<<"ServerEnvironment::clearAllObjects(): "
910 <<"Listing all loadable blocks"<<std::endl;
911 m_map->listAllLoadableBlocks(loadable_blocks);
912 infostream<<"ServerEnvironment::clearAllObjects(): "
913 <<"Done listing all loadable blocks: "
914 <<loadable_blocks.size()
915 <<", now clearing"<<std::endl;
917 // Grab a reference on each loaded block to avoid unloading it
918 for(std::vector<v3s16>::iterator i = loaded_blocks.begin();
919 i != loaded_blocks.end(); ++i) {
921 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
922 assert(block != NULL);
926 // Remove objects in all loadable blocks
927 u32 unload_interval = g_settings->getS32("max_clearobjects_extra_loaded_blocks");
928 unload_interval = MYMAX(unload_interval, 1);
929 u32 report_interval = loadable_blocks.size() / 10;
930 u32 num_blocks_checked = 0;
931 u32 num_blocks_cleared = 0;
932 u32 num_objs_cleared = 0;
933 for(std::vector<v3s16>::iterator i = loadable_blocks.begin();
934 i != loadable_blocks.end(); ++i) {
936 MapBlock *block = m_map->emergeBlock(p, false);
938 errorstream<<"ServerEnvironment::clearAllObjects(): "
939 <<"Failed to emerge block "<<PP(p)<<std::endl;
942 u32 num_stored = block->m_static_objects.m_stored.size();
943 u32 num_active = block->m_static_objects.m_active.size();
944 if(num_stored != 0 || num_active != 0){
945 block->m_static_objects.m_stored.clear();
946 block->m_static_objects.m_active.clear();
947 block->raiseModified(MOD_STATE_WRITE_NEEDED,
948 MOD_REASON_CLEAR_ALL_OBJECTS);
949 num_objs_cleared += num_stored + num_active;
950 num_blocks_cleared++;
952 num_blocks_checked++;
954 if(report_interval != 0 &&
955 num_blocks_checked % report_interval == 0){
956 float percent = 100.0 * (float)num_blocks_checked /
957 loadable_blocks.size();
958 infostream<<"ServerEnvironment::clearAllObjects(): "
959 <<"Cleared "<<num_objs_cleared<<" objects"
960 <<" in "<<num_blocks_cleared<<" blocks ("
961 <<percent<<"%)"<<std::endl;
963 if(num_blocks_checked % unload_interval == 0){
964 m_map->unloadUnreferencedBlocks();
967 m_map->unloadUnreferencedBlocks();
969 // Drop references that were added above
970 for(std::vector<v3s16>::iterator i = loaded_blocks.begin();
971 i != loaded_blocks.end(); ++i) {
973 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
978 infostream<<"ServerEnvironment::clearAllObjects(): "
979 <<"Finished: Cleared "<<num_objs_cleared<<" objects"
980 <<" in "<<num_blocks_cleared<<" blocks"<<std::endl;
983 void ServerEnvironment::step(float dtime)
985 DSTACK(__FUNCTION_NAME);
987 //TimeTaker timer("ServerEnv step");
989 /* Step time of day */
990 stepTimeOfDay(dtime);
993 // NOTE: This is kind of funny on a singleplayer game, but doesn't
994 // really matter that much.
995 m_recommended_send_interval = g_settings->getFloat("dedicated_server_step");
1001 m_game_time_fraction_counter += dtime;
1002 u32 inc_i = (u32)m_game_time_fraction_counter;
1003 m_game_time += inc_i;
1004 m_game_time_fraction_counter -= (float)inc_i;
1011 ScopeProfiler sp(g_profiler, "SEnv: handle players avg", SPT_AVG);
1012 for(std::vector<Player*>::iterator i = m_players.begin();
1013 i != m_players.end(); ++i)
1015 Player *player = *i;
1017 // Ignore disconnected players
1018 if(player->peer_id == 0)
1022 player->move(dtime, this, 100*BS);
1027 Manage active block list
1029 if(m_active_blocks_management_interval.step(dtime, 2.0))
1031 ScopeProfiler sp(g_profiler, "SEnv: manage act. block list avg /2s", SPT_AVG);
1033 Get player block positions
1035 std::vector<v3s16> players_blockpos;
1036 for(std::vector<Player*>::iterator
1037 i = m_players.begin();
1038 i != m_players.end(); ++i) {
1039 Player *player = *i;
1040 // Ignore disconnected players
1041 if(player->peer_id == 0)
1044 v3s16 blockpos = getNodeBlockPos(
1045 floatToInt(player->getPosition(), BS));
1046 players_blockpos.push_back(blockpos);
1050 Update list of active blocks, collecting changes
1052 const s16 active_block_range = g_settings->getS16("active_block_range");
1053 std::set<v3s16> blocks_removed;
1054 std::set<v3s16> blocks_added;
1055 m_active_blocks.update(players_blockpos, active_block_range,
1056 blocks_removed, blocks_added);
1059 Handle removed blocks
1062 // Convert active objects that are no more in active blocks to static
1063 deactivateFarObjects(false);
1065 for(std::set<v3s16>::iterator
1066 i = blocks_removed.begin();
1067 i != blocks_removed.end(); ++i)
1071 /* infostream<<"Server: Block " << PP(p)
1072 << " became inactive"<<std::endl; */
1074 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1078 // Set current time as timestamp (and let it set ChangedFlag)
1079 block->setTimestamp(m_game_time);
1086 for(std::set<v3s16>::iterator
1087 i = blocks_added.begin();
1088 i != blocks_added.end(); ++i)
1092 MapBlock *block = m_map->getBlockOrEmerge(p);
1094 m_active_blocks.m_list.erase(p);
1098 activateBlock(block);
1099 /* infostream<<"Server: Block " << PP(p)
1100 << " became active"<<std::endl; */
1105 Mess around in active blocks
1107 if(m_active_blocks_nodemetadata_interval.step(dtime, 1.0))
1109 ScopeProfiler sp(g_profiler, "SEnv: mess in act. blocks avg /1s", SPT_AVG);
1113 for(std::set<v3s16>::iterator
1114 i = m_active_blocks.m_list.begin();
1115 i != m_active_blocks.m_list.end(); ++i)
1119 /*infostream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
1120 <<") being handled"<<std::endl;*/
1122 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1126 // Reset block usage timer
1127 block->resetUsageTimer();
1129 // Set current time as timestamp
1130 block->setTimestampNoChangedFlag(m_game_time);
1131 // If time has changed much from the one on disk,
1132 // set block to be saved when it is unloaded
1133 if(block->getTimestamp() > block->getDiskTimestamp() + 60)
1134 block->raiseModified(MOD_STATE_WRITE_AT_UNLOAD,
1135 MOD_REASON_BLOCK_EXPIRED);
1138 std::map<v3s16, NodeTimer> elapsed_timers =
1139 block->m_node_timers.step((float)dtime);
1140 if(!elapsed_timers.empty()){
1142 for(std::map<v3s16, NodeTimer>::iterator
1143 i = elapsed_timers.begin();
1144 i != elapsed_timers.end(); ++i){
1145 n = block->getNodeNoEx(i->first);
1146 p = i->first + block->getPosRelative();
1147 if(m_script->node_on_timer(p,n,i->second.elapsed))
1148 block->setNodeTimer(i->first,NodeTimer(i->second.timeout,0));
1154 const float abm_interval = 1.0;
1155 if(m_active_block_modifier_interval.step(dtime, abm_interval))
1157 if(m_active_block_interval_overload_skip > 0){
1158 ScopeProfiler sp(g_profiler, "SEnv: ABM overload skips");
1159 m_active_block_interval_overload_skip--;
1162 ScopeProfiler sp(g_profiler, "SEnv: modify in blocks avg /1s", SPT_AVG);
1163 TimeTaker timer("modify in active blocks");
1165 // Initialize handling of ActiveBlockModifiers
1166 ABMHandler abmhandler(m_abms, abm_interval, this, true);
1168 for(std::set<v3s16>::iterator
1169 i = m_active_blocks.m_list.begin();
1170 i != m_active_blocks.m_list.end(); ++i)
1174 /*infostream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
1175 <<") being handled"<<std::endl;*/
1177 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1181 // Set current time as timestamp
1182 block->setTimestampNoChangedFlag(m_game_time);
1184 /* Handle ActiveBlockModifiers */
1185 abmhandler.apply(block);
1188 u32 time_ms = timer.stop(true);
1189 u32 max_time_ms = 200;
1190 if(time_ms > max_time_ms){
1191 infostream<<"WARNING: active block modifiers took "
1192 <<time_ms<<"ms (longer than "
1193 <<max_time_ms<<"ms)"<<std::endl;
1194 m_active_block_interval_overload_skip = (time_ms / max_time_ms) + 1;
1199 Step script environment (run global on_step())
1201 m_script->environment_Step(dtime);
1207 ScopeProfiler sp(g_profiler, "SEnv: step act. objs avg", SPT_AVG);
1208 //TimeTaker timer("Step active objects");
1210 g_profiler->avg("SEnv: num of objects", m_active_objects.size());
1212 // This helps the objects to send data at the same time
1213 bool send_recommended = false;
1214 m_send_recommended_timer += dtime;
1215 if(m_send_recommended_timer > getSendRecommendedInterval())
1217 m_send_recommended_timer -= getSendRecommendedInterval();
1218 send_recommended = true;
1221 for(std::map<u16, ServerActiveObject*>::iterator
1222 i = m_active_objects.begin();
1223 i != m_active_objects.end(); ++i)
1225 ServerActiveObject* obj = i->second;
1226 // Don't step if is to be removed or stored statically
1227 if(obj->m_removed || obj->m_pending_deactivation)
1230 obj->step(dtime, send_recommended);
1231 // Read messages from object
1232 while(!obj->m_messages_out.empty())
1234 m_active_object_messages.push(
1235 obj->m_messages_out.front());
1236 obj->m_messages_out.pop();
1242 Manage active objects
1244 if(m_object_management_interval.step(dtime, 0.5))
1246 ScopeProfiler sp(g_profiler, "SEnv: remove removed objs avg /.5s", SPT_AVG);
1248 Remove objects that satisfy (m_removed && m_known_by_count==0)
1250 removeRemovedObjects();
1254 ServerActiveObject* ServerEnvironment::getActiveObject(u16 id)
1256 std::map<u16, ServerActiveObject*>::iterator n;
1257 n = m_active_objects.find(id);
1258 if(n == m_active_objects.end())
1263 bool isFreeServerActiveObjectId(u16 id,
1264 std::map<u16, ServerActiveObject*> &objects)
1269 return objects.find(id) == objects.end();
1272 u16 getFreeServerActiveObjectId(
1273 std::map<u16, ServerActiveObject*> &objects)
1275 //try to reuse id's as late as possible
1276 static u16 last_used_id = 0;
1277 u16 startid = last_used_id;
1281 if(isFreeServerActiveObjectId(last_used_id, objects))
1282 return last_used_id;
1284 if(last_used_id == startid)
1289 u16 ServerEnvironment::addActiveObject(ServerActiveObject *object)
1291 assert(object); // Pre-condition
1293 u16 id = addActiveObjectRaw(object, true, 0);
1298 Finds out what new objects have been added to
1299 inside a radius around a position
1301 void ServerEnvironment::getAddedActiveObjects(Player *player, s16 radius,
1303 std::set<u16> ¤t_objects,
1304 std::queue<u16> &added_objects)
1306 f32 radius_f = radius * BS;
1307 f32 player_radius_f = player_radius * BS;
1309 if (player_radius_f < 0)
1310 player_radius_f = 0;
1313 Go through the object list,
1314 - discard m_removed objects,
1315 - discard objects that are too far away,
1316 - discard objects that are found in current_objects.
1317 - add remaining objects to added_objects
1319 for(std::map<u16, ServerActiveObject*>::iterator
1320 i = m_active_objects.begin();
1321 i != m_active_objects.end(); ++i) {
1325 ServerActiveObject *object = i->second;
1329 // Discard if removed or deactivating
1330 if(object->m_removed || object->m_pending_deactivation)
1333 f32 distance_f = object->getBasePosition().getDistanceFrom(player->getPosition());
1334 if (object->getType() == ACTIVEOBJECT_TYPE_PLAYER) {
1335 // Discard if too far
1336 if (distance_f > player_radius_f && player_radius_f != 0)
1338 } else if (distance_f > radius_f)
1341 // Discard if already on current_objects
1342 std::set<u16>::iterator n;
1343 n = current_objects.find(id);
1344 if(n != current_objects.end())
1346 // Add to added_objects
1347 added_objects.push(id);
1352 Finds out what objects have been removed from
1353 inside a radius around a position
1355 void ServerEnvironment::getRemovedActiveObjects(Player *player, s16 radius,
1357 std::set<u16> ¤t_objects,
1358 std::queue<u16> &removed_objects)
1360 f32 radius_f = radius * BS;
1361 f32 player_radius_f = player_radius * BS;
1363 if (player_radius_f < 0)
1364 player_radius_f = 0;
1367 Go through current_objects; object is removed if:
1368 - object is not found in m_active_objects (this is actually an
1369 error condition; objects should be set m_removed=true and removed
1370 only after all clients have been informed about removal), or
1371 - object has m_removed=true, or
1372 - object is too far away
1374 for(std::set<u16>::iterator
1375 i = current_objects.begin();
1376 i != current_objects.end(); ++i)
1379 ServerActiveObject *object = getActiveObject(id);
1381 if (object == NULL) {
1382 infostream << "ServerEnvironment::getRemovedActiveObjects():"
1383 << " object in current_objects is NULL" << std::endl;
1384 removed_objects.push(id);
1388 if (object->m_removed || object->m_pending_deactivation) {
1389 removed_objects.push(id);
1393 f32 distance_f = object->getBasePosition().getDistanceFrom(player->getPosition());
1394 if (object->getType() == ACTIVEOBJECT_TYPE_PLAYER) {
1395 if (distance_f <= player_radius_f || player_radius_f == 0)
1397 } else if (distance_f <= radius_f)
1400 // Object is no longer visible
1401 removed_objects.push(id);
1405 void ServerEnvironment::setStaticForActiveObjectsInBlock(
1406 v3s16 blockpos, bool static_exists, v3s16 static_block)
1408 MapBlock *block = m_map->getBlockNoCreateNoEx(blockpos);
1412 for (std::map<u16, StaticObject>::iterator
1413 so_it = block->m_static_objects.m_active.begin();
1414 so_it != block->m_static_objects.m_active.end(); ++so_it) {
1415 // Get the ServerActiveObject counterpart to this StaticObject
1416 std::map<u16, ServerActiveObject *>::iterator ao_it;
1417 ao_it = m_active_objects.find(so_it->first);
1418 if (ao_it == m_active_objects.end()) {
1419 // If this ever happens, there must be some kind of nasty bug.
1420 errorstream << "ServerEnvironment::setStaticForObjectsInBlock(): "
1421 "Object from MapBlock::m_static_objects::m_active not found "
1422 "in m_active_objects";
1426 ServerActiveObject *sao = ao_it->second;
1427 sao->m_static_exists = static_exists;
1428 sao->m_static_block = static_block;
1432 ActiveObjectMessage ServerEnvironment::getActiveObjectMessage()
1434 if(m_active_object_messages.empty())
1435 return ActiveObjectMessage(0);
1437 ActiveObjectMessage message = m_active_object_messages.front();
1438 m_active_object_messages.pop();
1443 ************ Private methods *************
1446 u16 ServerEnvironment::addActiveObjectRaw(ServerActiveObject *object,
1447 bool set_changed, u32 dtime_s)
1449 assert(object); // Pre-condition
1450 if(object->getId() == 0){
1451 u16 new_id = getFreeServerActiveObjectId(m_active_objects);
1454 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1455 <<"no free ids available"<<std::endl;
1456 if(object->environmentDeletes())
1460 object->setId(new_id);
1463 verbosestream<<"ServerEnvironment::addActiveObjectRaw(): "
1464 <<"supplied with id "<<object->getId()<<std::endl;
1466 if(isFreeServerActiveObjectId(object->getId(), m_active_objects) == false)
1468 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1469 <<"id is not free ("<<object->getId()<<")"<<std::endl;
1470 if(object->environmentDeletes())
1475 if (objectpos_over_limit(object->getBasePosition())) {
1476 v3f p = object->getBasePosition();
1477 errorstream << "ServerEnvironment::addActiveObjectRaw(): "
1478 << "object position (" << p.X << "," << p.Y << "," << p.Z
1479 << ") outside maximum range" << std::endl;
1480 if (object->environmentDeletes())
1485 /*infostream<<"ServerEnvironment::addActiveObjectRaw(): "
1486 <<"added (id="<<object->getId()<<")"<<std::endl;*/
1488 m_active_objects[object->getId()] = object;
1490 verbosestream<<"ServerEnvironment::addActiveObjectRaw(): "
1491 <<"Added id="<<object->getId()<<"; there are now "
1492 <<m_active_objects.size()<<" active objects."
1495 // Register reference in scripting api (must be done before post-init)
1496 m_script->addObjectReference(object);
1497 // Post-initialize object
1498 object->addedToEnvironment(dtime_s);
1500 // Add static data to block
1501 if(object->isStaticAllowed())
1503 // Add static object to active static list of the block
1504 v3f objectpos = object->getBasePosition();
1505 std::string staticdata = object->getStaticData();
1506 StaticObject s_obj(object->getType(), objectpos, staticdata);
1507 // Add to the block where the object is located in
1508 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1509 MapBlock *block = m_map->emergeBlock(blockpos);
1511 block->m_static_objects.m_active[object->getId()] = s_obj;
1512 object->m_static_exists = true;
1513 object->m_static_block = blockpos;
1516 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1517 MOD_REASON_ADD_ACTIVE_OBJECT_RAW);
1519 v3s16 p = floatToInt(objectpos, BS);
1520 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1521 <<"could not emerge block for storing id="<<object->getId()
1522 <<" statically (pos="<<PP(p)<<")"<<std::endl;
1526 return object->getId();
1530 Remove objects that satisfy (m_removed && m_known_by_count==0)
1532 void ServerEnvironment::removeRemovedObjects()
1534 std::vector<u16> objects_to_remove;
1535 for(std::map<u16, ServerActiveObject*>::iterator
1536 i = m_active_objects.begin();
1537 i != m_active_objects.end(); ++i) {
1539 ServerActiveObject* obj = i->second;
1540 // This shouldn't happen but check it
1543 infostream<<"NULL object found in ServerEnvironment"
1544 <<" while finding removed objects. id="<<id<<std::endl;
1545 // Id to be removed from m_active_objects
1546 objects_to_remove.push_back(id);
1551 We will delete objects that are marked as removed or thatare
1552 waiting for deletion after deactivation
1554 if(obj->m_removed == false && obj->m_pending_deactivation == false)
1558 Delete static data from block if is marked as removed
1560 if(obj->m_static_exists && obj->m_removed)
1562 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
1564 block->m_static_objects.remove(id);
1565 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1566 MOD_REASON_REMOVE_OBJECTS_REMOVE);
1567 obj->m_static_exists = false;
1569 infostream<<"Failed to emerge block from which an object to "
1570 <<"be removed was loaded from. id="<<id<<std::endl;
1574 // If m_known_by_count > 0, don't actually remove. On some future
1575 // invocation this will be 0, which is when removal will continue.
1576 if(obj->m_known_by_count > 0)
1580 Move static data from active to stored if not marked as removed
1582 if(obj->m_static_exists && !obj->m_removed){
1583 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
1585 std::map<u16, StaticObject>::iterator i =
1586 block->m_static_objects.m_active.find(id);
1587 if(i != block->m_static_objects.m_active.end()){
1588 block->m_static_objects.m_stored.push_back(i->second);
1589 block->m_static_objects.m_active.erase(id);
1590 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1591 MOD_REASON_REMOVE_OBJECTS_DEACTIVATE);
1594 infostream<<"Failed to emerge block from which an object to "
1595 <<"be deactivated was loaded from. id="<<id<<std::endl;
1599 // Tell the object about removal
1600 obj->removingFromEnvironment();
1601 // Deregister in scripting api
1602 m_script->removeObjectReference(obj);
1605 if(obj->environmentDeletes())
1608 // Id to be removed from m_active_objects
1609 objects_to_remove.push_back(id);
1611 // Remove references from m_active_objects
1612 for(std::vector<u16>::iterator i = objects_to_remove.begin();
1613 i != objects_to_remove.end(); ++i) {
1614 m_active_objects.erase(*i);
1618 static void print_hexdump(std::ostream &o, const std::string &data)
1620 const int linelength = 16;
1621 for(int l=0; ; l++){
1622 int i0 = linelength * l;
1623 bool at_end = false;
1624 int thislinelength = linelength;
1625 if(i0 + thislinelength > (int)data.size()){
1626 thislinelength = data.size() - i0;
1629 for(int di=0; di<linelength; di++){
1632 if(di<thislinelength)
1633 snprintf(buf, 4, "%.2x ", data[i]);
1635 snprintf(buf, 4, " ");
1639 for(int di=0; di<thislinelength; di++){
1653 Convert stored objects from blocks near the players to active.
1655 void ServerEnvironment::activateObjects(MapBlock *block, u32 dtime_s)
1660 // Ignore if no stored objects (to not set changed flag)
1661 if(block->m_static_objects.m_stored.empty())
1664 verbosestream<<"ServerEnvironment::activateObjects(): "
1665 <<"activating objects of block "<<PP(block->getPos())
1666 <<" ("<<block->m_static_objects.m_stored.size()
1667 <<" objects)"<<std::endl;
1668 bool large_amount = (block->m_static_objects.m_stored.size() > g_settings->getU16("max_objects_per_block"));
1670 errorstream<<"suspiciously large amount of objects detected: "
1671 <<block->m_static_objects.m_stored.size()<<" in "
1672 <<PP(block->getPos())
1673 <<"; removing all of them."<<std::endl;
1674 // Clear stored list
1675 block->m_static_objects.m_stored.clear();
1676 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1677 MOD_REASON_TOO_MANY_OBJECTS);
1681 // Activate stored objects
1682 std::vector<StaticObject> new_stored;
1683 for (std::vector<StaticObject>::iterator
1684 i = block->m_static_objects.m_stored.begin();
1685 i != block->m_static_objects.m_stored.end(); ++i) {
1686 StaticObject &s_obj = *i;
1688 // Create an active object from the data
1689 ServerActiveObject *obj = ServerActiveObject::create
1690 ((ActiveObjectType) s_obj.type, this, 0, s_obj.pos, s_obj.data);
1691 // If couldn't create object, store static data back.
1693 errorstream<<"ServerEnvironment::activateObjects(): "
1694 <<"failed to create active object from static object "
1695 <<"in block "<<PP(s_obj.pos/BS)
1696 <<" type="<<(int)s_obj.type<<" data:"<<std::endl;
1697 print_hexdump(verbosestream, s_obj.data);
1699 new_stored.push_back(s_obj);
1702 verbosestream<<"ServerEnvironment::activateObjects(): "
1703 <<"activated static object pos="<<PP(s_obj.pos/BS)
1704 <<" type="<<(int)s_obj.type<<std::endl;
1705 // This will also add the object to the active static list
1706 addActiveObjectRaw(obj, false, dtime_s);
1708 // Clear stored list
1709 block->m_static_objects.m_stored.clear();
1710 // Add leftover failed stuff to stored list
1711 for(std::vector<StaticObject>::iterator
1712 i = new_stored.begin();
1713 i != new_stored.end(); ++i) {
1714 StaticObject &s_obj = *i;
1715 block->m_static_objects.m_stored.push_back(s_obj);
1718 // Turn the active counterparts of activated objects not pending for
1720 for(std::map<u16, StaticObject>::iterator
1721 i = block->m_static_objects.m_active.begin();
1722 i != block->m_static_objects.m_active.end(); ++i)
1725 ServerActiveObject *object = getActiveObject(id);
1727 object->m_pending_deactivation = false;
1731 Note: Block hasn't really been modified here.
1732 The objects have just been activated and moved from the stored
1733 static list to the active static list.
1734 As such, the block is essentially the same.
1735 Thus, do not call block->raiseModified(MOD_STATE_WRITE_NEEDED).
1736 Otherwise there would be a huge amount of unnecessary I/O.
1741 Convert objects that are not standing inside active blocks to static.
1743 If m_known_by_count != 0, active object is not deleted, but static
1744 data is still updated.
1746 If force_delete is set, active object is deleted nevertheless. It
1747 shall only be set so in the destructor of the environment.
1749 If block wasn't generated (not in memory or on disk),
1751 void ServerEnvironment::deactivateFarObjects(bool force_delete)
1753 std::vector<u16> objects_to_remove;
1754 for(std::map<u16, ServerActiveObject*>::iterator
1755 i = m_active_objects.begin();
1756 i != m_active_objects.end(); ++i) {
1757 ServerActiveObject* obj = i->second;
1760 // Do not deactivate if static data creation not allowed
1761 if(!force_delete && !obj->isStaticAllowed())
1764 // If pending deactivation, let removeRemovedObjects() do it
1765 if(!force_delete && obj->m_pending_deactivation)
1769 v3f objectpos = obj->getBasePosition();
1771 // The block in which the object resides in
1772 v3s16 blockpos_o = getNodeBlockPos(floatToInt(objectpos, BS));
1774 // If object's static data is stored in a deactivated block and object
1775 // is actually located in an active block, re-save to the block in
1776 // which the object is actually located in.
1778 obj->m_static_exists &&
1779 !m_active_blocks.contains(obj->m_static_block) &&
1780 m_active_blocks.contains(blockpos_o))
1782 v3s16 old_static_block = obj->m_static_block;
1784 // Save to block where object is located
1785 MapBlock *block = m_map->emergeBlock(blockpos_o, false);
1787 errorstream<<"ServerEnvironment::deactivateFarObjects(): "
1788 <<"Could not save object id="<<id
1789 <<" to it's current block "<<PP(blockpos_o)
1793 std::string staticdata_new = obj->getStaticData();
1794 StaticObject s_obj(obj->getType(), objectpos, staticdata_new);
1795 block->m_static_objects.insert(id, s_obj);
1796 obj->m_static_block = blockpos_o;
1797 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1798 MOD_REASON_STATIC_DATA_ADDED);
1800 // Delete from block where object was located
1801 block = m_map->emergeBlock(old_static_block, false);
1803 errorstream<<"ServerEnvironment::deactivateFarObjects(): "
1804 <<"Could not delete object id="<<id
1805 <<" from it's previous block "<<PP(old_static_block)
1809 block->m_static_objects.remove(id);
1810 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1811 MOD_REASON_STATIC_DATA_REMOVED);
1815 // If block is active, don't remove
1816 if(!force_delete && m_active_blocks.contains(blockpos_o))
1819 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
1820 <<"deactivating object id="<<id<<" on inactive block "
1821 <<PP(blockpos_o)<<std::endl;
1823 // If known by some client, don't immediately delete.
1824 bool pending_delete = (obj->m_known_by_count > 0 && !force_delete);
1827 Update the static data
1830 if(obj->isStaticAllowed())
1832 // Create new static object
1833 std::string staticdata_new = obj->getStaticData();
1834 StaticObject s_obj(obj->getType(), objectpos, staticdata_new);
1836 bool stays_in_same_block = false;
1837 bool data_changed = true;
1839 if (obj->m_static_exists) {
1840 if (obj->m_static_block == blockpos_o)
1841 stays_in_same_block = true;
1843 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
1846 std::map<u16, StaticObject>::iterator n =
1847 block->m_static_objects.m_active.find(id);
1848 if (n != block->m_static_objects.m_active.end()) {
1849 StaticObject static_old = n->second;
1851 float save_movem = obj->getMinimumSavedMovement();
1853 if (static_old.data == staticdata_new &&
1854 (static_old.pos - objectpos).getLength() < save_movem)
1855 data_changed = false;
1857 errorstream<<"ServerEnvironment::deactivateFarObjects(): "
1858 <<"id="<<id<<" m_static_exists=true but "
1859 <<"static data doesn't actually exist in "
1860 <<PP(obj->m_static_block)<<std::endl;
1865 bool shall_be_written = (!stays_in_same_block || data_changed);
1867 // Delete old static object
1868 if(obj->m_static_exists)
1870 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
1873 block->m_static_objects.remove(id);
1874 obj->m_static_exists = false;
1875 // Only mark block as modified if data changed considerably
1876 if(shall_be_written)
1877 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1878 MOD_REASON_STATIC_DATA_CHANGED);
1882 // Add to the block where the object is located in
1883 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1884 // Get or generate the block
1885 MapBlock *block = NULL;
1887 block = m_map->emergeBlock(blockpos);
1888 } catch(InvalidPositionException &e){
1889 // Handled via NULL pointer
1890 // NOTE: emergeBlock's failure is usually determined by it
1891 // actually returning NULL
1896 if(block->m_static_objects.m_stored.size() >= g_settings->getU16("max_objects_per_block")){
1897 errorstream<<"ServerEnv: Trying to store id="<<obj->getId()
1898 <<" statically but block "<<PP(blockpos)
1899 <<" already contains "
1900 <<block->m_static_objects.m_stored.size()
1902 <<" Forcing delete."<<std::endl;
1903 force_delete = true;
1905 // If static counterpart already exists in target block,
1907 // This shouldn't happen because the object is removed from
1908 // the previous block before this according to
1909 // obj->m_static_block, but happens rarely for some unknown
1910 // reason. Unsuccessful attempts have been made to find
1912 if(id && block->m_static_objects.m_active.find(id) != block->m_static_objects.m_active.end()){
1913 infostream<<"ServerEnv: WARNING: Performing hack #83274"
1915 block->m_static_objects.remove(id);
1917 // Store static data
1918 u16 store_id = pending_delete ? id : 0;
1919 block->m_static_objects.insert(store_id, s_obj);
1921 // Only mark block as modified if data changed considerably
1922 if(shall_be_written)
1923 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1924 MOD_REASON_STATIC_DATA_CHANGED);
1926 obj->m_static_exists = true;
1927 obj->m_static_block = block->getPos();
1932 v3s16 p = floatToInt(objectpos, BS);
1933 errorstream<<"ServerEnv: Could not find or generate "
1934 <<"a block for storing id="<<obj->getId()
1935 <<" statically (pos="<<PP(p)<<")"<<std::endl;
1942 If known by some client, set pending deactivation.
1943 Otherwise delete it immediately.
1946 if(pending_delete && !force_delete)
1948 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
1949 <<"object id="<<id<<" is known by clients"
1950 <<"; not deleting yet"<<std::endl;
1952 obj->m_pending_deactivation = true;
1956 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
1957 <<"object id="<<id<<" is not known by clients"
1958 <<"; deleting"<<std::endl;
1960 // Tell the object about removal
1961 obj->removingFromEnvironment();
1962 // Deregister in scripting api
1963 m_script->removeObjectReference(obj);
1965 // Delete active object
1966 if(obj->environmentDeletes())
1968 // Id to be removed from m_active_objects
1969 objects_to_remove.push_back(id);
1972 // Remove references from m_active_objects
1973 for(std::vector<u16>::iterator i = objects_to_remove.begin();
1974 i != objects_to_remove.end(); ++i) {
1975 m_active_objects.erase(*i);
1981 #include "clientsimpleobject.h"
1987 ClientEnvironment::ClientEnvironment(ClientMap *map, scene::ISceneManager *smgr,
1988 ITextureSource *texturesource, IGameDef *gamedef,
1989 IrrlichtDevice *irr):
1992 m_texturesource(texturesource),
1997 memset(attachement_parent_ids, zero, sizeof(attachement_parent_ids));
2000 ClientEnvironment::~ClientEnvironment()
2002 // delete active objects
2003 for(std::map<u16, ClientActiveObject*>::iterator
2004 i = m_active_objects.begin();
2005 i != m_active_objects.end(); ++i)
2010 for(std::vector<ClientSimpleObject*>::iterator
2011 i = m_simple_objects.begin(); i != m_simple_objects.end(); ++i) {
2019 Map & ClientEnvironment::getMap()
2024 ClientMap & ClientEnvironment::getClientMap()
2029 void ClientEnvironment::addPlayer(Player *player)
2031 DSTACK(__FUNCTION_NAME);
2033 It is a failure if player is local and there already is a local
2036 FATAL_ERROR_IF(player->isLocal() == true && getLocalPlayer() != NULL,
2037 "Player is local but there is already a local player");
2039 Environment::addPlayer(player);
2042 LocalPlayer * ClientEnvironment::getLocalPlayer()
2044 for(std::vector<Player*>::iterator i = m_players.begin();
2045 i != m_players.end(); ++i) {
2046 Player *player = *i;
2047 if(player->isLocal())
2048 return (LocalPlayer*)player;
2053 void ClientEnvironment::step(float dtime)
2055 DSTACK(__FUNCTION_NAME);
2057 /* Step time of day */
2058 stepTimeOfDay(dtime);
2060 // Get some settings
2061 bool fly_allowed = m_gamedef->checkLocalPrivilege("fly");
2062 bool free_move = fly_allowed && g_settings->getBool("free_move");
2065 LocalPlayer *lplayer = getLocalPlayer();
2067 // collision info queue
2068 std::vector<CollisionInfo> player_collisions;
2071 Get the speed the player is going
2073 bool is_climbing = lplayer->is_climbing;
2075 f32 player_speed = lplayer->getSpeed().getLength();
2078 Maximum position increment
2080 //f32 position_max_increment = 0.05*BS;
2081 f32 position_max_increment = 0.1*BS;
2083 // Maximum time increment (for collision detection etc)
2084 // time = distance / speed
2085 f32 dtime_max_increment = 1;
2086 if(player_speed > 0.001)
2087 dtime_max_increment = position_max_increment / player_speed;
2089 // Maximum time increment is 10ms or lower
2090 if(dtime_max_increment > 0.01)
2091 dtime_max_increment = 0.01;
2093 // Don't allow overly huge dtime
2097 f32 dtime_downcount = dtime;
2100 Stuff that has a maximum time increment
2109 if(dtime_downcount > dtime_max_increment)
2111 dtime_part = dtime_max_increment;
2112 dtime_downcount -= dtime_part;
2116 dtime_part = dtime_downcount;
2118 Setting this to 0 (no -=dtime_part) disables an infinite loop
2119 when dtime_part is so small that dtime_downcount -= dtime_part
2122 dtime_downcount = 0;
2131 if(free_move == false && is_climbing == false)
2134 v3f speed = lplayer->getSpeed();
2135 if(lplayer->in_liquid == false)
2136 speed.Y -= lplayer->movement_gravity * lplayer->physics_override_gravity * dtime_part * 2;
2138 // Liquid floating / sinking
2139 if(lplayer->in_liquid && !lplayer->swimming_vertical)
2140 speed.Y -= lplayer->movement_liquid_sink * dtime_part * 2;
2142 // Liquid resistance
2143 if(lplayer->in_liquid_stable || lplayer->in_liquid)
2145 // How much the node's viscosity blocks movement, ranges between 0 and 1
2146 // Should match the scale at which viscosity increase affects other liquid attributes
2147 const f32 viscosity_factor = 0.3;
2149 v3f d_wanted = -speed / lplayer->movement_liquid_fluidity;
2150 f32 dl = d_wanted.getLength();
2151 if(dl > lplayer->movement_liquid_fluidity_smooth)
2152 dl = lplayer->movement_liquid_fluidity_smooth;
2153 dl *= (lplayer->liquid_viscosity * viscosity_factor) + (1 - viscosity_factor);
2155 v3f d = d_wanted.normalize() * dl;
2159 lplayer->setSpeed(speed);
2164 This also does collision detection.
2166 lplayer->move(dtime_part, this, position_max_increment,
2167 &player_collisions);
2170 while(dtime_downcount > 0.001);
2172 //std::cout<<"Looped "<<loopcount<<" times."<<std::endl;
2174 for(std::vector<CollisionInfo>::iterator i = player_collisions.begin();
2175 i != player_collisions.end(); ++i) {
2176 CollisionInfo &info = *i;
2177 v3f speed_diff = info.new_speed - info.old_speed;;
2178 // Handle only fall damage
2179 // (because otherwise walking against something in fast_move kills you)
2180 if(speed_diff.Y < 0 || info.old_speed.Y >= 0)
2182 // Get rid of other components
2185 f32 pre_factor = 1; // 1 hp per node/s
2186 f32 tolerance = BS*14; // 5 without damage
2187 f32 post_factor = 1; // 1 hp per node/s
2188 if(info.type == COLLISION_NODE)
2190 const ContentFeatures &f = m_gamedef->ndef()->
2191 get(m_map->getNodeNoEx(info.node_p));
2192 // Determine fall damage multiplier
2193 int addp = itemgroup_get(f.groups, "fall_damage_add_percent");
2194 pre_factor = 1.0 + (float)addp/100.0;
2196 float speed = pre_factor * speed_diff.getLength();
2197 if(speed > tolerance)
2199 f32 damage_f = (speed - tolerance)/BS * post_factor;
2200 u16 damage = (u16)(damage_f+0.5);
2202 damageLocalPlayer(damage, true);
2203 MtEvent *e = new SimpleTriggerEvent("PlayerFallingDamage");
2204 m_gamedef->event()->put(e);
2210 A quick draft of lava damage
2212 if(m_lava_hurt_interval.step(dtime, 1.0))
2214 v3f pf = lplayer->getPosition();
2216 // Feet, middle and head
2217 v3s16 p1 = floatToInt(pf + v3f(0, BS*0.1, 0), BS);
2218 MapNode n1 = m_map->getNodeNoEx(p1);
2219 v3s16 p2 = floatToInt(pf + v3f(0, BS*0.8, 0), BS);
2220 MapNode n2 = m_map->getNodeNoEx(p2);
2221 v3s16 p3 = floatToInt(pf + v3f(0, BS*1.6, 0), BS);
2222 MapNode n3 = m_map->getNodeNoEx(p3);
2224 u32 damage_per_second = 0;
2225 damage_per_second = MYMAX(damage_per_second,
2226 m_gamedef->ndef()->get(n1).damage_per_second);
2227 damage_per_second = MYMAX(damage_per_second,
2228 m_gamedef->ndef()->get(n2).damage_per_second);
2229 damage_per_second = MYMAX(damage_per_second,
2230 m_gamedef->ndef()->get(n3).damage_per_second);
2232 if(damage_per_second != 0)
2234 damageLocalPlayer(damage_per_second, true);
2241 if(m_drowning_interval.step(dtime, 2.0))
2243 v3f pf = lplayer->getPosition();
2246 v3s16 p = floatToInt(pf + v3f(0, BS*1.6, 0), BS);
2247 MapNode n = m_map->getNodeNoEx(p);
2248 ContentFeatures c = m_gamedef->ndef()->get(n);
2249 u8 drowning_damage = c.drowning;
2250 if(drowning_damage > 0 && lplayer->hp > 0){
2251 u16 breath = lplayer->getBreath();
2258 lplayer->setBreath(breath);
2259 updateLocalPlayerBreath(breath);
2262 if(lplayer->getBreath() == 0 && drowning_damage > 0){
2263 damageLocalPlayer(drowning_damage, true);
2266 if(m_breathing_interval.step(dtime, 0.5))
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);
2275 lplayer->setBreath(11);
2277 else if(c.drowning == 0){
2278 u16 breath = lplayer->getBreath();
2281 lplayer->setBreath(breath);
2282 updateLocalPlayerBreath(breath);
2288 Stuff that can be done in an arbitarily large dtime
2290 for(std::vector<Player*>::iterator i = m_players.begin();
2291 i != m_players.end(); ++i) {
2292 Player *player = *i;
2295 Handle non-local players
2297 if(player->isLocal() == false) {
2299 player->move(dtime, this, 100*BS);
2304 // Update lighting on local player (used for wield item)
2305 u32 day_night_ratio = getDayNightRatio();
2309 // On InvalidPositionException, use this as default
2310 // (day: LIGHT_SUN, night: 0)
2311 MapNode node_at_lplayer(CONTENT_AIR, 0x0f, 0);
2313 v3s16 p = lplayer->getLightPosition();
2314 node_at_lplayer = m_map->getNodeNoEx(p);
2316 u16 light = getInteriorLight(node_at_lplayer, 0, m_gamedef->ndef());
2317 u8 day = light & 0xff;
2318 u8 night = (light >> 8) & 0xff;
2319 finalColorBlend(lplayer->light_color, day, night, day_night_ratio);
2323 Step active objects and update lighting of them
2326 g_profiler->avg("CEnv: num of objects", m_active_objects.size());
2327 bool update_lighting = m_active_object_light_update_interval.step(dtime, 0.21);
2328 for(std::map<u16, ClientActiveObject*>::iterator
2329 i = m_active_objects.begin();
2330 i != m_active_objects.end(); ++i)
2332 ClientActiveObject* obj = i->second;
2334 obj->step(dtime, this);
2343 v3s16 p = obj->getLightPosition();
2344 MapNode n = m_map->getNodeNoEx(p, &pos_ok);
2346 light = n.getLightBlend(day_night_ratio, m_gamedef->ndef());
2348 light = blend_light(day_night_ratio, LIGHT_SUN, 0);
2350 obj->updateLight(light);
2355 Step and handle simple objects
2357 g_profiler->avg("CEnv: num of simple objects", m_simple_objects.size());
2358 for(std::vector<ClientSimpleObject*>::iterator
2359 i = m_simple_objects.begin(); i != m_simple_objects.end();) {
2360 std::vector<ClientSimpleObject*>::iterator cur = i;
2361 ClientSimpleObject *simple = *cur;
2363 simple->step(dtime);
2364 if(simple->m_to_be_removed) {
2366 i = m_simple_objects.erase(cur);
2374 void ClientEnvironment::addSimpleObject(ClientSimpleObject *simple)
2376 m_simple_objects.push_back(simple);
2379 GenericCAO* ClientEnvironment::getGenericCAO(u16 id)
2381 ClientActiveObject *obj = getActiveObject(id);
2382 if (obj && obj->getType() == ACTIVEOBJECT_TYPE_GENERIC)
2383 return (GenericCAO*) obj;
2388 ClientActiveObject* ClientEnvironment::getActiveObject(u16 id)
2390 std::map<u16, ClientActiveObject*>::iterator n;
2391 n = m_active_objects.find(id);
2392 if(n == m_active_objects.end())
2397 bool isFreeClientActiveObjectId(u16 id,
2398 std::map<u16, ClientActiveObject*> &objects)
2403 return objects.find(id) == objects.end();
2406 u16 getFreeClientActiveObjectId(
2407 std::map<u16, ClientActiveObject*> &objects)
2409 //try to reuse id's as late as possible
2410 static u16 last_used_id = 0;
2411 u16 startid = last_used_id;
2415 if(isFreeClientActiveObjectId(last_used_id, objects))
2416 return last_used_id;
2418 if(last_used_id == startid)
2423 u16 ClientEnvironment::addActiveObject(ClientActiveObject *object)
2425 assert(object); // Pre-condition
2426 if(object->getId() == 0)
2428 u16 new_id = getFreeClientActiveObjectId(m_active_objects);
2431 infostream<<"ClientEnvironment::addActiveObject(): "
2432 <<"no free ids available"<<std::endl;
2436 object->setId(new_id);
2438 if(isFreeClientActiveObjectId(object->getId(), m_active_objects) == false)
2440 infostream<<"ClientEnvironment::addActiveObject(): "
2441 <<"id is not free ("<<object->getId()<<")"<<std::endl;
2445 infostream<<"ClientEnvironment::addActiveObject(): "
2446 <<"added (id="<<object->getId()<<")"<<std::endl;
2447 m_active_objects[object->getId()] = object;
2448 object->addToScene(m_smgr, m_texturesource, m_irr);
2449 { // Update lighting immediately
2454 v3s16 p = object->getLightPosition();
2455 MapNode n = m_map->getNodeNoEx(p, &pos_ok);
2457 light = n.getLightBlend(getDayNightRatio(), m_gamedef->ndef());
2459 light = blend_light(getDayNightRatio(), LIGHT_SUN, 0);
2461 object->updateLight(light);
2463 return object->getId();
2466 void ClientEnvironment::addActiveObject(u16 id, u8 type,
2467 const std::string &init_data)
2469 ClientActiveObject* obj =
2470 ClientActiveObject::create((ActiveObjectType) type, m_gamedef, this);
2473 infostream<<"ClientEnvironment::addActiveObject(): "
2474 <<"id="<<id<<" type="<<type<<": Couldn't create object"
2483 obj->initialize(init_data);
2485 catch(SerializationError &e)
2487 errorstream<<"ClientEnvironment::addActiveObject():"
2488 <<" id="<<id<<" type="<<type
2489 <<": SerializationError in initialize(): "
2491 <<": init_data="<<serializeJsonString(init_data)
2495 addActiveObject(obj);
2498 void ClientEnvironment::removeActiveObject(u16 id)
2500 verbosestream<<"ClientEnvironment::removeActiveObject(): "
2501 <<"id="<<id<<std::endl;
2502 ClientActiveObject* obj = getActiveObject(id);
2505 infostream<<"ClientEnvironment::removeActiveObject(): "
2506 <<"id="<<id<<" not found"<<std::endl;
2509 obj->removeFromScene(true);
2511 m_active_objects.erase(id);
2514 void ClientEnvironment::processActiveObjectMessage(u16 id, const std::string &data)
2516 ClientActiveObject *obj = getActiveObject(id);
2518 infostream << "ClientEnvironment::processActiveObjectMessage():"
2519 << " got message for id=" << id << ", which doesn't exist."
2525 obj->processMessage(data);
2526 } catch (SerializationError &e) {
2527 errorstream<<"ClientEnvironment::processActiveObjectMessage():"
2528 << " id=" << id << " type=" << obj->getType()
2529 << " SerializationError in processMessage(): " << e.what()
2535 Callbacks for activeobjects
2538 void ClientEnvironment::damageLocalPlayer(u8 damage, bool handle_hp)
2540 LocalPlayer *lplayer = getLocalPlayer();
2544 if (lplayer->hp > damage)
2545 lplayer->hp -= damage;
2550 ClientEnvEvent event;
2551 event.type = CEE_PLAYER_DAMAGE;
2552 event.player_damage.amount = damage;
2553 event.player_damage.send_to_server = handle_hp;
2554 m_client_event_queue.push(event);
2557 void ClientEnvironment::updateLocalPlayerBreath(u16 breath)
2559 ClientEnvEvent event;
2560 event.type = CEE_PLAYER_BREATH;
2561 event.player_breath.amount = breath;
2562 m_client_event_queue.push(event);
2566 Client likes to call these
2569 void ClientEnvironment::getActiveObjects(v3f origin, f32 max_d,
2570 std::vector<DistanceSortedActiveObject> &dest)
2572 for(std::map<u16, ClientActiveObject*>::iterator
2573 i = m_active_objects.begin();
2574 i != m_active_objects.end(); ++i)
2576 ClientActiveObject* obj = i->second;
2578 f32 d = (obj->getPosition() - origin).getLength();
2583 DistanceSortedActiveObject dso(obj, d);
2585 dest.push_back(dso);
2589 ClientEnvEvent ClientEnvironment::getClientEvent()
2591 ClientEnvEvent event;
2592 if(m_client_event_queue.empty())
2593 event.type = CEE_NONE;
2595 event = m_client_event_queue.front();
2596 m_client_event_queue.pop();
2601 #endif // #ifndef SERVER