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 // Don't step if is to be removed or stored statically
1229 if(obj->m_removed || obj->m_pending_deactivation)
1232 obj->step(dtime, send_recommended);
1233 // Read messages from object
1234 while(!obj->m_messages_out.empty())
1236 m_active_object_messages.push_back(
1237 obj->m_messages_out.pop_front());
1243 Manage active objects
1245 if(m_object_management_interval.step(dtime, 0.5))
1247 ScopeProfiler sp(g_profiler, "SEnv: remove removed objs avg /.5s", SPT_AVG);
1249 Remove objects that satisfy (m_removed && m_known_by_count==0)
1251 removeRemovedObjects();
1255 ServerActiveObject* ServerEnvironment::getActiveObject(u16 id)
1257 std::map<u16, ServerActiveObject*>::iterator n;
1258 n = m_active_objects.find(id);
1259 if(n == m_active_objects.end())
1264 bool isFreeServerActiveObjectId(u16 id,
1265 std::map<u16, ServerActiveObject*> &objects)
1270 return objects.find(id) == objects.end();
1273 u16 getFreeServerActiveObjectId(
1274 std::map<u16, ServerActiveObject*> &objects)
1276 //try to reuse id's as late as possible
1277 static u16 last_used_id = 0;
1278 u16 startid = last_used_id;
1282 if(isFreeServerActiveObjectId(last_used_id, objects))
1283 return last_used_id;
1285 if(last_used_id == startid)
1290 u16 ServerEnvironment::addActiveObject(ServerActiveObject *object)
1294 u16 id = addActiveObjectRaw(object, true, 0);
1299 bool ServerEnvironment::addActiveObjectAsStatic(ServerActiveObject *obj)
1303 v3f objectpos = obj->getBasePosition();
1305 // The block in which the object resides in
1306 v3s16 blockpos_o = getNodeBlockPos(floatToInt(objectpos, BS));
1309 Update the static data
1312 // Create new static object
1313 std::string staticdata = obj->getStaticData();
1314 StaticObject s_obj(obj->getType(), objectpos, staticdata);
1315 // Add to the block where the object is located in
1316 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1317 // Get or generate the block
1318 MapBlock *block = m_map->emergeBlock(blockpos);
1320 bool succeeded = false;
1324 block->m_static_objects.insert(0, s_obj);
1325 block->raiseModified(MOD_STATE_WRITE_AT_UNLOAD,
1326 "addActiveObjectAsStatic");
1330 infostream<<"ServerEnvironment::addActiveObjectAsStatic: "
1331 <<"Could not find or generate "
1332 <<"a block for storing static object"<<std::endl;
1336 if(obj->environmentDeletes())
1344 Finds out what new objects have been added to
1345 inside a radius around a position
1347 void ServerEnvironment::getAddedActiveObjects(v3s16 pos, s16 radius,
1349 std::set<u16> ¤t_objects,
1350 std::set<u16> &added_objects)
1352 v3f pos_f = intToFloat(pos, BS);
1353 f32 radius_f = radius * BS;
1354 f32 player_radius_f = player_radius * BS;
1356 if (player_radius_f < 0)
1357 player_radius_f = 0;
1360 Go through the object list,
1361 - discard m_removed objects,
1362 - discard objects that are too far away,
1363 - discard objects that are found in current_objects.
1364 - add remaining objects to added_objects
1366 for(std::map<u16, ServerActiveObject*>::iterator
1367 i = m_active_objects.begin();
1368 i != m_active_objects.end(); ++i)
1372 ServerActiveObject *object = i->second;
1375 // Discard if removed or deactivating
1376 if(object->m_removed || object->m_pending_deactivation)
1379 f32 distance_f = object->getBasePosition().getDistanceFrom(pos_f);
1380 if (object->getType() == ACTIVEOBJECT_TYPE_PLAYER) {
1381 // Discard if too far
1382 if (distance_f > player_radius_f && player_radius_f != 0)
1384 } else if (distance_f > radius_f)
1387 // Discard if already on current_objects
1388 std::set<u16>::iterator n;
1389 n = current_objects.find(id);
1390 if(n != current_objects.end())
1392 // Add to added_objects
1393 added_objects.insert(id);
1398 Finds out what objects have been removed from
1399 inside a radius around a position
1401 void ServerEnvironment::getRemovedActiveObjects(v3s16 pos, s16 radius,
1403 std::set<u16> ¤t_objects,
1404 std::set<u16> &removed_objects)
1406 v3f pos_f = intToFloat(pos, BS);
1407 f32 radius_f = radius * BS;
1408 f32 player_radius_f = player_radius * BS;
1410 if (player_radius_f < 0)
1411 player_radius_f = 0;
1414 Go through current_objects; object is removed if:
1415 - object is not found in m_active_objects (this is actually an
1416 error condition; objects should be set m_removed=true and removed
1417 only after all clients have been informed about removal), or
1418 - object has m_removed=true, or
1419 - object is too far away
1421 for(std::set<u16>::iterator
1422 i = current_objects.begin();
1423 i != current_objects.end(); ++i)
1426 ServerActiveObject *object = getActiveObject(id);
1429 infostream<<"ServerEnvironment::getRemovedActiveObjects():"
1430 <<" object in current_objects is NULL"<<std::endl;
1431 removed_objects.insert(id);
1435 if(object->m_removed || object->m_pending_deactivation)
1437 removed_objects.insert(id);
1441 f32 distance_f = object->getBasePosition().getDistanceFrom(pos_f);
1442 if (object->getType() == ACTIVEOBJECT_TYPE_PLAYER) {
1443 if (distance_f <= player_radius_f || player_radius_f == 0)
1445 } else if (distance_f <= radius_f)
1448 // Object is no longer visible
1449 removed_objects.insert(id);
1453 ActiveObjectMessage ServerEnvironment::getActiveObjectMessage()
1455 if(m_active_object_messages.empty())
1456 return ActiveObjectMessage(0);
1458 ActiveObjectMessage message = m_active_object_messages.front();
1459 m_active_object_messages.pop_front();
1464 ************ Private methods *************
1467 u16 ServerEnvironment::addActiveObjectRaw(ServerActiveObject *object,
1468 bool set_changed, u32 dtime_s)
1471 if(object->getId() == 0){
1472 u16 new_id = getFreeServerActiveObjectId(m_active_objects);
1475 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1476 <<"no free ids available"<<std::endl;
1477 if(object->environmentDeletes())
1481 object->setId(new_id);
1484 verbosestream<<"ServerEnvironment::addActiveObjectRaw(): "
1485 <<"supplied with id "<<object->getId()<<std::endl;
1487 if(isFreeServerActiveObjectId(object->getId(), m_active_objects) == false)
1489 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1490 <<"id is not free ("<<object->getId()<<")"<<std::endl;
1491 if(object->environmentDeletes())
1495 /*infostream<<"ServerEnvironment::addActiveObjectRaw(): "
1496 <<"added (id="<<object->getId()<<")"<<std::endl;*/
1498 m_active_objects[object->getId()] = object;
1500 verbosestream<<"ServerEnvironment::addActiveObjectRaw(): "
1501 <<"Added id="<<object->getId()<<"; there are now "
1502 <<m_active_objects.size()<<" active objects."
1505 // Register reference in scripting api (must be done before post-init)
1506 m_script->addObjectReference(object);
1507 // Post-initialize object
1508 object->addedToEnvironment(dtime_s);
1510 // Add static data to block
1511 if(object->isStaticAllowed())
1513 // Add static object to active static list of the block
1514 v3f objectpos = object->getBasePosition();
1515 std::string staticdata = object->getStaticData();
1516 StaticObject s_obj(object->getType(), objectpos, staticdata);
1517 // Add to the block where the object is located in
1518 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1519 MapBlock *block = m_map->emergeBlock(blockpos);
1521 block->m_static_objects.m_active[object->getId()] = s_obj;
1522 object->m_static_exists = true;
1523 object->m_static_block = blockpos;
1526 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1527 "addActiveObjectRaw");
1529 v3s16 p = floatToInt(objectpos, BS);
1530 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1531 <<"could not emerge block for storing id="<<object->getId()
1532 <<" statically (pos="<<PP(p)<<")"<<std::endl;
1536 return object->getId();
1540 Remove objects that satisfy (m_removed && m_known_by_count==0)
1542 void ServerEnvironment::removeRemovedObjects()
1544 std::list<u16> objects_to_remove;
1545 for(std::map<u16, ServerActiveObject*>::iterator
1546 i = m_active_objects.begin();
1547 i != m_active_objects.end(); ++i)
1550 ServerActiveObject* obj = i->second;
1551 // This shouldn't happen but check it
1554 infostream<<"NULL object found in ServerEnvironment"
1555 <<" while finding removed objects. id="<<id<<std::endl;
1556 // Id to be removed from m_active_objects
1557 objects_to_remove.push_back(id);
1562 We will delete objects that are marked as removed or thatare
1563 waiting for deletion after deactivation
1565 if(obj->m_removed == false && obj->m_pending_deactivation == false)
1569 Delete static data from block if is marked as removed
1571 if(obj->m_static_exists && obj->m_removed)
1573 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
1575 block->m_static_objects.remove(id);
1576 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1577 "removeRemovedObjects/remove");
1578 obj->m_static_exists = false;
1580 infostream<<"Failed to emerge block from which an object to "
1581 <<"be removed was loaded from. id="<<id<<std::endl;
1585 // If m_known_by_count > 0, don't actually remove. On some future
1586 // invocation this will be 0, which is when removal will continue.
1587 if(obj->m_known_by_count > 0)
1591 Move static data from active to stored if not marked as removed
1593 if(obj->m_static_exists && !obj->m_removed){
1594 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
1596 std::map<u16, StaticObject>::iterator i =
1597 block->m_static_objects.m_active.find(id);
1598 if(i != block->m_static_objects.m_active.end()){
1599 block->m_static_objects.m_stored.push_back(i->second);
1600 block->m_static_objects.m_active.erase(id);
1601 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1602 "removeRemovedObjects/deactivate");
1605 infostream<<"Failed to emerge block from which an object to "
1606 <<"be deactivated was loaded from. id="<<id<<std::endl;
1610 // Tell the object about removal
1611 obj->removingFromEnvironment();
1612 // Deregister in scripting api
1613 m_script->removeObjectReference(obj);
1616 if(obj->environmentDeletes())
1618 // Id to be removed from m_active_objects
1619 objects_to_remove.push_back(id);
1621 // Remove references from m_active_objects
1622 for(std::list<u16>::iterator i = objects_to_remove.begin();
1623 i != objects_to_remove.end(); ++i)
1625 m_active_objects.erase(*i);
1629 static void print_hexdump(std::ostream &o, const std::string &data)
1631 const int linelength = 16;
1632 for(int l=0; ; l++){
1633 int i0 = linelength * l;
1634 bool at_end = false;
1635 int thislinelength = linelength;
1636 if(i0 + thislinelength > (int)data.size()){
1637 thislinelength = data.size() - i0;
1640 for(int di=0; di<linelength; di++){
1643 if(di<thislinelength)
1644 snprintf(buf, 4, "%.2x ", data[i]);
1646 snprintf(buf, 4, " ");
1650 for(int di=0; di<thislinelength; di++){
1664 Convert stored objects from blocks near the players to active.
1666 void ServerEnvironment::activateObjects(MapBlock *block, u32 dtime_s)
1670 // Ignore if no stored objects (to not set changed flag)
1671 if(block->m_static_objects.m_stored.size() == 0)
1673 verbosestream<<"ServerEnvironment::activateObjects(): "
1674 <<"activating objects of block "<<PP(block->getPos())
1675 <<" ("<<block->m_static_objects.m_stored.size()
1676 <<" objects)"<<std::endl;
1677 bool large_amount = (block->m_static_objects.m_stored.size() > g_settings->getU16("max_objects_per_block"));
1679 errorstream<<"suspiciously large amount of objects detected: "
1680 <<block->m_static_objects.m_stored.size()<<" in "
1681 <<PP(block->getPos())
1682 <<"; removing all of them."<<std::endl;
1683 // Clear stored list
1684 block->m_static_objects.m_stored.clear();
1685 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1686 "stored list cleared in activateObjects due to "
1687 "large amount of objects");
1691 // Activate stored objects
1692 std::list<StaticObject> new_stored;
1693 for(std::list<StaticObject>::iterator
1694 i = block->m_static_objects.m_stored.begin();
1695 i != block->m_static_objects.m_stored.end(); ++i)
1697 /*infostream<<"Server: Creating an active object from "
1698 <<"static data"<<std::endl;*/
1699 StaticObject &s_obj = *i;
1700 // Create an active object from the data
1701 ServerActiveObject *obj = ServerActiveObject::create
1702 (s_obj.type, this, 0, s_obj.pos, s_obj.data);
1703 // If couldn't create object, store static data back.
1706 errorstream<<"ServerEnvironment::activateObjects(): "
1707 <<"failed to create active object from static object "
1708 <<"in block "<<PP(s_obj.pos/BS)
1709 <<" type="<<(int)s_obj.type<<" data:"<<std::endl;
1710 print_hexdump(verbosestream, s_obj.data);
1712 new_stored.push_back(s_obj);
1715 verbosestream<<"ServerEnvironment::activateObjects(): "
1716 <<"activated static object pos="<<PP(s_obj.pos/BS)
1717 <<" type="<<(int)s_obj.type<<std::endl;
1718 // This will also add the object to the active static list
1719 addActiveObjectRaw(obj, false, dtime_s);
1721 // Clear stored list
1722 block->m_static_objects.m_stored.clear();
1723 // Add leftover failed stuff to stored list
1724 for(std::list<StaticObject>::iterator
1725 i = new_stored.begin();
1726 i != new_stored.end(); ++i)
1728 StaticObject &s_obj = *i;
1729 block->m_static_objects.m_stored.push_back(s_obj);
1732 // Turn the active counterparts of activated objects not pending for
1734 for(std::map<u16, StaticObject>::iterator
1735 i = block->m_static_objects.m_active.begin();
1736 i != block->m_static_objects.m_active.end(); ++i)
1739 ServerActiveObject *object = getActiveObject(id);
1741 object->m_pending_deactivation = false;
1745 Note: Block hasn't really been modified here.
1746 The objects have just been activated and moved from the stored
1747 static list to the active static list.
1748 As such, the block is essentially the same.
1749 Thus, do not call block->raiseModified(MOD_STATE_WRITE_NEEDED).
1750 Otherwise there would be a huge amount of unnecessary I/O.
1755 Convert objects that are not standing inside active blocks to static.
1757 If m_known_by_count != 0, active object is not deleted, but static
1758 data is still updated.
1760 If force_delete is set, active object is deleted nevertheless. It
1761 shall only be set so in the destructor of the environment.
1763 If block wasn't generated (not in memory or on disk),
1765 void ServerEnvironment::deactivateFarObjects(bool force_delete)
1767 std::list<u16> objects_to_remove;
1768 for(std::map<u16, ServerActiveObject*>::iterator
1769 i = m_active_objects.begin();
1770 i != m_active_objects.end(); ++i)
1772 ServerActiveObject* obj = i->second;
1775 // Do not deactivate if static data creation not allowed
1776 if(!force_delete && !obj->isStaticAllowed())
1779 // If pending deactivation, let removeRemovedObjects() do it
1780 if(!force_delete && obj->m_pending_deactivation)
1784 v3f objectpos = obj->getBasePosition();
1786 // The block in which the object resides in
1787 v3s16 blockpos_o = getNodeBlockPos(floatToInt(objectpos, BS));
1789 // If object's static data is stored in a deactivated block and object
1790 // is actually located in an active block, re-save to the block in
1791 // which the object is actually located in.
1793 obj->m_static_exists &&
1794 !m_active_blocks.contains(obj->m_static_block) &&
1795 m_active_blocks.contains(blockpos_o))
1797 v3s16 old_static_block = obj->m_static_block;
1799 // Save to block where object is located
1800 MapBlock *block = m_map->emergeBlock(blockpos_o, false);
1802 errorstream<<"ServerEnvironment::deactivateFarObjects(): "
1803 <<"Could not save object id="<<id
1804 <<" to it's current block "<<PP(blockpos_o)
1808 std::string staticdata_new = obj->getStaticData();
1809 StaticObject s_obj(obj->getType(), objectpos, staticdata_new);
1810 block->m_static_objects.insert(id, s_obj);
1811 obj->m_static_block = blockpos_o;
1812 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1813 "deactivateFarObjects: Static data moved in");
1815 // Delete from block where object was located
1816 block = m_map->emergeBlock(old_static_block, false);
1818 errorstream<<"ServerEnvironment::deactivateFarObjects(): "
1819 <<"Could not delete object id="<<id
1820 <<" from it's previous block "<<PP(old_static_block)
1824 block->m_static_objects.remove(id);
1825 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1826 "deactivateFarObjects: Static data moved out");
1830 // If block is active, don't remove
1831 if(!force_delete && m_active_blocks.contains(blockpos_o))
1834 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
1835 <<"deactivating object id="<<id<<" on inactive block "
1836 <<PP(blockpos_o)<<std::endl;
1838 // If known by some client, don't immediately delete.
1839 bool pending_delete = (obj->m_known_by_count > 0 && !force_delete);
1842 Update the static data
1845 if(obj->isStaticAllowed())
1847 // Create new static object
1848 std::string staticdata_new = obj->getStaticData();
1849 StaticObject s_obj(obj->getType(), objectpos, staticdata_new);
1851 bool stays_in_same_block = false;
1852 bool data_changed = true;
1854 if(obj->m_static_exists){
1855 if(obj->m_static_block == blockpos_o)
1856 stays_in_same_block = true;
1858 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
1860 std::map<u16, StaticObject>::iterator n =
1861 block->m_static_objects.m_active.find(id);
1862 if(n != block->m_static_objects.m_active.end()){
1863 StaticObject static_old = n->second;
1865 float save_movem = obj->getMinimumSavedMovement();
1867 if(static_old.data == staticdata_new &&
1868 (static_old.pos - objectpos).getLength() < save_movem)
1869 data_changed = false;
1871 errorstream<<"ServerEnvironment::deactivateFarObjects(): "
1872 <<"id="<<id<<" m_static_exists=true but "
1873 <<"static data doesn't actually exist in "
1874 <<PP(obj->m_static_block)<<std::endl;
1878 bool shall_be_written = (!stays_in_same_block || data_changed);
1880 // Delete old static object
1881 if(obj->m_static_exists)
1883 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
1886 block->m_static_objects.remove(id);
1887 obj->m_static_exists = false;
1888 // Only mark block as modified if data changed considerably
1889 if(shall_be_written)
1890 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1891 "deactivateFarObjects: Static data "
1892 "changed considerably");
1896 // Add to the block where the object is located in
1897 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1898 // Get or generate the block
1899 MapBlock *block = NULL;
1901 block = m_map->emergeBlock(blockpos);
1902 } catch(InvalidPositionException &e){
1903 // Handled via NULL pointer
1904 // NOTE: emergeBlock's failure is usually determined by it
1905 // actually returning NULL
1910 if(block->m_static_objects.m_stored.size() >= g_settings->getU16("max_objects_per_block")){
1911 errorstream<<"ServerEnv: Trying to store id="<<obj->getId()
1912 <<" statically but block "<<PP(blockpos)
1913 <<" already contains "
1914 <<block->m_static_objects.m_stored.size()
1916 <<" Forcing delete."<<std::endl;
1917 force_delete = true;
1919 // If static counterpart already exists in target block,
1921 // This shouldn't happen because the object is removed from
1922 // the previous block before this according to
1923 // obj->m_static_block, but happens rarely for some unknown
1924 // reason. Unsuccessful attempts have been made to find
1926 if(id && block->m_static_objects.m_active.find(id) != block->m_static_objects.m_active.end()){
1927 infostream<<"ServerEnv: WARNING: Performing hack #83274"
1929 block->m_static_objects.remove(id);
1931 // Store static data
1932 u16 store_id = pending_delete ? id : 0;
1933 block->m_static_objects.insert(store_id, s_obj);
1935 // Only mark block as modified if data changed considerably
1936 if(shall_be_written)
1937 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1938 "deactivateFarObjects: Static data "
1939 "changed considerably");
1941 obj->m_static_exists = true;
1942 obj->m_static_block = block->getPos();
1947 v3s16 p = floatToInt(objectpos, BS);
1948 errorstream<<"ServerEnv: Could not find or generate "
1949 <<"a block for storing id="<<obj->getId()
1950 <<" statically (pos="<<PP(p)<<")"<<std::endl;
1957 If known by some client, set pending deactivation.
1958 Otherwise delete it immediately.
1961 if(pending_delete && !force_delete)
1963 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
1964 <<"object id="<<id<<" is known by clients"
1965 <<"; not deleting yet"<<std::endl;
1967 obj->m_pending_deactivation = true;
1971 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
1972 <<"object id="<<id<<" is not known by clients"
1973 <<"; deleting"<<std::endl;
1975 // Tell the object about removal
1976 obj->removingFromEnvironment();
1977 // Deregister in scripting api
1978 m_script->removeObjectReference(obj);
1980 // Delete active object
1981 if(obj->environmentDeletes())
1983 // Id to be removed from m_active_objects
1984 objects_to_remove.push_back(id);
1987 // Remove references from m_active_objects
1988 for(std::list<u16>::iterator i = objects_to_remove.begin();
1989 i != objects_to_remove.end(); ++i)
1991 m_active_objects.erase(*i);
1998 #include "clientsimpleobject.h"
2004 ClientEnvironment::ClientEnvironment(ClientMap *map, scene::ISceneManager *smgr,
2005 ITextureSource *texturesource, IGameDef *gamedef,
2006 IrrlichtDevice *irr):
2009 m_texturesource(texturesource),
2014 memset(m_attachements, zero, sizeof(m_attachements));
2017 ClientEnvironment::~ClientEnvironment()
2019 // delete active objects
2020 for(std::map<u16, ClientActiveObject*>::iterator
2021 i = m_active_objects.begin();
2022 i != m_active_objects.end(); ++i)
2027 for(std::list<ClientSimpleObject*>::iterator
2028 i = m_simple_objects.begin(); i != m_simple_objects.end(); ++i)
2037 Map & ClientEnvironment::getMap()
2042 ClientMap & ClientEnvironment::getClientMap()
2047 void ClientEnvironment::addPlayer(Player *player)
2049 DSTACK(__FUNCTION_NAME);
2051 It is a failure if player is local and there already is a local
2054 assert(!(player->isLocal() == true && getLocalPlayer() != NULL));
2056 Environment::addPlayer(player);
2059 LocalPlayer * ClientEnvironment::getLocalPlayer()
2061 for(std::list<Player*>::iterator i = m_players.begin();
2062 i != m_players.end(); ++i)
2064 Player *player = *i;
2065 if(player->isLocal())
2066 return (LocalPlayer*)player;
2071 void ClientEnvironment::step(float dtime)
2073 DSTACK(__FUNCTION_NAME);
2075 /* Step time of day */
2076 stepTimeOfDay(dtime);
2078 // Get some settings
2079 bool fly_allowed = m_gamedef->checkLocalPrivilege("fly");
2080 bool free_move = fly_allowed && g_settings->getBool("free_move");
2083 LocalPlayer *lplayer = getLocalPlayer();
2085 // collision info queue
2086 std::list<CollisionInfo> player_collisions;
2089 Get the speed the player is going
2091 bool is_climbing = lplayer->is_climbing;
2093 f32 player_speed = lplayer->getSpeed().getLength();
2096 Maximum position increment
2098 //f32 position_max_increment = 0.05*BS;
2099 f32 position_max_increment = 0.1*BS;
2101 // Maximum time increment (for collision detection etc)
2102 // time = distance / speed
2103 f32 dtime_max_increment = 1;
2104 if(player_speed > 0.001)
2105 dtime_max_increment = position_max_increment / player_speed;
2107 // Maximum time increment is 10ms or lower
2108 if(dtime_max_increment > 0.01)
2109 dtime_max_increment = 0.01;
2111 // Don't allow overly huge dtime
2115 f32 dtime_downcount = dtime;
2118 Stuff that has a maximum time increment
2127 if(dtime_downcount > dtime_max_increment)
2129 dtime_part = dtime_max_increment;
2130 dtime_downcount -= dtime_part;
2134 dtime_part = dtime_downcount;
2136 Setting this to 0 (no -=dtime_part) disables an infinite loop
2137 when dtime_part is so small that dtime_downcount -= dtime_part
2140 dtime_downcount = 0;
2149 if(free_move == false && is_climbing == false)
2152 v3f speed = lplayer->getSpeed();
2153 if(lplayer->in_liquid == false)
2154 speed.Y -= lplayer->movement_gravity * lplayer->physics_override_gravity * dtime_part * 2;
2156 // Liquid floating / sinking
2157 if(lplayer->in_liquid && !lplayer->swimming_vertical)
2158 speed.Y -= lplayer->movement_liquid_sink * dtime_part * 2;
2160 // Liquid resistance
2161 if(lplayer->in_liquid_stable || lplayer->in_liquid)
2163 // How much the node's viscosity blocks movement, ranges between 0 and 1
2164 // Should match the scale at which viscosity increase affects other liquid attributes
2165 const f32 viscosity_factor = 0.3;
2167 v3f d_wanted = -speed / lplayer->movement_liquid_fluidity;
2168 f32 dl = d_wanted.getLength();
2169 if(dl > lplayer->movement_liquid_fluidity_smooth)
2170 dl = lplayer->movement_liquid_fluidity_smooth;
2171 dl *= (lplayer->liquid_viscosity * viscosity_factor) + (1 - viscosity_factor);
2173 v3f d = d_wanted.normalize() * dl;
2177 if(speed.X > lplayer->movement_liquid_fluidity + lplayer->movement_liquid_fluidity_smooth) speed.X -= lplayer->movement_liquid_fluidity_smooth;
2178 if(speed.X < -lplayer->movement_liquid_fluidity - lplayer->movement_liquid_fluidity_smooth) speed.X += lplayer->movement_liquid_fluidity_smooth;
2179 if(speed.Y > lplayer->movement_liquid_fluidity + lplayer->movement_liquid_fluidity_smooth) speed.Y -= lplayer->movement_liquid_fluidity_smooth;
2180 if(speed.Y < -lplayer->movement_liquid_fluidity - lplayer->movement_liquid_fluidity_smooth) speed.Y += lplayer->movement_liquid_fluidity_smooth;
2181 if(speed.Z > lplayer->movement_liquid_fluidity + lplayer->movement_liquid_fluidity_smooth) speed.Z -= lplayer->movement_liquid_fluidity_smooth;
2182 if(speed.Z < -lplayer->movement_liquid_fluidity - lplayer->movement_liquid_fluidity_smooth) speed.Z += lplayer->movement_liquid_fluidity_smooth;
2186 lplayer->setSpeed(speed);
2191 This also does collision detection.
2193 lplayer->move(dtime_part, this, position_max_increment,
2194 &player_collisions);
2197 while(dtime_downcount > 0.001);
2199 //std::cout<<"Looped "<<loopcount<<" times."<<std::endl;
2201 for(std::list<CollisionInfo>::iterator
2202 i = player_collisions.begin();
2203 i != player_collisions.end(); ++i)
2205 CollisionInfo &info = *i;
2206 v3f speed_diff = info.new_speed - info.old_speed;;
2207 // Handle only fall damage
2208 // (because otherwise walking against something in fast_move kills you)
2209 if(speed_diff.Y < 0 || info.old_speed.Y >= 0)
2211 // Get rid of other components
2214 f32 pre_factor = 1; // 1 hp per node/s
2215 f32 tolerance = BS*14; // 5 without damage
2216 f32 post_factor = 1; // 1 hp per node/s
2217 if(info.type == COLLISION_NODE)
2219 const ContentFeatures &f = m_gamedef->ndef()->
2220 get(m_map->getNodeNoEx(info.node_p));
2221 // Determine fall damage multiplier
2222 int addp = itemgroup_get(f.groups, "fall_damage_add_percent");
2223 pre_factor = 1.0 + (float)addp/100.0;
2225 float speed = pre_factor * speed_diff.getLength();
2226 if(speed > tolerance)
2228 f32 damage_f = (speed - tolerance)/BS * post_factor;
2229 u16 damage = (u16)(damage_f+0.5);
2231 damageLocalPlayer(damage, true);
2232 MtEvent *e = new SimpleTriggerEvent("PlayerFallingDamage");
2233 m_gamedef->event()->put(e);
2239 A quick draft of lava damage
2241 if(m_lava_hurt_interval.step(dtime, 1.0))
2243 v3f pf = lplayer->getPosition();
2245 // Feet, middle and head
2246 v3s16 p1 = floatToInt(pf + v3f(0, BS*0.1, 0), BS);
2247 MapNode n1 = m_map->getNodeNoEx(p1);
2248 v3s16 p2 = floatToInt(pf + v3f(0, BS*0.8, 0), BS);
2249 MapNode n2 = m_map->getNodeNoEx(p2);
2250 v3s16 p3 = floatToInt(pf + v3f(0, BS*1.6, 0), BS);
2251 MapNode n3 = m_map->getNodeNoEx(p3);
2253 u32 damage_per_second = 0;
2254 damage_per_second = MYMAX(damage_per_second,
2255 m_gamedef->ndef()->get(n1).damage_per_second);
2256 damage_per_second = MYMAX(damage_per_second,
2257 m_gamedef->ndef()->get(n2).damage_per_second);
2258 damage_per_second = MYMAX(damage_per_second,
2259 m_gamedef->ndef()->get(n3).damage_per_second);
2261 if(damage_per_second != 0)
2263 damageLocalPlayer(damage_per_second, true);
2270 if(m_drowning_interval.step(dtime, 2.0))
2272 v3f pf = lplayer->getPosition();
2275 v3s16 p = floatToInt(pf + v3f(0, BS*1.6, 0), BS);
2276 MapNode n = m_map->getNodeNoEx(p);
2277 ContentFeatures c = m_gamedef->ndef()->get(n);
2278 u8 drowning_damage = c.drowning;
2279 if(drowning_damage > 0 && lplayer->hp > 0){
2280 u16 breath = lplayer->getBreath();
2287 lplayer->setBreath(breath);
2288 updateLocalPlayerBreath(breath);
2291 if(lplayer->getBreath() == 0 && drowning_damage > 0){
2292 damageLocalPlayer(drowning_damage, true);
2295 if(m_breathing_interval.step(dtime, 0.5))
2297 v3f pf = lplayer->getPosition();
2300 v3s16 p = floatToInt(pf + v3f(0, BS*1.6, 0), BS);
2301 MapNode n = m_map->getNodeNoEx(p);
2302 ContentFeatures c = m_gamedef->ndef()->get(n);
2304 lplayer->setBreath(11);
2306 else if(c.drowning == 0){
2307 u16 breath = lplayer->getBreath();
2310 lplayer->setBreath(breath);
2311 updateLocalPlayerBreath(breath);
2317 Stuff that can be done in an arbitarily large dtime
2319 for(std::list<Player*>::iterator i = m_players.begin();
2320 i != m_players.end(); ++i)
2322 Player *player = *i;
2325 Handle non-local players
2327 if(player->isLocal() == false)
2330 player->move(dtime, this, 100*BS);
2334 // Update lighting on all players on client
2338 v3s16 p = player->getLightPosition();
2339 MapNode n = m_map->getNode(p);
2340 light = n.getLightBlendF1((float)getDayNightRatio()/1000, m_gamedef->ndef());
2342 catch(InvalidPositionException &e){
2343 light = blend_light_f1((float)getDayNightRatio()/1000, LIGHT_SUN, 0);
2345 player->light = light;
2349 Step active objects and update lighting of them
2352 g_profiler->avg("CEnv: num of objects", m_active_objects.size());
2353 bool update_lighting = m_active_object_light_update_interval.step(dtime, 0.21);
2354 for(std::map<u16, ClientActiveObject*>::iterator
2355 i = m_active_objects.begin();
2356 i != m_active_objects.end(); ++i)
2358 ClientActiveObject* obj = i->second;
2360 obj->step(dtime, this);
2368 v3s16 p = obj->getLightPosition();
2369 MapNode n = m_map->getNode(p);
2370 light = n.getLightBlend(getDayNightRatio(), m_gamedef->ndef());
2372 catch(InvalidPositionException &e){
2373 light = blend_light(getDayNightRatio(), LIGHT_SUN, 0);
2375 obj->updateLight(light);
2380 Step and handle simple objects
2382 g_profiler->avg("CEnv: num of simple objects", m_simple_objects.size());
2383 for(std::list<ClientSimpleObject*>::iterator
2384 i = m_simple_objects.begin(); i != m_simple_objects.end();)
2386 ClientSimpleObject *simple = *i;
2387 std::list<ClientSimpleObject*>::iterator cur = i;
2389 simple->step(dtime);
2390 if(simple->m_to_be_removed){
2392 m_simple_objects.erase(cur);
2397 void ClientEnvironment::addSimpleObject(ClientSimpleObject *simple)
2399 m_simple_objects.push_back(simple);
2402 ClientActiveObject* ClientEnvironment::getActiveObject(u16 id)
2404 std::map<u16, ClientActiveObject*>::iterator n;
2405 n = m_active_objects.find(id);
2406 if(n == m_active_objects.end())
2411 bool isFreeClientActiveObjectId(u16 id,
2412 std::map<u16, ClientActiveObject*> &objects)
2417 return objects.find(id) == objects.end();
2420 u16 getFreeClientActiveObjectId(
2421 std::map<u16, ClientActiveObject*> &objects)
2423 //try to reuse id's as late as possible
2424 static u16 last_used_id = 0;
2425 u16 startid = last_used_id;
2429 if(isFreeClientActiveObjectId(last_used_id, objects))
2430 return last_used_id;
2432 if(last_used_id == startid)
2437 u16 ClientEnvironment::addActiveObject(ClientActiveObject *object)
2440 if(object->getId() == 0)
2442 u16 new_id = getFreeClientActiveObjectId(m_active_objects);
2445 infostream<<"ClientEnvironment::addActiveObject(): "
2446 <<"no free ids available"<<std::endl;
2450 object->setId(new_id);
2452 if(isFreeClientActiveObjectId(object->getId(), m_active_objects) == false)
2454 infostream<<"ClientEnvironment::addActiveObject(): "
2455 <<"id is not free ("<<object->getId()<<")"<<std::endl;
2459 infostream<<"ClientEnvironment::addActiveObject(): "
2460 <<"added (id="<<object->getId()<<")"<<std::endl;
2461 m_active_objects[object->getId()] = object;
2462 object->addToScene(m_smgr, m_texturesource, m_irr);
2463 { // Update lighting immediately
2467 v3s16 p = object->getLightPosition();
2468 MapNode n = m_map->getNode(p);
2469 light = n.getLightBlend(getDayNightRatio(), m_gamedef->ndef());
2471 catch(InvalidPositionException &e){
2472 light = blend_light(getDayNightRatio(), LIGHT_SUN, 0);
2474 object->updateLight(light);
2476 return object->getId();
2479 void ClientEnvironment::addActiveObject(u16 id, u8 type,
2480 const std::string &init_data)
2482 ClientActiveObject* obj =
2483 ClientActiveObject::create(type, m_gamedef, this);
2486 infostream<<"ClientEnvironment::addActiveObject(): "
2487 <<"id="<<id<<" type="<<type<<": Couldn't create object"
2496 obj->initialize(init_data);
2498 catch(SerializationError &e)
2500 errorstream<<"ClientEnvironment::addActiveObject():"
2501 <<" id="<<id<<" type="<<type
2502 <<": SerializationError in initialize(): "
2504 <<": init_data="<<serializeJsonString(init_data)
2508 addActiveObject(obj);
2511 void ClientEnvironment::removeActiveObject(u16 id)
2513 verbosestream<<"ClientEnvironment::removeActiveObject(): "
2514 <<"id="<<id<<std::endl;
2515 ClientActiveObject* obj = getActiveObject(id);
2518 infostream<<"ClientEnvironment::removeActiveObject(): "
2519 <<"id="<<id<<" not found"<<std::endl;
2522 obj->removeFromScene(true);
2524 m_active_objects.erase(id);
2527 void ClientEnvironment::processActiveObjectMessage(u16 id,
2528 const std::string &data)
2530 ClientActiveObject* obj = getActiveObject(id);
2533 infostream<<"ClientEnvironment::processActiveObjectMessage():"
2534 <<" got message for id="<<id<<", which doesn't exist."
2540 obj->processMessage(data);
2542 catch(SerializationError &e)
2544 errorstream<<"ClientEnvironment::processActiveObjectMessage():"
2545 <<" id="<<id<<" type="<<obj->getType()
2546 <<" SerializationError in processMessage(),"
2547 <<" message="<<serializeJsonString(data)
2553 Callbacks for activeobjects
2556 void ClientEnvironment::damageLocalPlayer(u8 damage, bool handle_hp)
2558 LocalPlayer *lplayer = getLocalPlayer();
2562 if(lplayer->hp > damage)
2563 lplayer->hp -= damage;
2568 ClientEnvEvent event;
2569 event.type = CEE_PLAYER_DAMAGE;
2570 event.player_damage.amount = damage;
2571 event.player_damage.send_to_server = handle_hp;
2572 m_client_event_queue.push_back(event);
2575 void ClientEnvironment::updateLocalPlayerBreath(u16 breath)
2577 ClientEnvEvent event;
2578 event.type = CEE_PLAYER_BREATH;
2579 event.player_breath.amount = breath;
2580 m_client_event_queue.push_back(event);
2584 Client likes to call these
2587 void ClientEnvironment::getActiveObjects(v3f origin, f32 max_d,
2588 std::vector<DistanceSortedActiveObject> &dest)
2590 for(std::map<u16, ClientActiveObject*>::iterator
2591 i = m_active_objects.begin();
2592 i != m_active_objects.end(); ++i)
2594 ClientActiveObject* obj = i->second;
2596 f32 d = (obj->getPosition() - origin).getLength();
2601 DistanceSortedActiveObject dso(obj, d);
2603 dest.push_back(dso);
2607 ClientEnvEvent ClientEnvironment::getClientEvent()
2609 ClientEnvEvent event;
2610 if(m_client_event_queue.empty())
2611 event.type = CEE_NONE;
2613 event = m_client_event_queue.front();
2614 m_client_event_queue.pop_front();
2619 #endif // #ifndef SERVER