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"
41 #include "daynightratio.h"
44 #include "util/serialize.h"
45 #include "jthread/jmutexautolock.h"
47 #define PP(x) "("<<(x).X<<","<<(x).Y<<","<<(x).Z<<")"
49 Environment::Environment():
51 m_time_of_day_f(9000./24000),
52 m_time_of_day_speed(0),
54 m_enable_day_night_ratio_override(false),
55 m_day_night_ratio_override(0.0f)
59 Environment::~Environment()
62 for(std::list<Player*>::iterator i = m_players.begin();
63 i != m_players.end(); ++i)
69 void Environment::addPlayer(Player *player)
71 DSTACK(__FUNCTION_NAME);
73 Check that peer_ids are unique.
74 Also check that names are unique.
75 Exception: there can be multiple players with peer_id=0
77 // If peer id is non-zero, it has to be unique.
78 if(player->peer_id != 0)
79 assert(getPlayer(player->peer_id) == NULL);
80 // Name has to be unique.
81 assert(getPlayer(player->getName()) == NULL);
83 m_players.push_back(player);
86 void Environment::removePlayer(u16 peer_id)
88 DSTACK(__FUNCTION_NAME);
90 for(std::list<Player*>::iterator i = m_players.begin();
91 i != m_players.end();)
94 if(player->peer_id == peer_id) {
96 i = m_players.erase(i);
103 void Environment::removePlayer(const char *name)
105 for (std::list<Player*>::iterator it = m_players.begin();
106 it != m_players.end(); ++it) {
107 if (strcmp((*it)->getName(), name) == 0) {
115 Player * Environment::getPlayer(u16 peer_id)
117 for(std::list<Player*>::iterator i = m_players.begin();
118 i != m_players.end(); ++i)
121 if(player->peer_id == peer_id)
127 Player * Environment::getPlayer(const char *name)
129 for(std::list<Player*>::iterator i = m_players.begin();
130 i != m_players.end(); ++i)
133 if(strcmp(player->getName(), name) == 0)
139 Player * Environment::getRandomConnectedPlayer()
141 std::list<Player*> connected_players = getPlayers(true);
142 u32 chosen_one = myrand() % connected_players.size();
144 for(std::list<Player*>::iterator
145 i = connected_players.begin();
146 i != connected_players.end(); ++i)
158 Player * Environment::getNearestConnectedPlayer(v3f pos)
160 std::list<Player*> connected_players = getPlayers(true);
162 Player *nearest_player = NULL;
163 for(std::list<Player*>::iterator
164 i = connected_players.begin();
165 i != connected_players.end(); ++i)
168 f32 d = player->getPosition().getDistanceFrom(pos);
169 if(d < nearest_d || nearest_player == NULL)
172 nearest_player = player;
175 return nearest_player;
178 std::list<Player*> Environment::getPlayers()
183 std::list<Player*> Environment::getPlayers(bool ignore_disconnected)
185 std::list<Player*> newlist;
186 for(std::list<Player*>::iterator
187 i = m_players.begin();
188 i != m_players.end(); ++i)
192 if(ignore_disconnected)
194 // Ignore disconnected players
195 if(player->peer_id == 0)
199 newlist.push_back(player);
204 u32 Environment::getDayNightRatio()
206 if(m_enable_day_night_ratio_override)
207 return m_day_night_ratio_override;
208 bool smooth = g_settings->getBool("enable_shaders");
209 return time_to_daynight_ratio(m_time_of_day_f*24000, smooth);
212 void Environment::setTimeOfDaySpeed(float speed)
214 JMutexAutoLock(this->m_lock);
215 m_time_of_day_speed = speed;
218 float Environment::getTimeOfDaySpeed()
220 JMutexAutoLock(this->m_lock);
221 float retval = m_time_of_day_speed;
225 void Environment::stepTimeOfDay(float dtime)
229 JMutexAutoLock(this->m_lock);
230 day_speed = m_time_of_day_speed;
233 m_time_counter += dtime;
234 f32 speed = day_speed * 24000./(24.*3600);
235 u32 units = (u32)(m_time_counter*speed);
239 if(m_time_of_day + units >= 24000)
241 m_time_of_day = (m_time_of_day + units) % 24000;
243 m_time_of_day_f = (float)m_time_of_day / 24000.0;
246 m_time_counter -= (f32)units / speed;
249 m_time_of_day_f += day_speed/24/3600*dtime;
250 if(m_time_of_day_f > 1.0)
251 m_time_of_day_f -= 1.0;
252 if(m_time_of_day_f < 0.0)
253 m_time_of_day_f += 1.0;
261 ABMWithState::ABMWithState(ActiveBlockModifier *abm_):
265 // Initialize timer to random value to spread processing
266 float itv = abm->getTriggerInterval();
267 itv = MYMAX(0.001, itv); // No less than 1ms
268 int minval = MYMAX(-0.51*itv, -60); // Clamp to
269 int maxval = MYMIN(0.51*itv, 60); // +-60 seconds
270 timer = myrand_range(minval, maxval);
277 void fillRadiusBlock(v3s16 p0, s16 r, std::set<v3s16> &list)
280 for(p.X=p0.X-r; p.X<=p0.X+r; p.X++)
281 for(p.Y=p0.Y-r; p.Y<=p0.Y+r; p.Y++)
282 for(p.Z=p0.Z-r; p.Z<=p0.Z+r; p.Z++)
289 void ActiveBlockList::update(std::list<v3s16> &active_positions,
291 std::set<v3s16> &blocks_removed,
292 std::set<v3s16> &blocks_added)
297 std::set<v3s16> newlist = m_forceloaded_list;
298 for(std::list<v3s16>::iterator i = active_positions.begin();
299 i != active_positions.end(); ++i)
301 fillRadiusBlock(*i, radius, newlist);
305 Find out which blocks on the old list are not on the new list
307 // Go through old list
308 for(std::set<v3s16>::iterator i = m_list.begin();
309 i != m_list.end(); ++i)
312 // If not on new list, it's been removed
313 if(newlist.find(p) == newlist.end())
314 blocks_removed.insert(p);
318 Find out which blocks on the new list are not on the old list
320 // Go through new list
321 for(std::set<v3s16>::iterator i = newlist.begin();
322 i != newlist.end(); ++i)
325 // If not on old list, it's been added
326 if(m_list.find(p) == m_list.end())
327 blocks_added.insert(p);
334 for(std::set<v3s16>::iterator i = newlist.begin();
335 i != newlist.end(); ++i)
346 ServerEnvironment::ServerEnvironment(ServerMap *map,
347 GameScripting *scriptIface, IGameDef *gamedef,
348 const std::string &path_world) :
350 m_script(scriptIface),
352 m_path_world(path_world),
353 m_send_recommended_timer(0),
354 m_active_block_interval_overload_skip(0),
356 m_game_time_fraction_counter(0),
357 m_recommended_send_interval(0.1),
358 m_max_lag_estimate(0.1)
362 ServerEnvironment::~ServerEnvironment()
364 // Clear active block list.
365 // This makes the next one delete all active objects.
366 m_active_blocks.clear();
368 // Convert all objects to static and delete the active objects
369 deactivateFarObjects(true);
374 // Delete ActiveBlockModifiers
375 for(std::list<ABMWithState>::iterator
376 i = m_abms.begin(); i != m_abms.end(); ++i){
381 Map & ServerEnvironment::getMap()
386 ServerMap & ServerEnvironment::getServerMap()
391 bool ServerEnvironment::line_of_sight(v3f pos1, v3f pos2, float stepsize, v3s16 *p)
393 float distance = pos1.getDistanceFrom(pos2);
395 //calculate normalized direction vector
396 v3f normalized_vector = v3f((pos2.X - pos1.X)/distance,
397 (pos2.Y - pos1.Y)/distance,
398 (pos2.Z - pos1.Z)/distance);
400 //find out if there's a node on path between pos1 and pos2
401 for (float i = 1; i < distance; i += stepsize) {
402 v3s16 pos = floatToInt(v3f(normalized_vector.X * i,
403 normalized_vector.Y * i,
404 normalized_vector.Z * i) +pos1,BS);
406 MapNode n = getMap().getNodeNoEx(pos);
408 if(n.param0 != CONTENT_AIR) {
418 void ServerEnvironment::saveLoadedPlayers()
420 std::string players_path = m_path_world + DIR_DELIM "players";
421 fs::CreateDir(players_path);
423 for (std::list<Player*>::iterator it = m_players.begin();
424 it != m_players.end();
426 RemotePlayer *player = static_cast<RemotePlayer*>(*it);
427 if (player->checkModified()) {
428 player->save(players_path);
433 void ServerEnvironment::savePlayer(const std::string &playername)
435 std::string players_path = m_path_world + DIR_DELIM "players";
436 fs::CreateDir(players_path);
438 RemotePlayer *player = static_cast<RemotePlayer*>(getPlayer(playername.c_str()));
440 player->save(players_path);
444 Player *ServerEnvironment::loadPlayer(const std::string &playername)
446 std::string players_path = m_path_world + DIR_DELIM "players" DIR_DELIM;
448 RemotePlayer *player = static_cast<RemotePlayer*>(getPlayer(playername.c_str()));
449 bool newplayer = false;
452 player = new RemotePlayer(m_gamedef, playername.c_str());
456 RemotePlayer testplayer(m_gamedef, "");
457 std::string path = players_path + playername;
458 for (u32 i = 0; i < PLAYER_FILE_ALTERNATE_TRIES; i++) {
459 // Open file and deserialize
460 std::ifstream is(path.c_str(), std::ios_base::binary);
464 testplayer.deSerialize(is, path);
466 if (testplayer.getName() == playername) {
467 *player = testplayer;
471 path = players_path + playername + itos(i);
474 infostream << "Player file for player " << playername
475 << " not found" << std::endl;
481 player->setModified(false);
485 void ServerEnvironment::saveMeta()
487 std::string path = m_path_world + DIR_DELIM "env_meta.txt";
489 // Open file and serialize
490 std::ostringstream ss(std::ios_base::binary);
493 args.setU64("game_time", m_game_time);
494 args.setU64("time_of_day", getTimeOfDay());
498 if(!fs::safeWriteToFile(path, ss.str()))
500 infostream<<"ServerEnvironment::saveMeta(): Failed to write "
502 throw SerializationError("Couldn't save env meta");
506 void ServerEnvironment::loadMeta()
508 std::string path = m_path_world + DIR_DELIM "env_meta.txt";
510 // Open file and deserialize
511 std::ifstream is(path.c_str(), std::ios_base::binary);
513 infostream << "ServerEnvironment::loadMeta(): Failed to open "
514 << path << std::endl;
515 throw SerializationError("Couldn't load env meta");
520 if (!args.parseConfigLines(is, "EnvArgsEnd")) {
521 throw SerializationError("ServerEnvironment::loadMeta(): "
522 "EnvArgsEnd not found!");
526 m_game_time = args.getU64("game_time");
527 } catch (SettingNotFoundException &e) {
528 // Getting this is crucial, otherwise timestamps are useless
529 throw SerializationError("Couldn't load env meta game_time");
533 m_time_of_day = args.getU64("time_of_day");
534 } catch (SettingNotFoundException &e) {
535 // This is not as important
536 m_time_of_day = 9000;
542 ActiveBlockModifier *abm;
544 std::set<content_t> required_neighbors;
550 ServerEnvironment *m_env;
551 std::map<content_t, std::list<ActiveABM> > m_aabms;
553 ABMHandler(std::list<ABMWithState> &abms,
554 float dtime_s, ServerEnvironment *env,
560 INodeDefManager *ndef = env->getGameDef()->ndef();
561 for(std::list<ABMWithState>::iterator
562 i = abms.begin(); i != abms.end(); ++i){
563 ActiveBlockModifier *abm = i->abm;
564 float trigger_interval = abm->getTriggerInterval();
565 if(trigger_interval < 0.001)
566 trigger_interval = 0.001;
567 float actual_interval = dtime_s;
570 if(i->timer < trigger_interval)
572 i->timer -= trigger_interval;
573 actual_interval = trigger_interval;
575 float intervals = actual_interval / trigger_interval;
578 float chance = abm->getTriggerChance();
583 aabm.chance = chance / intervals;
587 std::set<std::string> required_neighbors_s
588 = abm->getRequiredNeighbors();
589 for(std::set<std::string>::iterator
590 i = required_neighbors_s.begin();
591 i != required_neighbors_s.end(); i++)
593 ndef->getIds(*i, aabm.required_neighbors);
596 std::set<std::string> contents_s = abm->getTriggerContents();
597 for(std::set<std::string>::iterator
598 i = contents_s.begin(); i != contents_s.end(); i++)
600 std::set<content_t> ids;
601 ndef->getIds(*i, ids);
602 for(std::set<content_t>::const_iterator k = ids.begin();
606 std::map<content_t, std::list<ActiveABM> >::iterator j;
608 if(j == m_aabms.end()){
609 std::list<ActiveABM> aabmlist;
610 m_aabms[c] = aabmlist;
613 j->second.push_back(aabm);
618 // Find out how many objects the given block and its neighbours contain.
619 // Returns the number of objects in the block, and also in 'wider' the
620 // number of objects in the block and all its neighbours. The latter
621 // may an estimate if any neighbours are unloaded.
622 u32 countObjects(MapBlock *block, ServerMap * map, u32 &wider)
625 u32 wider_unknown_count = 0;
626 for(s16 x=-1; x<=1; x++)
627 for(s16 y=-1; y<=1; y++)
628 for(s16 z=-1; z<=1; z++)
630 MapBlock *block2 = map->getBlockNoCreateNoEx(
631 block->getPos() + v3s16(x,y,z));
633 wider_unknown_count++;
636 wider += block2->m_static_objects.m_active.size()
637 + block2->m_static_objects.m_stored.size();
640 u32 active_object_count = block->m_static_objects.m_active.size();
641 u32 wider_known_count = 3*3*3 - wider_unknown_count;
642 wider += wider_unknown_count * wider / wider_known_count;
643 return active_object_count;
646 void apply(MapBlock *block)
651 ServerMap *map = &m_env->getServerMap();
653 u32 active_object_count_wider;
654 u32 active_object_count = this->countObjects(block, map, active_object_count_wider);
655 m_env->m_added_objects = 0;
658 for(p0.X=0; p0.X<MAP_BLOCKSIZE; p0.X++)
659 for(p0.Y=0; p0.Y<MAP_BLOCKSIZE; p0.Y++)
660 for(p0.Z=0; p0.Z<MAP_BLOCKSIZE; p0.Z++)
662 MapNode n = block->getNodeNoEx(p0);
663 content_t c = n.getContent();
664 v3s16 p = p0 + block->getPosRelative();
666 std::map<content_t, std::list<ActiveABM> >::iterator j;
668 if(j == m_aabms.end())
671 for(std::list<ActiveABM>::iterator
672 i = j->second.begin(); i != j->second.end(); i++)
674 if(myrand() % i->chance != 0)
678 if(!i->required_neighbors.empty())
681 for(p1.X = p.X-1; p1.X <= p.X+1; p1.X++)
682 for(p1.Y = p.Y-1; p1.Y <= p.Y+1; p1.Y++)
683 for(p1.Z = p.Z-1; p1.Z <= p.Z+1; p1.Z++)
687 MapNode n = map->getNodeNoEx(p1);
688 content_t c = n.getContent();
689 std::set<content_t>::const_iterator k;
690 k = i->required_neighbors.find(c);
691 if(k != i->required_neighbors.end()){
695 // No required neighbor found
700 // Call all the trigger variations
701 i->abm->trigger(m_env, p, n);
702 i->abm->trigger(m_env, p, n,
703 active_object_count, active_object_count_wider);
705 // Count surrounding objects again if the abms added any
706 if(m_env->m_added_objects > 0) {
707 active_object_count = countObjects(block, map, active_object_count_wider);
708 m_env->m_added_objects = 0;
715 void ServerEnvironment::activateBlock(MapBlock *block, u32 additional_dtime)
717 // Reset usage timer immediately, otherwise a block that becomes active
718 // again at around the same time as it would normally be unloaded will
719 // get unloaded incorrectly. (I think this still leaves a small possibility
720 // of a race condition between this and server::AsyncRunStep, which only
721 // some kind of synchronisation will fix, but it at least reduces the window
722 // of opportunity for it to break from seconds to nanoseconds)
723 block->resetUsageTimer();
725 // Get time difference
727 u32 stamp = block->getTimestamp();
728 if(m_game_time > stamp && stamp != BLOCK_TIMESTAMP_UNDEFINED)
729 dtime_s = m_game_time - block->getTimestamp();
730 dtime_s += additional_dtime;
732 /*infostream<<"ServerEnvironment::activateBlock(): block timestamp: "
733 <<stamp<<", game time: "<<m_game_time<<std::endl;*/
735 // Set current time as timestamp
736 block->setTimestampNoChangedFlag(m_game_time);
738 /*infostream<<"ServerEnvironment::activateBlock(): block is "
739 <<dtime_s<<" seconds old."<<std::endl;*/
741 // Activate stored objects
742 activateObjects(block, dtime_s);
745 std::map<v3s16, NodeTimer> elapsed_timers =
746 block->m_node_timers.step((float)dtime_s);
747 if(!elapsed_timers.empty()){
749 for(std::map<v3s16, NodeTimer>::iterator
750 i = elapsed_timers.begin();
751 i != elapsed_timers.end(); i++){
752 n = block->getNodeNoEx(i->first);
753 v3s16 p = i->first + block->getPosRelative();
754 if(m_script->node_on_timer(p,n,i->second.elapsed))
755 block->setNodeTimer(i->first,NodeTimer(i->second.timeout,0));
759 /* Handle ActiveBlockModifiers */
760 ABMHandler abmhandler(m_abms, dtime_s, this, false);
761 abmhandler.apply(block);
764 void ServerEnvironment::addActiveBlockModifier(ActiveBlockModifier *abm)
766 m_abms.push_back(ABMWithState(abm));
769 bool ServerEnvironment::setNode(v3s16 p, const MapNode &n)
771 INodeDefManager *ndef = m_gamedef->ndef();
772 MapNode n_old = m_map->getNodeNoEx(p);
775 if (ndef->get(n_old).has_on_destruct)
776 m_script->node_on_destruct(p, n_old);
779 if (!m_map->addNodeWithEvent(p, n))
782 // Update active VoxelManipulator if a mapgen thread
783 m_map->updateVManip(p);
785 // Call post-destructor
786 if (ndef->get(n_old).has_after_destruct)
787 m_script->node_after_destruct(p, n_old);
790 if (ndef->get(n).has_on_construct)
791 m_script->node_on_construct(p, n);
796 bool ServerEnvironment::removeNode(v3s16 p)
798 INodeDefManager *ndef = m_gamedef->ndef();
799 MapNode n_old = m_map->getNodeNoEx(p);
802 if (ndef->get(n_old).has_on_destruct)
803 m_script->node_on_destruct(p, n_old);
806 // This is slightly optimized compared to addNodeWithEvent(air)
807 if (!m_map->removeNodeWithEvent(p))
810 // Update active VoxelManipulator if a mapgen thread
811 m_map->updateVManip(p);
813 // Call post-destructor
814 if (ndef->get(n_old).has_after_destruct)
815 m_script->node_after_destruct(p, n_old);
817 // Air doesn't require constructor
821 bool ServerEnvironment::swapNode(v3s16 p, const MapNode &n)
823 if (!m_map->addNodeWithEvent(p, n, false))
826 // Update active VoxelManipulator if a mapgen thread
827 m_map->updateVManip(p);
832 std::set<u16> ServerEnvironment::getObjectsInsideRadius(v3f pos, float radius)
834 std::set<u16> objects;
835 for(std::map<u16, ServerActiveObject*>::iterator
836 i = m_active_objects.begin();
837 i != m_active_objects.end(); ++i)
839 ServerActiveObject* obj = i->second;
841 v3f objectpos = obj->getBasePosition();
842 if(objectpos.getDistanceFrom(pos) > radius)
849 void ServerEnvironment::clearAllObjects()
851 infostream<<"ServerEnvironment::clearAllObjects(): "
852 <<"Removing all active objects"<<std::endl;
853 std::list<u16> objects_to_remove;
854 for(std::map<u16, ServerActiveObject*>::iterator
855 i = m_active_objects.begin();
856 i != m_active_objects.end(); ++i)
858 ServerActiveObject* obj = i->second;
859 if(obj->getType() == ACTIVEOBJECT_TYPE_PLAYER)
862 // Delete static object if block is loaded
863 if(obj->m_static_exists){
864 MapBlock *block = m_map->getBlockNoCreateNoEx(obj->m_static_block);
866 block->m_static_objects.remove(id);
867 block->raiseModified(MOD_STATE_WRITE_NEEDED,
869 obj->m_static_exists = false;
872 // If known by some client, don't delete immediately
873 if(obj->m_known_by_count > 0){
874 obj->m_pending_deactivation = true;
875 obj->m_removed = true;
879 // Tell the object about removal
880 obj->removingFromEnvironment();
881 // Deregister in scripting api
882 m_script->removeObjectReference(obj);
884 // Delete active object
885 if(obj->environmentDeletes())
887 // Id to be removed from m_active_objects
888 objects_to_remove.push_back(id);
890 // Remove references from m_active_objects
891 for(std::list<u16>::iterator i = objects_to_remove.begin();
892 i != objects_to_remove.end(); ++i)
894 m_active_objects.erase(*i);
897 // Get list of loaded blocks
898 std::list<v3s16> loaded_blocks;
899 infostream<<"ServerEnvironment::clearAllObjects(): "
900 <<"Listing all loaded blocks"<<std::endl;
901 m_map->listAllLoadedBlocks(loaded_blocks);
902 infostream<<"ServerEnvironment::clearAllObjects(): "
903 <<"Done listing all loaded blocks: "
904 <<loaded_blocks.size()<<std::endl;
906 // Get list of loadable blocks
907 std::list<v3s16> loadable_blocks;
908 infostream<<"ServerEnvironment::clearAllObjects(): "
909 <<"Listing all loadable blocks"<<std::endl;
910 m_map->listAllLoadableBlocks(loadable_blocks);
911 infostream<<"ServerEnvironment::clearAllObjects(): "
912 <<"Done listing all loadable blocks: "
913 <<loadable_blocks.size()
914 <<", now clearing"<<std::endl;
916 // Grab a reference on each loaded block to avoid unloading it
917 for(std::list<v3s16>::iterator i = loaded_blocks.begin();
918 i != loaded_blocks.end(); ++i)
921 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
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::list<v3s16>::iterator i = loadable_blocks.begin();
934 i != loadable_blocks.end(); ++i)
937 MapBlock *block = m_map->emergeBlock(p, false);
939 errorstream<<"ServerEnvironment::clearAllObjects(): "
940 <<"Failed to emerge block "<<PP(p)<<std::endl;
943 u32 num_stored = block->m_static_objects.m_stored.size();
944 u32 num_active = block->m_static_objects.m_active.size();
945 if(num_stored != 0 || num_active != 0){
946 block->m_static_objects.m_stored.clear();
947 block->m_static_objects.m_active.clear();
948 block->raiseModified(MOD_STATE_WRITE_NEEDED,
950 num_objs_cleared += num_stored + num_active;
951 num_blocks_cleared++;
953 num_blocks_checked++;
955 if(report_interval != 0 &&
956 num_blocks_checked % report_interval == 0){
957 float percent = 100.0 * (float)num_blocks_checked /
958 loadable_blocks.size();
959 infostream<<"ServerEnvironment::clearAllObjects(): "
960 <<"Cleared "<<num_objs_cleared<<" objects"
961 <<" in "<<num_blocks_cleared<<" blocks ("
962 <<percent<<"%)"<<std::endl;
964 if(num_blocks_checked % unload_interval == 0){
965 m_map->unloadUnreferencedBlocks();
968 m_map->unloadUnreferencedBlocks();
970 // Drop references that were added above
971 for(std::list<v3s16>::iterator i = loaded_blocks.begin();
972 i != loaded_blocks.end(); ++i)
975 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
980 infostream<<"ServerEnvironment::clearAllObjects(): "
981 <<"Finished: Cleared "<<num_objs_cleared<<" objects"
982 <<" in "<<num_blocks_cleared<<" blocks"<<std::endl;
985 void ServerEnvironment::step(float dtime)
987 DSTACK(__FUNCTION_NAME);
989 //TimeTaker timer("ServerEnv step");
991 /* Step time of day */
992 stepTimeOfDay(dtime);
995 // NOTE: This is kind of funny on a singleplayer game, but doesn't
996 // really matter that much.
997 m_recommended_send_interval = g_settings->getFloat("dedicated_server_step");
1003 m_game_time_fraction_counter += dtime;
1004 u32 inc_i = (u32)m_game_time_fraction_counter;
1005 m_game_time += inc_i;
1006 m_game_time_fraction_counter -= (float)inc_i;
1013 ScopeProfiler sp(g_profiler, "SEnv: handle players avg", SPT_AVG);
1014 for(std::list<Player*>::iterator i = m_players.begin();
1015 i != m_players.end(); ++i)
1017 Player *player = *i;
1019 // Ignore disconnected players
1020 if(player->peer_id == 0)
1024 player->move(dtime, this, 100*BS);
1029 Manage active block list
1031 if(m_active_blocks_management_interval.step(dtime, 2.0))
1033 ScopeProfiler sp(g_profiler, "SEnv: manage act. block list avg /2s", SPT_AVG);
1035 Get player block positions
1037 std::list<v3s16> players_blockpos;
1038 for(std::list<Player*>::iterator
1039 i = m_players.begin();
1040 i != m_players.end(); ++i)
1042 Player *player = *i;
1043 // Ignore disconnected players
1044 if(player->peer_id == 0)
1046 v3s16 blockpos = getNodeBlockPos(
1047 floatToInt(player->getPosition(), BS));
1048 players_blockpos.push_back(blockpos);
1052 Update list of active blocks, collecting changes
1054 const s16 active_block_range = g_settings->getS16("active_block_range");
1055 std::set<v3s16> blocks_removed;
1056 std::set<v3s16> blocks_added;
1057 m_active_blocks.update(players_blockpos, active_block_range,
1058 blocks_removed, blocks_added);
1061 Handle removed blocks
1064 // Convert active objects that are no more in active blocks to static
1065 deactivateFarObjects(false);
1067 for(std::set<v3s16>::iterator
1068 i = blocks_removed.begin();
1069 i != blocks_removed.end(); ++i)
1073 /* infostream<<"Server: Block " << PP(p)
1074 << " became inactive"<<std::endl; */
1076 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1080 // Set current time as timestamp (and let it set ChangedFlag)
1081 block->setTimestamp(m_game_time);
1088 for(std::set<v3s16>::iterator
1089 i = blocks_added.begin();
1090 i != blocks_added.end(); ++i)
1094 MapBlock *block = m_map->getBlockOrEmerge(p);
1096 m_active_blocks.m_list.erase(p);
1100 activateBlock(block);
1101 /* infostream<<"Server: Block " << PP(p)
1102 << " became active"<<std::endl; */
1107 Mess around in active blocks
1109 if(m_active_blocks_nodemetadata_interval.step(dtime, 1.0))
1111 ScopeProfiler sp(g_profiler, "SEnv: mess in act. blocks avg /1s", SPT_AVG);
1115 for(std::set<v3s16>::iterator
1116 i = m_active_blocks.m_list.begin();
1117 i != m_active_blocks.m_list.end(); ++i)
1121 /*infostream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
1122 <<") being handled"<<std::endl;*/
1124 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1128 // Reset block usage timer
1129 block->resetUsageTimer();
1131 // Set current time as timestamp
1132 block->setTimestampNoChangedFlag(m_game_time);
1133 // If time has changed much from the one on disk,
1134 // set block to be saved when it is unloaded
1135 if(block->getTimestamp() > block->getDiskTimestamp() + 60)
1136 block->raiseModified(MOD_STATE_WRITE_AT_UNLOAD,
1137 "Timestamp older than 60s (step)");
1140 std::map<v3s16, NodeTimer> elapsed_timers =
1141 block->m_node_timers.step((float)dtime);
1142 if(!elapsed_timers.empty()){
1144 for(std::map<v3s16, NodeTimer>::iterator
1145 i = elapsed_timers.begin();
1146 i != elapsed_timers.end(); i++){
1147 n = block->getNodeNoEx(i->first);
1148 p = i->first + block->getPosRelative();
1149 if(m_script->node_on_timer(p,n,i->second.elapsed))
1150 block->setNodeTimer(i->first,NodeTimer(i->second.timeout,0));
1156 const float abm_interval = 1.0;
1157 if(m_active_block_modifier_interval.step(dtime, abm_interval))
1159 if(m_active_block_interval_overload_skip > 0){
1160 ScopeProfiler sp(g_profiler, "SEnv: ABM overload skips");
1161 m_active_block_interval_overload_skip--;
1164 ScopeProfiler sp(g_profiler, "SEnv: modify in blocks avg /1s", SPT_AVG);
1165 TimeTaker timer("modify in active blocks");
1167 // Initialize handling of ActiveBlockModifiers
1168 ABMHandler abmhandler(m_abms, abm_interval, this, true);
1170 for(std::set<v3s16>::iterator
1171 i = m_active_blocks.m_list.begin();
1172 i != m_active_blocks.m_list.end(); ++i)
1176 /*infostream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
1177 <<") being handled"<<std::endl;*/
1179 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1183 // Set current time as timestamp
1184 block->setTimestampNoChangedFlag(m_game_time);
1186 /* Handle ActiveBlockModifiers */
1187 abmhandler.apply(block);
1190 u32 time_ms = timer.stop(true);
1191 u32 max_time_ms = 200;
1192 if(time_ms > max_time_ms){
1193 infostream<<"WARNING: active block modifiers took "
1194 <<time_ms<<"ms (longer than "
1195 <<max_time_ms<<"ms)"<<std::endl;
1196 m_active_block_interval_overload_skip = (time_ms / max_time_ms) + 1;
1201 Step script environment (run global on_step())
1203 m_script->environment_Step(dtime);
1209 ScopeProfiler sp(g_profiler, "SEnv: step act. objs avg", SPT_AVG);
1210 //TimeTaker timer("Step active objects");
1212 g_profiler->avg("SEnv: num of objects", m_active_objects.size());
1214 // This helps the objects to send data at the same time
1215 bool send_recommended = false;
1216 m_send_recommended_timer += dtime;
1217 if(m_send_recommended_timer > getSendRecommendedInterval())
1219 m_send_recommended_timer -= getSendRecommendedInterval();
1220 send_recommended = true;
1223 for(std::map<u16, ServerActiveObject*>::iterator
1224 i = m_active_objects.begin();
1225 i != m_active_objects.end(); ++i)
1227 ServerActiveObject* obj = i->second;
1228 // Remove non-peaceful mobs on peaceful mode
1229 if(g_settings->getBool("only_peaceful_mobs")){
1230 if(!obj->isPeaceful())
1231 obj->m_removed = true;
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,
1353 std::set<u16> ¤t_objects,
1354 std::set<u16> &added_objects)
1356 v3f pos_f = intToFloat(pos, BS);
1357 f32 radius_f = radius * BS;
1359 Go through the object list,
1360 - discard m_removed objects,
1361 - discard objects that are too far away,
1362 - discard objects that are found in current_objects.
1363 - add remaining objects to added_objects
1365 for(std::map<u16, ServerActiveObject*>::iterator
1366 i = m_active_objects.begin();
1367 i != m_active_objects.end(); ++i)
1371 ServerActiveObject *object = i->second;
1374 // Discard if removed or deactivating
1375 if(object->m_removed || object->m_pending_deactivation)
1377 if(object->unlimitedTransferDistance() == false){
1378 // Discard if too far
1379 f32 distance_f = object->getBasePosition().getDistanceFrom(pos_f);
1380 if(distance_f > radius_f)
1383 // Discard if already on current_objects
1384 std::set<u16>::iterator n;
1385 n = current_objects.find(id);
1386 if(n != current_objects.end())
1388 // Add to added_objects
1389 added_objects.insert(id);
1394 Finds out what objects have been removed from
1395 inside a radius around a position
1397 void ServerEnvironment::getRemovedActiveObjects(v3s16 pos, s16 radius,
1398 std::set<u16> ¤t_objects,
1399 std::set<u16> &removed_objects)
1401 v3f pos_f = intToFloat(pos, BS);
1402 f32 radius_f = radius * BS;
1404 Go through current_objects; object is removed if:
1405 - object is not found in m_active_objects (this is actually an
1406 error condition; objects should be set m_removed=true and removed
1407 only after all clients have been informed about removal), or
1408 - object has m_removed=true, or
1409 - object is too far away
1411 for(std::set<u16>::iterator
1412 i = current_objects.begin();
1413 i != current_objects.end(); ++i)
1416 ServerActiveObject *object = getActiveObject(id);
1419 infostream<<"ServerEnvironment::getRemovedActiveObjects():"
1420 <<" object in current_objects is NULL"<<std::endl;
1421 removed_objects.insert(id);
1425 if(object->m_removed || object->m_pending_deactivation)
1427 removed_objects.insert(id);
1431 // If transfer distance is unlimited, don't remove
1432 if(object->unlimitedTransferDistance())
1435 f32 distance_f = object->getBasePosition().getDistanceFrom(pos_f);
1437 if(distance_f >= radius_f)
1439 removed_objects.insert(id);
1447 ActiveObjectMessage ServerEnvironment::getActiveObjectMessage()
1449 if(m_active_object_messages.empty())
1450 return ActiveObjectMessage(0);
1452 ActiveObjectMessage message = m_active_object_messages.front();
1453 m_active_object_messages.pop_front();
1458 ************ Private methods *************
1461 u16 ServerEnvironment::addActiveObjectRaw(ServerActiveObject *object,
1462 bool set_changed, u32 dtime_s)
1465 if(object->getId() == 0){
1466 u16 new_id = getFreeServerActiveObjectId(m_active_objects);
1469 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1470 <<"no free ids available"<<std::endl;
1471 if(object->environmentDeletes())
1475 object->setId(new_id);
1478 verbosestream<<"ServerEnvironment::addActiveObjectRaw(): "
1479 <<"supplied with id "<<object->getId()<<std::endl;
1481 if(isFreeServerActiveObjectId(object->getId(), m_active_objects) == false)
1483 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1484 <<"id is not free ("<<object->getId()<<")"<<std::endl;
1485 if(object->environmentDeletes())
1489 /*infostream<<"ServerEnvironment::addActiveObjectRaw(): "
1490 <<"added (id="<<object->getId()<<")"<<std::endl;*/
1492 m_active_objects[object->getId()] = object;
1494 verbosestream<<"ServerEnvironment::addActiveObjectRaw(): "
1495 <<"Added id="<<object->getId()<<"; there are now "
1496 <<m_active_objects.size()<<" active objects."
1499 // Register reference in scripting api (must be done before post-init)
1500 m_script->addObjectReference(object);
1501 // Post-initialize object
1502 object->addedToEnvironment(dtime_s);
1504 // Add static data to block
1505 if(object->isStaticAllowed())
1507 // Add static object to active static list of the block
1508 v3f objectpos = object->getBasePosition();
1509 std::string staticdata = object->getStaticData();
1510 StaticObject s_obj(object->getType(), objectpos, staticdata);
1511 // Add to the block where the object is located in
1512 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1513 MapBlock *block = m_map->emergeBlock(blockpos);
1515 block->m_static_objects.m_active[object->getId()] = s_obj;
1516 object->m_static_exists = true;
1517 object->m_static_block = blockpos;
1520 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1521 "addActiveObjectRaw");
1523 v3s16 p = floatToInt(objectpos, BS);
1524 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1525 <<"could not emerge block for storing id="<<object->getId()
1526 <<" statically (pos="<<PP(p)<<")"<<std::endl;
1530 return object->getId();
1534 Remove objects that satisfy (m_removed && m_known_by_count==0)
1536 void ServerEnvironment::removeRemovedObjects()
1538 std::list<u16> objects_to_remove;
1539 for(std::map<u16, ServerActiveObject*>::iterator
1540 i = m_active_objects.begin();
1541 i != m_active_objects.end(); ++i)
1544 ServerActiveObject* obj = i->second;
1545 // This shouldn't happen but check it
1548 infostream<<"NULL object found in ServerEnvironment"
1549 <<" while finding removed objects. id="<<id<<std::endl;
1550 // Id to be removed from m_active_objects
1551 objects_to_remove.push_back(id);
1556 We will delete objects that are marked as removed or thatare
1557 waiting for deletion after deactivation
1559 if(obj->m_removed == false && obj->m_pending_deactivation == false)
1563 Delete static data from block if is marked as removed
1565 if(obj->m_static_exists && obj->m_removed)
1567 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
1569 block->m_static_objects.remove(id);
1570 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1571 "removeRemovedObjects/remove");
1572 obj->m_static_exists = false;
1574 infostream<<"Failed to emerge block from which an object to "
1575 <<"be removed was loaded from. id="<<id<<std::endl;
1579 // If m_known_by_count > 0, don't actually remove. On some future
1580 // invocation this will be 0, which is when removal will continue.
1581 if(obj->m_known_by_count > 0)
1585 Move static data from active to stored if not marked as removed
1587 if(obj->m_static_exists && !obj->m_removed){
1588 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
1590 std::map<u16, StaticObject>::iterator i =
1591 block->m_static_objects.m_active.find(id);
1592 if(i != block->m_static_objects.m_active.end()){
1593 block->m_static_objects.m_stored.push_back(i->second);
1594 block->m_static_objects.m_active.erase(id);
1595 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1596 "removeRemovedObjects/deactivate");
1599 infostream<<"Failed to emerge block from which an object to "
1600 <<"be deactivated was loaded from. id="<<id<<std::endl;
1604 // Tell the object about removal
1605 obj->removingFromEnvironment();
1606 // Deregister in scripting api
1607 m_script->removeObjectReference(obj);
1610 if(obj->environmentDeletes())
1612 // Id to be removed from m_active_objects
1613 objects_to_remove.push_back(id);
1615 // Remove references from m_active_objects
1616 for(std::list<u16>::iterator i = objects_to_remove.begin();
1617 i != objects_to_remove.end(); ++i)
1619 m_active_objects.erase(*i);
1623 static void print_hexdump(std::ostream &o, const std::string &data)
1625 const int linelength = 16;
1626 for(int l=0; ; l++){
1627 int i0 = linelength * l;
1628 bool at_end = false;
1629 int thislinelength = linelength;
1630 if(i0 + thislinelength > (int)data.size()){
1631 thislinelength = data.size() - i0;
1634 for(int di=0; di<linelength; di++){
1637 if(di<thislinelength)
1638 snprintf(buf, 4, "%.2x ", data[i]);
1640 snprintf(buf, 4, " ");
1644 for(int di=0; di<thislinelength; di++){
1658 Convert stored objects from blocks near the players to active.
1660 void ServerEnvironment::activateObjects(MapBlock *block, u32 dtime_s)
1664 // Ignore if no stored objects (to not set changed flag)
1665 if(block->m_static_objects.m_stored.size() == 0)
1667 verbosestream<<"ServerEnvironment::activateObjects(): "
1668 <<"activating objects of block "<<PP(block->getPos())
1669 <<" ("<<block->m_static_objects.m_stored.size()
1670 <<" objects)"<<std::endl;
1671 bool large_amount = (block->m_static_objects.m_stored.size() > g_settings->getU16("max_objects_per_block"));
1673 errorstream<<"suspiciously large amount of objects detected: "
1674 <<block->m_static_objects.m_stored.size()<<" in "
1675 <<PP(block->getPos())
1676 <<"; removing all of them."<<std::endl;
1677 // Clear stored list
1678 block->m_static_objects.m_stored.clear();
1679 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1680 "stored list cleared in activateObjects due to "
1681 "large amount of objects");
1685 // Activate stored objects
1686 std::list<StaticObject> new_stored;
1687 for(std::list<StaticObject>::iterator
1688 i = block->m_static_objects.m_stored.begin();
1689 i != block->m_static_objects.m_stored.end(); ++i)
1691 /*infostream<<"Server: Creating an active object from "
1692 <<"static data"<<std::endl;*/
1693 StaticObject &s_obj = *i;
1694 // Create an active object from the data
1695 ServerActiveObject *obj = ServerActiveObject::create
1696 (s_obj.type, this, 0, s_obj.pos, s_obj.data);
1697 // If couldn't create object, store static data back.
1700 errorstream<<"ServerEnvironment::activateObjects(): "
1701 <<"failed to create active object from static object "
1702 <<"in block "<<PP(s_obj.pos/BS)
1703 <<" type="<<(int)s_obj.type<<" data:"<<std::endl;
1704 print_hexdump(verbosestream, s_obj.data);
1706 new_stored.push_back(s_obj);
1709 verbosestream<<"ServerEnvironment::activateObjects(): "
1710 <<"activated static object pos="<<PP(s_obj.pos/BS)
1711 <<" type="<<(int)s_obj.type<<std::endl;
1712 // This will also add the object to the active static list
1713 addActiveObjectRaw(obj, false, dtime_s);
1715 // Clear stored list
1716 block->m_static_objects.m_stored.clear();
1717 // Add leftover failed stuff to stored list
1718 for(std::list<StaticObject>::iterator
1719 i = new_stored.begin();
1720 i != new_stored.end(); ++i)
1722 StaticObject &s_obj = *i;
1723 block->m_static_objects.m_stored.push_back(s_obj);
1726 // Turn the active counterparts of activated objects not pending for
1728 for(std::map<u16, StaticObject>::iterator
1729 i = block->m_static_objects.m_active.begin();
1730 i != block->m_static_objects.m_active.end(); ++i)
1733 ServerActiveObject *object = getActiveObject(id);
1735 object->m_pending_deactivation = false;
1739 Note: Block hasn't really been modified here.
1740 The objects have just been activated and moved from the stored
1741 static list to the active static list.
1742 As such, the block is essentially the same.
1743 Thus, do not call block->raiseModified(MOD_STATE_WRITE_NEEDED).
1744 Otherwise there would be a huge amount of unnecessary I/O.
1749 Convert objects that are not standing inside active blocks to static.
1751 If m_known_by_count != 0, active object is not deleted, but static
1752 data is still updated.
1754 If force_delete is set, active object is deleted nevertheless. It
1755 shall only be set so in the destructor of the environment.
1757 If block wasn't generated (not in memory or on disk),
1759 void ServerEnvironment::deactivateFarObjects(bool force_delete)
1761 std::list<u16> objects_to_remove;
1762 for(std::map<u16, ServerActiveObject*>::iterator
1763 i = m_active_objects.begin();
1764 i != m_active_objects.end(); ++i)
1766 ServerActiveObject* obj = i->second;
1769 // Do not deactivate if static data creation not allowed
1770 if(!force_delete && !obj->isStaticAllowed())
1773 // If pending deactivation, let removeRemovedObjects() do it
1774 if(!force_delete && obj->m_pending_deactivation)
1778 v3f objectpos = obj->getBasePosition();
1780 // The block in which the object resides in
1781 v3s16 blockpos_o = getNodeBlockPos(floatToInt(objectpos, BS));
1783 // If object's static data is stored in a deactivated block and object
1784 // is actually located in an active block, re-save to the block in
1785 // which the object is actually located in.
1787 obj->m_static_exists &&
1788 !m_active_blocks.contains(obj->m_static_block) &&
1789 m_active_blocks.contains(blockpos_o))
1791 v3s16 old_static_block = obj->m_static_block;
1793 // Save to block where object is located
1794 MapBlock *block = m_map->emergeBlock(blockpos_o, false);
1796 errorstream<<"ServerEnvironment::deactivateFarObjects(): "
1797 <<"Could not save object id="<<id
1798 <<" to it's current block "<<PP(blockpos_o)
1802 std::string staticdata_new = obj->getStaticData();
1803 StaticObject s_obj(obj->getType(), objectpos, staticdata_new);
1804 block->m_static_objects.insert(id, s_obj);
1805 obj->m_static_block = blockpos_o;
1806 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1807 "deactivateFarObjects: Static data moved in");
1809 // Delete from block where object was located
1810 block = m_map->emergeBlock(old_static_block, false);
1812 errorstream<<"ServerEnvironment::deactivateFarObjects(): "
1813 <<"Could not delete object id="<<id
1814 <<" from it's previous block "<<PP(old_static_block)
1818 block->m_static_objects.remove(id);
1819 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1820 "deactivateFarObjects: Static data moved out");
1824 // If block is active, don't remove
1825 if(!force_delete && m_active_blocks.contains(blockpos_o))
1828 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
1829 <<"deactivating object id="<<id<<" on inactive block "
1830 <<PP(blockpos_o)<<std::endl;
1832 // If known by some client, don't immediately delete.
1833 bool pending_delete = (obj->m_known_by_count > 0 && !force_delete);
1836 Update the static data
1839 if(obj->isStaticAllowed())
1841 // Create new static object
1842 std::string staticdata_new = obj->getStaticData();
1843 StaticObject s_obj(obj->getType(), objectpos, staticdata_new);
1845 bool stays_in_same_block = false;
1846 bool data_changed = true;
1848 if(obj->m_static_exists){
1849 if(obj->m_static_block == blockpos_o)
1850 stays_in_same_block = true;
1852 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
1854 std::map<u16, StaticObject>::iterator n =
1855 block->m_static_objects.m_active.find(id);
1856 if(n != block->m_static_objects.m_active.end()){
1857 StaticObject static_old = n->second;
1859 float save_movem = obj->getMinimumSavedMovement();
1861 if(static_old.data == staticdata_new &&
1862 (static_old.pos - objectpos).getLength() < save_movem)
1863 data_changed = false;
1865 errorstream<<"ServerEnvironment::deactivateFarObjects(): "
1866 <<"id="<<id<<" m_static_exists=true but "
1867 <<"static data doesn't actually exist in "
1868 <<PP(obj->m_static_block)<<std::endl;
1872 bool shall_be_written = (!stays_in_same_block || data_changed);
1874 // Delete old static object
1875 if(obj->m_static_exists)
1877 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
1880 block->m_static_objects.remove(id);
1881 obj->m_static_exists = false;
1882 // Only mark block as modified if data changed considerably
1883 if(shall_be_written)
1884 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1885 "deactivateFarObjects: Static data "
1886 "changed considerably");
1890 // Add to the block where the object is located in
1891 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1892 // Get or generate the block
1893 MapBlock *block = NULL;
1895 block = m_map->emergeBlock(blockpos);
1896 } catch(InvalidPositionException &e){
1897 // Handled via NULL pointer
1898 // NOTE: emergeBlock's failure is usually determined by it
1899 // actually returning NULL
1904 if(block->m_static_objects.m_stored.size() >= g_settings->getU16("max_objects_per_block")){
1905 errorstream<<"ServerEnv: Trying to store id="<<obj->getId()
1906 <<" statically but block "<<PP(blockpos)
1907 <<" already contains "
1908 <<block->m_static_objects.m_stored.size()
1910 <<" Forcing delete."<<std::endl;
1911 force_delete = true;
1913 // If static counterpart already exists in target block,
1915 // This shouldn't happen because the object is removed from
1916 // the previous block before this according to
1917 // obj->m_static_block, but happens rarely for some unknown
1918 // reason. Unsuccessful attempts have been made to find
1920 if(id && block->m_static_objects.m_active.find(id) != block->m_static_objects.m_active.end()){
1921 infostream<<"ServerEnv: WARNING: Performing hack #83274"
1923 block->m_static_objects.remove(id);
1925 // Store static data
1926 u16 store_id = pending_delete ? id : 0;
1927 block->m_static_objects.insert(store_id, s_obj);
1929 // Only mark block as modified if data changed considerably
1930 if(shall_be_written)
1931 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1932 "deactivateFarObjects: Static data "
1933 "changed considerably");
1935 obj->m_static_exists = true;
1936 obj->m_static_block = block->getPos();
1941 v3s16 p = floatToInt(objectpos, BS);
1942 errorstream<<"ServerEnv: Could not find or generate "
1943 <<"a block for storing id="<<obj->getId()
1944 <<" statically (pos="<<PP(p)<<")"<<std::endl;
1951 If known by some client, set pending deactivation.
1952 Otherwise delete it immediately.
1955 if(pending_delete && !force_delete)
1957 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
1958 <<"object id="<<id<<" is known by clients"
1959 <<"; not deleting yet"<<std::endl;
1961 obj->m_pending_deactivation = true;
1965 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
1966 <<"object id="<<id<<" is not known by clients"
1967 <<"; deleting"<<std::endl;
1969 // Tell the object about removal
1970 obj->removingFromEnvironment();
1971 // Deregister in scripting api
1972 m_script->removeObjectReference(obj);
1974 // Delete active object
1975 if(obj->environmentDeletes())
1977 // Id to be removed from m_active_objects
1978 objects_to_remove.push_back(id);
1981 // Remove references from m_active_objects
1982 for(std::list<u16>::iterator i = objects_to_remove.begin();
1983 i != objects_to_remove.end(); ++i)
1985 m_active_objects.erase(*i);
1992 #include "clientsimpleobject.h"
1998 ClientEnvironment::ClientEnvironment(ClientMap *map, scene::ISceneManager *smgr,
1999 ITextureSource *texturesource, IGameDef *gamedef,
2000 IrrlichtDevice *irr):
2003 m_texturesource(texturesource),
2008 memset(m_attachements, zero, sizeof(m_attachements));
2011 ClientEnvironment::~ClientEnvironment()
2013 // delete active objects
2014 for(std::map<u16, ClientActiveObject*>::iterator
2015 i = m_active_objects.begin();
2016 i != m_active_objects.end(); ++i)
2021 for(std::list<ClientSimpleObject*>::iterator
2022 i = m_simple_objects.begin(); i != m_simple_objects.end(); ++i)
2031 Map & ClientEnvironment::getMap()
2036 ClientMap & ClientEnvironment::getClientMap()
2041 void ClientEnvironment::addPlayer(Player *player)
2043 DSTACK(__FUNCTION_NAME);
2045 It is a failure if player is local and there already is a local
2048 assert(!(player->isLocal() == true && getLocalPlayer() != NULL));
2050 Environment::addPlayer(player);
2053 LocalPlayer * ClientEnvironment::getLocalPlayer()
2055 for(std::list<Player*>::iterator i = m_players.begin();
2056 i != m_players.end(); ++i)
2058 Player *player = *i;
2059 if(player->isLocal())
2060 return (LocalPlayer*)player;
2065 void ClientEnvironment::step(float dtime)
2067 DSTACK(__FUNCTION_NAME);
2069 /* Step time of day */
2070 stepTimeOfDay(dtime);
2072 // Get some settings
2073 bool fly_allowed = m_gamedef->checkLocalPrivilege("fly");
2074 bool free_move = fly_allowed && g_settings->getBool("free_move");
2077 LocalPlayer *lplayer = getLocalPlayer();
2079 // collision info queue
2080 std::list<CollisionInfo> player_collisions;
2083 Get the speed the player is going
2085 bool is_climbing = lplayer->is_climbing;
2087 f32 player_speed = lplayer->getSpeed().getLength();
2090 Maximum position increment
2092 //f32 position_max_increment = 0.05*BS;
2093 f32 position_max_increment = 0.1*BS;
2095 // Maximum time increment (for collision detection etc)
2096 // time = distance / speed
2097 f32 dtime_max_increment = 1;
2098 if(player_speed > 0.001)
2099 dtime_max_increment = position_max_increment / player_speed;
2101 // Maximum time increment is 10ms or lower
2102 if(dtime_max_increment > 0.01)
2103 dtime_max_increment = 0.01;
2105 // Don't allow overly huge dtime
2109 f32 dtime_downcount = dtime;
2112 Stuff that has a maximum time increment
2121 if(dtime_downcount > dtime_max_increment)
2123 dtime_part = dtime_max_increment;
2124 dtime_downcount -= dtime_part;
2128 dtime_part = dtime_downcount;
2130 Setting this to 0 (no -=dtime_part) disables an infinite loop
2131 when dtime_part is so small that dtime_downcount -= dtime_part
2134 dtime_downcount = 0;
2143 if(free_move == false && is_climbing == false)
2146 v3f speed = lplayer->getSpeed();
2147 if(lplayer->in_liquid == false)
2148 speed.Y -= lplayer->movement_gravity * lplayer->physics_override_gravity * dtime_part * 2;
2150 // Liquid floating / sinking
2151 if(lplayer->in_liquid && !lplayer->swimming_vertical)
2152 speed.Y -= lplayer->movement_liquid_sink * dtime_part * 2;
2154 // Liquid resistance
2155 if(lplayer->in_liquid_stable || lplayer->in_liquid)
2157 // How much the node's viscosity blocks movement, ranges between 0 and 1
2158 // Should match the scale at which viscosity increase affects other liquid attributes
2159 const f32 viscosity_factor = 0.3;
2161 v3f d_wanted = -speed / lplayer->movement_liquid_fluidity;
2162 f32 dl = d_wanted.getLength();
2163 if(dl > lplayer->movement_liquid_fluidity_smooth)
2164 dl = lplayer->movement_liquid_fluidity_smooth;
2165 dl *= (lplayer->liquid_viscosity * viscosity_factor) + (1 - viscosity_factor);
2167 v3f d = d_wanted.normalize() * dl;
2171 if(speed.X > lplayer->movement_liquid_fluidity + lplayer->movement_liquid_fluidity_smooth) speed.X -= lplayer->movement_liquid_fluidity_smooth;
2172 if(speed.X < -lplayer->movement_liquid_fluidity - lplayer->movement_liquid_fluidity_smooth) speed.X += lplayer->movement_liquid_fluidity_smooth;
2173 if(speed.Y > lplayer->movement_liquid_fluidity + lplayer->movement_liquid_fluidity_smooth) speed.Y -= lplayer->movement_liquid_fluidity_smooth;
2174 if(speed.Y < -lplayer->movement_liquid_fluidity - lplayer->movement_liquid_fluidity_smooth) speed.Y += lplayer->movement_liquid_fluidity_smooth;
2175 if(speed.Z > lplayer->movement_liquid_fluidity + lplayer->movement_liquid_fluidity_smooth) speed.Z -= lplayer->movement_liquid_fluidity_smooth;
2176 if(speed.Z < -lplayer->movement_liquid_fluidity - lplayer->movement_liquid_fluidity_smooth) speed.Z += lplayer->movement_liquid_fluidity_smooth;
2180 lplayer->setSpeed(speed);
2185 This also does collision detection.
2187 lplayer->move(dtime_part, this, position_max_increment,
2188 &player_collisions);
2191 while(dtime_downcount > 0.001);
2193 //std::cout<<"Looped "<<loopcount<<" times."<<std::endl;
2195 for(std::list<CollisionInfo>::iterator
2196 i = player_collisions.begin();
2197 i != player_collisions.end(); ++i)
2199 CollisionInfo &info = *i;
2200 v3f speed_diff = info.new_speed - info.old_speed;;
2201 // Handle only fall damage
2202 // (because otherwise walking against something in fast_move kills you)
2203 if(speed_diff.Y < 0 || info.old_speed.Y >= 0)
2205 // Get rid of other components
2208 f32 pre_factor = 1; // 1 hp per node/s
2209 f32 tolerance = BS*14; // 5 without damage
2210 f32 post_factor = 1; // 1 hp per node/s
2211 if(info.type == COLLISION_NODE)
2213 const ContentFeatures &f = m_gamedef->ndef()->
2214 get(m_map->getNodeNoEx(info.node_p));
2215 // Determine fall damage multiplier
2216 int addp = itemgroup_get(f.groups, "fall_damage_add_percent");
2217 pre_factor = 1.0 + (float)addp/100.0;
2219 float speed = pre_factor * speed_diff.getLength();
2220 if(speed > tolerance)
2222 f32 damage_f = (speed - tolerance)/BS * post_factor;
2223 u16 damage = (u16)(damage_f+0.5);
2225 damageLocalPlayer(damage, true);
2226 MtEvent *e = new SimpleTriggerEvent("PlayerFallingDamage");
2227 m_gamedef->event()->put(e);
2233 A quick draft of lava damage
2235 if(m_lava_hurt_interval.step(dtime, 1.0))
2237 v3f pf = lplayer->getPosition();
2239 // Feet, middle and head
2240 v3s16 p1 = floatToInt(pf + v3f(0, BS*0.1, 0), BS);
2241 MapNode n1 = m_map->getNodeNoEx(p1);
2242 v3s16 p2 = floatToInt(pf + v3f(0, BS*0.8, 0), BS);
2243 MapNode n2 = m_map->getNodeNoEx(p2);
2244 v3s16 p3 = floatToInt(pf + v3f(0, BS*1.6, 0), BS);
2245 MapNode n3 = m_map->getNodeNoEx(p3);
2247 u32 damage_per_second = 0;
2248 damage_per_second = MYMAX(damage_per_second,
2249 m_gamedef->ndef()->get(n1).damage_per_second);
2250 damage_per_second = MYMAX(damage_per_second,
2251 m_gamedef->ndef()->get(n2).damage_per_second);
2252 damage_per_second = MYMAX(damage_per_second,
2253 m_gamedef->ndef()->get(n3).damage_per_second);
2255 if(damage_per_second != 0)
2257 damageLocalPlayer(damage_per_second, true);
2264 if(m_drowning_interval.step(dtime, 2.0))
2266 v3f pf = lplayer->getPosition();
2269 v3s16 p = floatToInt(pf + v3f(0, BS*1.6, 0), BS);
2270 MapNode n = m_map->getNodeNoEx(p);
2271 ContentFeatures c = m_gamedef->ndef()->get(n);
2272 u8 drowning_damage = c.drowning;
2273 if(drowning_damage > 0 && lplayer->hp > 0){
2274 u16 breath = lplayer->getBreath();
2281 lplayer->setBreath(breath);
2282 updateLocalPlayerBreath(breath);
2285 if(lplayer->getBreath() == 0 && drowning_damage > 0){
2286 damageLocalPlayer(drowning_damage, true);
2289 if(m_breathing_interval.step(dtime, 0.5))
2291 v3f pf = lplayer->getPosition();
2294 v3s16 p = floatToInt(pf + v3f(0, BS*1.6, 0), BS);
2295 MapNode n = m_map->getNodeNoEx(p);
2296 ContentFeatures c = m_gamedef->ndef()->get(n);
2298 lplayer->setBreath(11);
2300 else if(c.drowning == 0){
2301 u16 breath = lplayer->getBreath();
2304 lplayer->setBreath(breath);
2305 updateLocalPlayerBreath(breath);
2311 Stuff that can be done in an arbitarily large dtime
2313 for(std::list<Player*>::iterator i = m_players.begin();
2314 i != m_players.end(); ++i)
2316 Player *player = *i;
2319 Handle non-local players
2321 if(player->isLocal() == false)
2324 player->move(dtime, this, 100*BS);
2328 // Update lighting on all players on client
2332 v3s16 p = player->getLightPosition();
2333 MapNode n = m_map->getNode(p);
2334 light = n.getLightBlendF1((float)getDayNightRatio()/1000, m_gamedef->ndef());
2336 catch(InvalidPositionException &e){
2337 light = blend_light_f1((float)getDayNightRatio()/1000, LIGHT_SUN, 0);
2339 player->light = light;
2343 Step active objects and update lighting of them
2346 g_profiler->avg("CEnv: num of objects", m_active_objects.size());
2347 bool update_lighting = m_active_object_light_update_interval.step(dtime, 0.21);
2348 for(std::map<u16, ClientActiveObject*>::iterator
2349 i = m_active_objects.begin();
2350 i != m_active_objects.end(); ++i)
2352 ClientActiveObject* obj = i->second;
2354 obj->step(dtime, this);
2362 v3s16 p = obj->getLightPosition();
2363 MapNode n = m_map->getNode(p);
2364 light = n.getLightBlend(getDayNightRatio(), m_gamedef->ndef());
2366 catch(InvalidPositionException &e){
2367 light = blend_light(getDayNightRatio(), LIGHT_SUN, 0);
2369 obj->updateLight(light);
2374 Step and handle simple objects
2376 g_profiler->avg("CEnv: num of simple objects", m_simple_objects.size());
2377 for(std::list<ClientSimpleObject*>::iterator
2378 i = m_simple_objects.begin(); i != m_simple_objects.end();)
2380 ClientSimpleObject *simple = *i;
2381 std::list<ClientSimpleObject*>::iterator cur = i;
2383 simple->step(dtime);
2384 if(simple->m_to_be_removed){
2386 m_simple_objects.erase(cur);
2391 void ClientEnvironment::addSimpleObject(ClientSimpleObject *simple)
2393 m_simple_objects.push_back(simple);
2396 ClientActiveObject* ClientEnvironment::getActiveObject(u16 id)
2398 std::map<u16, ClientActiveObject*>::iterator n;
2399 n = m_active_objects.find(id);
2400 if(n == m_active_objects.end())
2405 bool isFreeClientActiveObjectId(u16 id,
2406 std::map<u16, ClientActiveObject*> &objects)
2411 return objects.find(id) == objects.end();
2414 u16 getFreeClientActiveObjectId(
2415 std::map<u16, ClientActiveObject*> &objects)
2417 //try to reuse id's as late as possible
2418 static u16 last_used_id = 0;
2419 u16 startid = last_used_id;
2423 if(isFreeClientActiveObjectId(last_used_id, objects))
2424 return last_used_id;
2426 if(last_used_id == startid)
2431 u16 ClientEnvironment::addActiveObject(ClientActiveObject *object)
2434 if(object->getId() == 0)
2436 u16 new_id = getFreeClientActiveObjectId(m_active_objects);
2439 infostream<<"ClientEnvironment::addActiveObject(): "
2440 <<"no free ids available"<<std::endl;
2444 object->setId(new_id);
2446 if(isFreeClientActiveObjectId(object->getId(), m_active_objects) == false)
2448 infostream<<"ClientEnvironment::addActiveObject(): "
2449 <<"id is not free ("<<object->getId()<<")"<<std::endl;
2453 infostream<<"ClientEnvironment::addActiveObject(): "
2454 <<"added (id="<<object->getId()<<")"<<std::endl;
2455 m_active_objects[object->getId()] = object;
2456 object->addToScene(m_smgr, m_texturesource, m_irr);
2457 { // Update lighting immediately
2461 v3s16 p = object->getLightPosition();
2462 MapNode n = m_map->getNode(p);
2463 light = n.getLightBlend(getDayNightRatio(), m_gamedef->ndef());
2465 catch(InvalidPositionException &e){
2466 light = blend_light(getDayNightRatio(), LIGHT_SUN, 0);
2468 object->updateLight(light);
2470 return object->getId();
2473 void ClientEnvironment::addActiveObject(u16 id, u8 type,
2474 const std::string &init_data)
2476 ClientActiveObject* obj =
2477 ClientActiveObject::create(type, m_gamedef, this);
2480 infostream<<"ClientEnvironment::addActiveObject(): "
2481 <<"id="<<id<<" type="<<type<<": Couldn't create object"
2490 obj->initialize(init_data);
2492 catch(SerializationError &e)
2494 errorstream<<"ClientEnvironment::addActiveObject():"
2495 <<" id="<<id<<" type="<<type
2496 <<": SerializationError in initialize(): "
2498 <<": init_data="<<serializeJsonString(init_data)
2502 addActiveObject(obj);
2505 void ClientEnvironment::removeActiveObject(u16 id)
2507 verbosestream<<"ClientEnvironment::removeActiveObject(): "
2508 <<"id="<<id<<std::endl;
2509 ClientActiveObject* obj = getActiveObject(id);
2512 infostream<<"ClientEnvironment::removeActiveObject(): "
2513 <<"id="<<id<<" not found"<<std::endl;
2516 obj->removeFromScene(true);
2518 m_active_objects.erase(id);
2521 void ClientEnvironment::processActiveObjectMessage(u16 id,
2522 const std::string &data)
2524 ClientActiveObject* obj = getActiveObject(id);
2527 infostream<<"ClientEnvironment::processActiveObjectMessage():"
2528 <<" got message for id="<<id<<", which doesn't exist."
2534 obj->processMessage(data);
2536 catch(SerializationError &e)
2538 errorstream<<"ClientEnvironment::processActiveObjectMessage():"
2539 <<" id="<<id<<" type="<<obj->getType()
2540 <<" SerializationError in processMessage(),"
2541 <<" message="<<serializeJsonString(data)
2547 Callbacks for activeobjects
2550 void ClientEnvironment::damageLocalPlayer(u8 damage, bool handle_hp)
2552 LocalPlayer *lplayer = getLocalPlayer();
2556 if(lplayer->hp > damage)
2557 lplayer->hp -= damage;
2562 ClientEnvEvent event;
2563 event.type = CEE_PLAYER_DAMAGE;
2564 event.player_damage.amount = damage;
2565 event.player_damage.send_to_server = handle_hp;
2566 m_client_event_queue.push_back(event);
2569 void ClientEnvironment::updateLocalPlayerBreath(u16 breath)
2571 ClientEnvEvent event;
2572 event.type = CEE_PLAYER_BREATH;
2573 event.player_breath.amount = breath;
2574 m_client_event_queue.push_back(event);
2578 Client likes to call these
2581 void ClientEnvironment::getActiveObjects(v3f origin, f32 max_d,
2582 std::vector<DistanceSortedActiveObject> &dest)
2584 for(std::map<u16, ClientActiveObject*>::iterator
2585 i = m_active_objects.begin();
2586 i != m_active_objects.end(); ++i)
2588 ClientActiveObject* obj = i->second;
2590 f32 d = (obj->getPosition() - origin).getLength();
2595 DistanceSortedActiveObject dso(obj, d);
2597 dest.push_back(dso);
2601 ClientEnvEvent ClientEnvironment::getClientEvent()
2603 ClientEnvEvent event;
2604 if(m_client_event_queue.empty())
2605 event.type = CEE_NONE;
2607 event = m_client_event_queue.front();
2608 m_client_event_queue.pop_front();
2613 #endif // #ifndef SERVER