3 Copyright (C) 2010-2013 celeron55, Perttu Ahola <celeron55@gmail.com>
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU Lesser General Public License as published by
7 the Free Software Foundation; either version 2.1 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU Lesser General Public License for more details.
15 You should have received a copy of the GNU Lesser General Public License along
16 with this program; if not, write to the Free Software Foundation, Inc.,
17 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20 #include "environment.h"
23 #include "collision.h"
24 #include "content_mapnode.h"
26 #include "serverobject.h"
27 #include "content_sao.h"
31 #include "scripting_game.h"
33 #include "nodemetadata.h"
34 #include "main.h" // For g_settings, g_profiler
37 #include "clientmap.h"
38 #include "localplayer.h"
39 #include "mapblock_mesh.h"
42 #include "daynightratio.h"
45 #include "util/serialize.h"
46 #include "jthread/jmutexautolock.h"
48 #define PP(x) "("<<(x).X<<","<<(x).Y<<","<<(x).Z<<")"
50 Environment::Environment():
52 m_time_of_day_f(9000./24000),
53 m_time_of_day_speed(0),
55 m_enable_day_night_ratio_override(false),
56 m_day_night_ratio_override(0.0f)
60 Environment::~Environment()
63 for(std::list<Player*>::iterator i = m_players.begin();
64 i != m_players.end(); ++i)
70 void Environment::addPlayer(Player *player)
72 DSTACK(__FUNCTION_NAME);
74 Check that peer_ids are unique.
75 Also check that names are unique.
76 Exception: there can be multiple players with peer_id=0
78 // If peer id is non-zero, it has to be unique.
79 if(player->peer_id != 0)
80 assert(getPlayer(player->peer_id) == NULL);
81 // Name has to be unique.
82 assert(getPlayer(player->getName()) == NULL);
84 m_players.push_back(player);
87 void Environment::removePlayer(u16 peer_id)
89 DSTACK(__FUNCTION_NAME);
91 for(std::list<Player*>::iterator i = m_players.begin();
92 i != m_players.end();)
95 if(player->peer_id == peer_id) {
97 i = m_players.erase(i);
104 void Environment::removePlayer(const char *name)
106 for (std::list<Player*>::iterator it = m_players.begin();
107 it != m_players.end(); ++it) {
108 if (strcmp((*it)->getName(), name) == 0) {
116 Player * Environment::getPlayer(u16 peer_id)
118 for(std::list<Player*>::iterator i = m_players.begin();
119 i != m_players.end(); ++i)
122 if(player->peer_id == peer_id)
128 Player * Environment::getPlayer(const char *name)
130 for(std::list<Player*>::iterator i = m_players.begin();
131 i != m_players.end(); ++i)
134 if(strcmp(player->getName(), name) == 0)
140 Player * Environment::getRandomConnectedPlayer()
142 std::list<Player*> connected_players = getPlayers(true);
143 u32 chosen_one = myrand() % connected_players.size();
145 for(std::list<Player*>::iterator
146 i = connected_players.begin();
147 i != connected_players.end(); ++i)
159 Player * Environment::getNearestConnectedPlayer(v3f pos)
161 std::list<Player*> connected_players = getPlayers(true);
163 Player *nearest_player = NULL;
164 for(std::list<Player*>::iterator
165 i = connected_players.begin();
166 i != connected_players.end(); ++i)
169 f32 d = player->getPosition().getDistanceFrom(pos);
170 if(d < nearest_d || nearest_player == NULL)
173 nearest_player = player;
176 return nearest_player;
179 std::list<Player*> Environment::getPlayers()
184 std::list<Player*> Environment::getPlayers(bool ignore_disconnected)
186 std::list<Player*> newlist;
187 for(std::list<Player*>::iterator
188 i = m_players.begin();
189 i != m_players.end(); ++i)
193 if(ignore_disconnected)
195 // Ignore disconnected players
196 if(player->peer_id == 0)
200 newlist.push_back(player);
205 u32 Environment::getDayNightRatio()
207 if(m_enable_day_night_ratio_override)
208 return m_day_night_ratio_override;
209 bool smooth = g_settings->getBool("enable_shaders");
210 return time_to_daynight_ratio(m_time_of_day_f*24000, smooth);
213 void Environment::setTimeOfDaySpeed(float speed)
215 JMutexAutoLock(this->m_lock);
216 m_time_of_day_speed = speed;
219 float Environment::getTimeOfDaySpeed()
221 JMutexAutoLock(this->m_lock);
222 float retval = m_time_of_day_speed;
226 void Environment::stepTimeOfDay(float dtime)
230 JMutexAutoLock(this->m_lock);
231 day_speed = m_time_of_day_speed;
234 m_time_counter += dtime;
235 f32 speed = day_speed * 24000./(24.*3600);
236 u32 units = (u32)(m_time_counter*speed);
240 if(m_time_of_day + units >= 24000)
242 m_time_of_day = (m_time_of_day + units) % 24000;
244 m_time_of_day_f = (float)m_time_of_day / 24000.0;
247 m_time_counter -= (f32)units / speed;
250 m_time_of_day_f += day_speed/24/3600*dtime;
251 if(m_time_of_day_f > 1.0)
252 m_time_of_day_f -= 1.0;
253 if(m_time_of_day_f < 0.0)
254 m_time_of_day_f += 1.0;
262 ABMWithState::ABMWithState(ActiveBlockModifier *abm_):
266 // Initialize timer to random value to spread processing
267 float itv = abm->getTriggerInterval();
268 itv = MYMAX(0.001, itv); // No less than 1ms
269 int minval = MYMAX(-0.51*itv, -60); // Clamp to
270 int maxval = MYMIN(0.51*itv, 60); // +-60 seconds
271 timer = myrand_range(minval, maxval);
278 void fillRadiusBlock(v3s16 p0, s16 r, std::set<v3s16> &list)
281 for(p.X=p0.X-r; p.X<=p0.X+r; p.X++)
282 for(p.Y=p0.Y-r; p.Y<=p0.Y+r; p.Y++)
283 for(p.Z=p0.Z-r; p.Z<=p0.Z+r; p.Z++)
290 void ActiveBlockList::update(std::list<v3s16> &active_positions,
292 std::set<v3s16> &blocks_removed,
293 std::set<v3s16> &blocks_added)
298 std::set<v3s16> newlist = m_forceloaded_list;
299 for(std::list<v3s16>::iterator i = active_positions.begin();
300 i != active_positions.end(); ++i)
302 fillRadiusBlock(*i, radius, newlist);
306 Find out which blocks on the old list are not on the new list
308 // Go through old list
309 for(std::set<v3s16>::iterator i = m_list.begin();
310 i != m_list.end(); ++i)
313 // If not on new list, it's been removed
314 if(newlist.find(p) == newlist.end())
315 blocks_removed.insert(p);
319 Find out which blocks on the new list are not on the old list
321 // Go through new list
322 for(std::set<v3s16>::iterator i = newlist.begin();
323 i != newlist.end(); ++i)
326 // If not on old list, it's been added
327 if(m_list.find(p) == m_list.end())
328 blocks_added.insert(p);
335 for(std::set<v3s16>::iterator i = newlist.begin();
336 i != newlist.end(); ++i)
347 ServerEnvironment::ServerEnvironment(ServerMap *map,
348 GameScripting *scriptIface, IGameDef *gamedef,
349 const std::string &path_world) :
351 m_script(scriptIface),
353 m_path_world(path_world),
354 m_send_recommended_timer(0),
355 m_active_block_interval_overload_skip(0),
357 m_game_time_fraction_counter(0),
358 m_recommended_send_interval(0.1),
359 m_max_lag_estimate(0.1)
363 ServerEnvironment::~ServerEnvironment()
365 // Clear active block list.
366 // This makes the next one delete all active objects.
367 m_active_blocks.clear();
369 // Convert all objects to static and delete the active objects
370 deactivateFarObjects(true);
375 // Delete ActiveBlockModifiers
376 for(std::list<ABMWithState>::iterator
377 i = m_abms.begin(); i != m_abms.end(); ++i){
382 Map & ServerEnvironment::getMap()
387 ServerMap & ServerEnvironment::getServerMap()
392 bool ServerEnvironment::line_of_sight(v3f pos1, v3f pos2, float stepsize, v3s16 *p)
394 float distance = pos1.getDistanceFrom(pos2);
396 //calculate normalized direction vector
397 v3f normalized_vector = v3f((pos2.X - pos1.X)/distance,
398 (pos2.Y - pos1.Y)/distance,
399 (pos2.Z - pos1.Z)/distance);
401 //find out if there's a node on path between pos1 and pos2
402 for (float i = 1; i < distance; i += stepsize) {
403 v3s16 pos = floatToInt(v3f(normalized_vector.X * i,
404 normalized_vector.Y * i,
405 normalized_vector.Z * i) +pos1,BS);
407 MapNode n = getMap().getNodeNoEx(pos);
409 if(n.param0 != CONTENT_AIR) {
419 void ServerEnvironment::saveLoadedPlayers()
421 std::string players_path = m_path_world + DIR_DELIM "players";
422 fs::CreateDir(players_path);
424 for (std::list<Player*>::iterator it = m_players.begin();
425 it != m_players.end();
427 RemotePlayer *player = static_cast<RemotePlayer*>(*it);
428 if (player->checkModified()) {
429 player->save(players_path);
434 void ServerEnvironment::savePlayer(const std::string &playername)
436 std::string players_path = m_path_world + DIR_DELIM "players";
437 fs::CreateDir(players_path);
439 RemotePlayer *player = static_cast<RemotePlayer*>(getPlayer(playername.c_str()));
441 player->save(players_path);
445 Player *ServerEnvironment::loadPlayer(const std::string &playername)
447 std::string players_path = m_path_world + DIR_DELIM "players" DIR_DELIM;
449 RemotePlayer *player = static_cast<RemotePlayer*>(getPlayer(playername.c_str()));
450 bool newplayer = false;
453 player = new RemotePlayer(m_gamedef, playername.c_str());
457 RemotePlayer testplayer(m_gamedef, "");
458 std::string path = players_path + playername;
459 for (u32 i = 0; i < PLAYER_FILE_ALTERNATE_TRIES; i++) {
460 // Open file and deserialize
461 std::ifstream is(path.c_str(), std::ios_base::binary);
465 testplayer.deSerialize(is, path);
467 if (testplayer.getName() == playername) {
468 *player = testplayer;
472 path = players_path + playername + itos(i);
475 infostream << "Player file for player " << playername
476 << " not found" << std::endl;
482 player->setModified(false);
486 void ServerEnvironment::saveMeta()
488 std::string path = m_path_world + DIR_DELIM "env_meta.txt";
490 // Open file and serialize
491 std::ostringstream ss(std::ios_base::binary);
494 args.setU64("game_time", m_game_time);
495 args.setU64("time_of_day", getTimeOfDay());
499 if(!fs::safeWriteToFile(path, ss.str()))
501 infostream<<"ServerEnvironment::saveMeta(): Failed to write "
503 throw SerializationError("Couldn't save env meta");
507 void ServerEnvironment::loadMeta()
509 std::string path = m_path_world + DIR_DELIM "env_meta.txt";
511 // Open file and deserialize
512 std::ifstream is(path.c_str(), std::ios_base::binary);
514 infostream << "ServerEnvironment::loadMeta(): Failed to open "
515 << path << std::endl;
516 throw SerializationError("Couldn't load env meta");
521 if (!args.parseConfigLines(is, "EnvArgsEnd")) {
522 throw SerializationError("ServerEnvironment::loadMeta(): "
523 "EnvArgsEnd not found!");
527 m_game_time = args.getU64("game_time");
528 } catch (SettingNotFoundException &e) {
529 // Getting this is crucial, otherwise timestamps are useless
530 throw SerializationError("Couldn't load env meta game_time");
534 m_time_of_day = args.getU64("time_of_day");
535 } catch (SettingNotFoundException &e) {
536 // This is not as important
537 m_time_of_day = 9000;
543 ActiveBlockModifier *abm;
545 std::set<content_t> required_neighbors;
551 ServerEnvironment *m_env;
552 std::map<content_t, std::list<ActiveABM> > m_aabms;
554 ABMHandler(std::list<ABMWithState> &abms,
555 float dtime_s, ServerEnvironment *env,
561 INodeDefManager *ndef = env->getGameDef()->ndef();
562 for(std::list<ABMWithState>::iterator
563 i = abms.begin(); i != abms.end(); ++i){
564 ActiveBlockModifier *abm = i->abm;
565 float trigger_interval = abm->getTriggerInterval();
566 if(trigger_interval < 0.001)
567 trigger_interval = 0.001;
568 float actual_interval = dtime_s;
571 if(i->timer < trigger_interval)
573 i->timer -= trigger_interval;
574 actual_interval = trigger_interval;
576 float intervals = actual_interval / trigger_interval;
579 float chance = abm->getTriggerChance();
584 aabm.chance = chance / intervals;
588 std::set<std::string> required_neighbors_s
589 = abm->getRequiredNeighbors();
590 for(std::set<std::string>::iterator
591 i = required_neighbors_s.begin();
592 i != required_neighbors_s.end(); i++)
594 ndef->getIds(*i, aabm.required_neighbors);
597 std::set<std::string> contents_s = abm->getTriggerContents();
598 for(std::set<std::string>::iterator
599 i = contents_s.begin(); i != contents_s.end(); i++)
601 std::set<content_t> ids;
602 ndef->getIds(*i, ids);
603 for(std::set<content_t>::const_iterator k = ids.begin();
607 std::map<content_t, std::list<ActiveABM> >::iterator j;
609 if(j == m_aabms.end()){
610 std::list<ActiveABM> aabmlist;
611 m_aabms[c] = aabmlist;
614 j->second.push_back(aabm);
619 // Find out how many objects the given block and its neighbours contain.
620 // Returns the number of objects in the block, and also in 'wider' the
621 // number of objects in the block and all its neighbours. The latter
622 // may an estimate if any neighbours are unloaded.
623 u32 countObjects(MapBlock *block, ServerMap * map, u32 &wider)
626 u32 wider_unknown_count = 0;
627 for(s16 x=-1; x<=1; x++)
628 for(s16 y=-1; y<=1; y++)
629 for(s16 z=-1; z<=1; z++)
631 MapBlock *block2 = map->getBlockNoCreateNoEx(
632 block->getPos() + v3s16(x,y,z));
634 wider_unknown_count++;
637 wider += block2->m_static_objects.m_active.size()
638 + block2->m_static_objects.m_stored.size();
641 u32 active_object_count = block->m_static_objects.m_active.size();
642 u32 wider_known_count = 3*3*3 - wider_unknown_count;
643 wider += wider_unknown_count * wider / wider_known_count;
644 return active_object_count;
647 void apply(MapBlock *block)
652 ServerMap *map = &m_env->getServerMap();
654 u32 active_object_count_wider;
655 u32 active_object_count = this->countObjects(block, map, active_object_count_wider);
656 m_env->m_added_objects = 0;
659 for(p0.X=0; p0.X<MAP_BLOCKSIZE; p0.X++)
660 for(p0.Y=0; p0.Y<MAP_BLOCKSIZE; p0.Y++)
661 for(p0.Z=0; p0.Z<MAP_BLOCKSIZE; p0.Z++)
663 MapNode n = block->getNodeNoEx(p0);
664 content_t c = n.getContent();
665 v3s16 p = p0 + block->getPosRelative();
667 std::map<content_t, std::list<ActiveABM> >::iterator j;
669 if(j == m_aabms.end())
672 for(std::list<ActiveABM>::iterator
673 i = j->second.begin(); i != j->second.end(); i++)
675 if(myrand() % i->chance != 0)
679 if(!i->required_neighbors.empty())
682 for(p1.X = p.X-1; p1.X <= p.X+1; p1.X++)
683 for(p1.Y = p.Y-1; p1.Y <= p.Y+1; p1.Y++)
684 for(p1.Z = p.Z-1; p1.Z <= p.Z+1; p1.Z++)
688 MapNode n = map->getNodeNoEx(p1);
689 content_t c = n.getContent();
690 std::set<content_t>::const_iterator k;
691 k = i->required_neighbors.find(c);
692 if(k != i->required_neighbors.end()){
696 // No required neighbor found
701 // Call all the trigger variations
702 i->abm->trigger(m_env, p, n);
703 i->abm->trigger(m_env, p, n,
704 active_object_count, active_object_count_wider);
706 // Count surrounding objects again if the abms added any
707 if(m_env->m_added_objects > 0) {
708 active_object_count = countObjects(block, map, active_object_count_wider);
709 m_env->m_added_objects = 0;
716 void ServerEnvironment::activateBlock(MapBlock *block, u32 additional_dtime)
718 // Reset usage timer immediately, otherwise a block that becomes active
719 // again at around the same time as it would normally be unloaded will
720 // get unloaded incorrectly. (I think this still leaves a small possibility
721 // of a race condition between this and server::AsyncRunStep, which only
722 // some kind of synchronisation will fix, but it at least reduces the window
723 // of opportunity for it to break from seconds to nanoseconds)
724 block->resetUsageTimer();
726 // Get time difference
728 u32 stamp = block->getTimestamp();
729 if(m_game_time > stamp && stamp != BLOCK_TIMESTAMP_UNDEFINED)
730 dtime_s = m_game_time - block->getTimestamp();
731 dtime_s += additional_dtime;
733 /*infostream<<"ServerEnvironment::activateBlock(): block timestamp: "
734 <<stamp<<", game time: "<<m_game_time<<std::endl;*/
736 // Set current time as timestamp
737 block->setTimestampNoChangedFlag(m_game_time);
739 /*infostream<<"ServerEnvironment::activateBlock(): block is "
740 <<dtime_s<<" seconds old."<<std::endl;*/
742 // Activate stored objects
743 activateObjects(block, dtime_s);
746 std::map<v3s16, NodeTimer> elapsed_timers =
747 block->m_node_timers.step((float)dtime_s);
748 if(!elapsed_timers.empty()){
750 for(std::map<v3s16, NodeTimer>::iterator
751 i = elapsed_timers.begin();
752 i != elapsed_timers.end(); i++){
753 n = block->getNodeNoEx(i->first);
754 v3s16 p = i->first + block->getPosRelative();
755 if(m_script->node_on_timer(p,n,i->second.elapsed))
756 block->setNodeTimer(i->first,NodeTimer(i->second.timeout,0));
760 /* Handle ActiveBlockModifiers */
761 ABMHandler abmhandler(m_abms, dtime_s, this, false);
762 abmhandler.apply(block);
765 void ServerEnvironment::addActiveBlockModifier(ActiveBlockModifier *abm)
767 m_abms.push_back(ABMWithState(abm));
770 bool ServerEnvironment::setNode(v3s16 p, const MapNode &n)
772 INodeDefManager *ndef = m_gamedef->ndef();
773 MapNode n_old = m_map->getNodeNoEx(p);
776 if (ndef->get(n_old).has_on_destruct)
777 m_script->node_on_destruct(p, n_old);
780 if (!m_map->addNodeWithEvent(p, n))
783 // Update active VoxelManipulator if a mapgen thread
784 m_map->updateVManip(p);
786 // Call post-destructor
787 if (ndef->get(n_old).has_after_destruct)
788 m_script->node_after_destruct(p, n_old);
791 if (ndef->get(n).has_on_construct)
792 m_script->node_on_construct(p, n);
797 bool ServerEnvironment::removeNode(v3s16 p)
799 INodeDefManager *ndef = m_gamedef->ndef();
800 MapNode n_old = m_map->getNodeNoEx(p);
803 if (ndef->get(n_old).has_on_destruct)
804 m_script->node_on_destruct(p, n_old);
807 // This is slightly optimized compared to addNodeWithEvent(air)
808 if (!m_map->removeNodeWithEvent(p))
811 // Update active VoxelManipulator if a mapgen thread
812 m_map->updateVManip(p);
814 // Call post-destructor
815 if (ndef->get(n_old).has_after_destruct)
816 m_script->node_after_destruct(p, n_old);
818 // Air doesn't require constructor
822 bool ServerEnvironment::swapNode(v3s16 p, const MapNode &n)
824 if (!m_map->addNodeWithEvent(p, n, false))
827 // Update active VoxelManipulator if a mapgen thread
828 m_map->updateVManip(p);
833 std::set<u16> ServerEnvironment::getObjectsInsideRadius(v3f pos, float radius)
835 std::set<u16> objects;
836 for(std::map<u16, ServerActiveObject*>::iterator
837 i = m_active_objects.begin();
838 i != m_active_objects.end(); ++i)
840 ServerActiveObject* obj = i->second;
842 v3f objectpos = obj->getBasePosition();
843 if(objectpos.getDistanceFrom(pos) > radius)
850 void ServerEnvironment::clearAllObjects()
852 infostream<<"ServerEnvironment::clearAllObjects(): "
853 <<"Removing all active objects"<<std::endl;
854 std::list<u16> objects_to_remove;
855 for(std::map<u16, ServerActiveObject*>::iterator
856 i = m_active_objects.begin();
857 i != m_active_objects.end(); ++i)
859 ServerActiveObject* obj = i->second;
860 if(obj->getType() == ACTIVEOBJECT_TYPE_PLAYER)
863 // Delete static object if block is loaded
864 if(obj->m_static_exists){
865 MapBlock *block = m_map->getBlockNoCreateNoEx(obj->m_static_block);
867 block->m_static_objects.remove(id);
868 block->raiseModified(MOD_STATE_WRITE_NEEDED,
870 obj->m_static_exists = false;
873 // If known by some client, don't delete immediately
874 if(obj->m_known_by_count > 0){
875 obj->m_pending_deactivation = true;
876 obj->m_removed = true;
880 // Tell the object about removal
881 obj->removingFromEnvironment();
882 // Deregister in scripting api
883 m_script->removeObjectReference(obj);
885 // Delete active object
886 if(obj->environmentDeletes())
888 // Id to be removed from m_active_objects
889 objects_to_remove.push_back(id);
891 // Remove references from m_active_objects
892 for(std::list<u16>::iterator i = objects_to_remove.begin();
893 i != objects_to_remove.end(); ++i)
895 m_active_objects.erase(*i);
898 // Get list of loaded blocks
899 std::list<v3s16> loaded_blocks;
900 infostream<<"ServerEnvironment::clearAllObjects(): "
901 <<"Listing all loaded blocks"<<std::endl;
902 m_map->listAllLoadedBlocks(loaded_blocks);
903 infostream<<"ServerEnvironment::clearAllObjects(): "
904 <<"Done listing all loaded blocks: "
905 <<loaded_blocks.size()<<std::endl;
907 // Get list of loadable blocks
908 std::list<v3s16> loadable_blocks;
909 infostream<<"ServerEnvironment::clearAllObjects(): "
910 <<"Listing all loadable blocks"<<std::endl;
911 m_map->listAllLoadableBlocks(loadable_blocks);
912 infostream<<"ServerEnvironment::clearAllObjects(): "
913 <<"Done listing all loadable blocks: "
914 <<loadable_blocks.size()
915 <<", now clearing"<<std::endl;
917 // Grab a reference on each loaded block to avoid unloading it
918 for(std::list<v3s16>::iterator i = loaded_blocks.begin();
919 i != loaded_blocks.end(); ++i)
922 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
927 // Remove objects in all loadable blocks
928 u32 unload_interval = g_settings->getS32("max_clearobjects_extra_loaded_blocks");
929 unload_interval = MYMAX(unload_interval, 1);
930 u32 report_interval = loadable_blocks.size() / 10;
931 u32 num_blocks_checked = 0;
932 u32 num_blocks_cleared = 0;
933 u32 num_objs_cleared = 0;
934 for(std::list<v3s16>::iterator i = loadable_blocks.begin();
935 i != loadable_blocks.end(); ++i)
938 MapBlock *block = m_map->emergeBlock(p, false);
940 errorstream<<"ServerEnvironment::clearAllObjects(): "
941 <<"Failed to emerge block "<<PP(p)<<std::endl;
944 u32 num_stored = block->m_static_objects.m_stored.size();
945 u32 num_active = block->m_static_objects.m_active.size();
946 if(num_stored != 0 || num_active != 0){
947 block->m_static_objects.m_stored.clear();
948 block->m_static_objects.m_active.clear();
949 block->raiseModified(MOD_STATE_WRITE_NEEDED,
951 num_objs_cleared += num_stored + num_active;
952 num_blocks_cleared++;
954 num_blocks_checked++;
956 if(report_interval != 0 &&
957 num_blocks_checked % report_interval == 0){
958 float percent = 100.0 * (float)num_blocks_checked /
959 loadable_blocks.size();
960 infostream<<"ServerEnvironment::clearAllObjects(): "
961 <<"Cleared "<<num_objs_cleared<<" objects"
962 <<" in "<<num_blocks_cleared<<" blocks ("
963 <<percent<<"%)"<<std::endl;
965 if(num_blocks_checked % unload_interval == 0){
966 m_map->unloadUnreferencedBlocks();
969 m_map->unloadUnreferencedBlocks();
971 // Drop references that were added above
972 for(std::list<v3s16>::iterator i = loaded_blocks.begin();
973 i != loaded_blocks.end(); ++i)
976 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
981 infostream<<"ServerEnvironment::clearAllObjects(): "
982 <<"Finished: Cleared "<<num_objs_cleared<<" objects"
983 <<" in "<<num_blocks_cleared<<" blocks"<<std::endl;
986 void ServerEnvironment::step(float dtime)
988 DSTACK(__FUNCTION_NAME);
990 //TimeTaker timer("ServerEnv step");
992 /* Step time of day */
993 stepTimeOfDay(dtime);
996 // NOTE: This is kind of funny on a singleplayer game, but doesn't
997 // really matter that much.
998 m_recommended_send_interval = g_settings->getFloat("dedicated_server_step");
1004 m_game_time_fraction_counter += dtime;
1005 u32 inc_i = (u32)m_game_time_fraction_counter;
1006 m_game_time += inc_i;
1007 m_game_time_fraction_counter -= (float)inc_i;
1014 ScopeProfiler sp(g_profiler, "SEnv: handle players avg", SPT_AVG);
1015 for(std::list<Player*>::iterator i = m_players.begin();
1016 i != m_players.end(); ++i)
1018 Player *player = *i;
1020 // Ignore disconnected players
1021 if(player->peer_id == 0)
1025 player->move(dtime, this, 100*BS);
1030 Manage active block list
1032 if(m_active_blocks_management_interval.step(dtime, 2.0))
1034 ScopeProfiler sp(g_profiler, "SEnv: manage act. block list avg /2s", SPT_AVG);
1036 Get player block positions
1038 std::list<v3s16> players_blockpos;
1039 for(std::list<Player*>::iterator
1040 i = m_players.begin();
1041 i != m_players.end(); ++i)
1043 Player *player = *i;
1044 // Ignore disconnected players
1045 if(player->peer_id == 0)
1047 v3s16 blockpos = getNodeBlockPos(
1048 floatToInt(player->getPosition(), BS));
1049 players_blockpos.push_back(blockpos);
1053 Update list of active blocks, collecting changes
1055 const s16 active_block_range = g_settings->getS16("active_block_range");
1056 std::set<v3s16> blocks_removed;
1057 std::set<v3s16> blocks_added;
1058 m_active_blocks.update(players_blockpos, active_block_range,
1059 blocks_removed, blocks_added);
1062 Handle removed blocks
1065 // Convert active objects that are no more in active blocks to static
1066 deactivateFarObjects(false);
1068 for(std::set<v3s16>::iterator
1069 i = blocks_removed.begin();
1070 i != blocks_removed.end(); ++i)
1074 /* infostream<<"Server: Block " << PP(p)
1075 << " became inactive"<<std::endl; */
1077 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1081 // Set current time as timestamp (and let it set ChangedFlag)
1082 block->setTimestamp(m_game_time);
1089 for(std::set<v3s16>::iterator
1090 i = blocks_added.begin();
1091 i != blocks_added.end(); ++i)
1095 MapBlock *block = m_map->getBlockOrEmerge(p);
1097 m_active_blocks.m_list.erase(p);
1101 activateBlock(block);
1102 /* infostream<<"Server: Block " << PP(p)
1103 << " became active"<<std::endl; */
1108 Mess around in active blocks
1110 if(m_active_blocks_nodemetadata_interval.step(dtime, 1.0))
1112 ScopeProfiler sp(g_profiler, "SEnv: mess in act. blocks avg /1s", SPT_AVG);
1116 for(std::set<v3s16>::iterator
1117 i = m_active_blocks.m_list.begin();
1118 i != m_active_blocks.m_list.end(); ++i)
1122 /*infostream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
1123 <<") being handled"<<std::endl;*/
1125 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1129 // Reset block usage timer
1130 block->resetUsageTimer();
1132 // Set current time as timestamp
1133 block->setTimestampNoChangedFlag(m_game_time);
1134 // If time has changed much from the one on disk,
1135 // set block to be saved when it is unloaded
1136 if(block->getTimestamp() > block->getDiskTimestamp() + 60)
1137 block->raiseModified(MOD_STATE_WRITE_AT_UNLOAD,
1138 "Timestamp older than 60s (step)");
1141 std::map<v3s16, NodeTimer> elapsed_timers =
1142 block->m_node_timers.step((float)dtime);
1143 if(!elapsed_timers.empty()){
1145 for(std::map<v3s16, NodeTimer>::iterator
1146 i = elapsed_timers.begin();
1147 i != elapsed_timers.end(); i++){
1148 n = block->getNodeNoEx(i->first);
1149 p = i->first + block->getPosRelative();
1150 if(m_script->node_on_timer(p,n,i->second.elapsed))
1151 block->setNodeTimer(i->first,NodeTimer(i->second.timeout,0));
1157 const float abm_interval = 1.0;
1158 if(m_active_block_modifier_interval.step(dtime, abm_interval))
1160 if(m_active_block_interval_overload_skip > 0){
1161 ScopeProfiler sp(g_profiler, "SEnv: ABM overload skips");
1162 m_active_block_interval_overload_skip--;
1165 ScopeProfiler sp(g_profiler, "SEnv: modify in blocks avg /1s", SPT_AVG);
1166 TimeTaker timer("modify in active blocks");
1168 // Initialize handling of ActiveBlockModifiers
1169 ABMHandler abmhandler(m_abms, abm_interval, this, true);
1171 for(std::set<v3s16>::iterator
1172 i = m_active_blocks.m_list.begin();
1173 i != m_active_blocks.m_list.end(); ++i)
1177 /*infostream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
1178 <<") being handled"<<std::endl;*/
1180 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1184 // Set current time as timestamp
1185 block->setTimestampNoChangedFlag(m_game_time);
1187 /* Handle ActiveBlockModifiers */
1188 abmhandler.apply(block);
1191 u32 time_ms = timer.stop(true);
1192 u32 max_time_ms = 200;
1193 if(time_ms > max_time_ms){
1194 infostream<<"WARNING: active block modifiers took "
1195 <<time_ms<<"ms (longer than "
1196 <<max_time_ms<<"ms)"<<std::endl;
1197 m_active_block_interval_overload_skip = (time_ms / max_time_ms) + 1;
1202 Step script environment (run global on_step())
1204 m_script->environment_Step(dtime);
1210 ScopeProfiler sp(g_profiler, "SEnv: step act. objs avg", SPT_AVG);
1211 //TimeTaker timer("Step active objects");
1213 g_profiler->avg("SEnv: num of objects", m_active_objects.size());
1215 // This helps the objects to send data at the same time
1216 bool send_recommended = false;
1217 m_send_recommended_timer += dtime;
1218 if(m_send_recommended_timer > getSendRecommendedInterval())
1220 m_send_recommended_timer -= getSendRecommendedInterval();
1221 send_recommended = true;
1224 for(std::map<u16, ServerActiveObject*>::iterator
1225 i = m_active_objects.begin();
1226 i != m_active_objects.end(); ++i)
1228 ServerActiveObject* obj = i->second;
1229 // Don't step if is to be removed or stored statically
1230 if(obj->m_removed || obj->m_pending_deactivation)
1233 obj->step(dtime, send_recommended);
1234 // Read messages from object
1235 while(!obj->m_messages_out.empty())
1237 m_active_object_messages.push_back(
1238 obj->m_messages_out.pop_front());
1244 Manage active objects
1246 if(m_object_management_interval.step(dtime, 0.5))
1248 ScopeProfiler sp(g_profiler, "SEnv: remove removed objs avg /.5s", SPT_AVG);
1250 Remove objects that satisfy (m_removed && m_known_by_count==0)
1252 removeRemovedObjects();
1256 ServerActiveObject* ServerEnvironment::getActiveObject(u16 id)
1258 std::map<u16, ServerActiveObject*>::iterator n;
1259 n = m_active_objects.find(id);
1260 if(n == m_active_objects.end())
1265 bool isFreeServerActiveObjectId(u16 id,
1266 std::map<u16, ServerActiveObject*> &objects)
1271 return objects.find(id) == objects.end();
1274 u16 getFreeServerActiveObjectId(
1275 std::map<u16, ServerActiveObject*> &objects)
1277 //try to reuse id's as late as possible
1278 static u16 last_used_id = 0;
1279 u16 startid = last_used_id;
1283 if(isFreeServerActiveObjectId(last_used_id, objects))
1284 return last_used_id;
1286 if(last_used_id == startid)
1291 u16 ServerEnvironment::addActiveObject(ServerActiveObject *object)
1295 u16 id = addActiveObjectRaw(object, true, 0);
1300 bool ServerEnvironment::addActiveObjectAsStatic(ServerActiveObject *obj)
1304 v3f objectpos = obj->getBasePosition();
1306 // The block in which the object resides in
1307 v3s16 blockpos_o = getNodeBlockPos(floatToInt(objectpos, BS));
1310 Update the static data
1313 // Create new static object
1314 std::string staticdata = obj->getStaticData();
1315 StaticObject s_obj(obj->getType(), objectpos, staticdata);
1316 // Add to the block where the object is located in
1317 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1318 // Get or generate the block
1319 MapBlock *block = m_map->emergeBlock(blockpos);
1321 bool succeeded = false;
1325 block->m_static_objects.insert(0, s_obj);
1326 block->raiseModified(MOD_STATE_WRITE_AT_UNLOAD,
1327 "addActiveObjectAsStatic");
1331 infostream<<"ServerEnvironment::addActiveObjectAsStatic: "
1332 <<"Could not find or generate "
1333 <<"a block for storing static object"<<std::endl;
1337 if(obj->environmentDeletes())
1345 Finds out what new objects have been added to
1346 inside a radius around a position
1348 void ServerEnvironment::getAddedActiveObjects(v3s16 pos, s16 radius,
1350 std::set<u16> ¤t_objects,
1351 std::set<u16> &added_objects)
1353 v3f pos_f = intToFloat(pos, BS);
1354 f32 radius_f = radius * BS;
1355 f32 player_radius_f = player_radius * BS;
1357 if (player_radius_f < 0)
1358 player_radius_f = 0;
1361 Go through the object list,
1362 - discard m_removed objects,
1363 - discard objects that are too far away,
1364 - discard objects that are found in current_objects.
1365 - add remaining objects to added_objects
1367 for(std::map<u16, ServerActiveObject*>::iterator
1368 i = m_active_objects.begin();
1369 i != m_active_objects.end(); ++i)
1373 ServerActiveObject *object = i->second;
1376 // Discard if removed or deactivating
1377 if(object->m_removed || object->m_pending_deactivation)
1380 f32 distance_f = object->getBasePosition().getDistanceFrom(pos_f);
1381 if (object->getType() == ACTIVEOBJECT_TYPE_PLAYER) {
1382 // Discard if too far
1383 if (distance_f > player_radius_f && player_radius_f != 0)
1385 } else if (distance_f > radius_f)
1388 // Discard if already on current_objects
1389 std::set<u16>::iterator n;
1390 n = current_objects.find(id);
1391 if(n != current_objects.end())
1393 // Add to added_objects
1394 added_objects.insert(id);
1399 Finds out what objects have been removed from
1400 inside a radius around a position
1402 void ServerEnvironment::getRemovedActiveObjects(v3s16 pos, s16 radius,
1404 std::set<u16> ¤t_objects,
1405 std::set<u16> &removed_objects)
1407 v3f pos_f = intToFloat(pos, BS);
1408 f32 radius_f = radius * BS;
1409 f32 player_radius_f = player_radius * BS;
1411 if (player_radius_f < 0)
1412 player_radius_f = 0;
1415 Go through current_objects; object is removed if:
1416 - object is not found in m_active_objects (this is actually an
1417 error condition; objects should be set m_removed=true and removed
1418 only after all clients have been informed about removal), or
1419 - object has m_removed=true, or
1420 - object is too far away
1422 for(std::set<u16>::iterator
1423 i = current_objects.begin();
1424 i != current_objects.end(); ++i)
1427 ServerActiveObject *object = getActiveObject(id);
1430 infostream<<"ServerEnvironment::getRemovedActiveObjects():"
1431 <<" object in current_objects is NULL"<<std::endl;
1432 removed_objects.insert(id);
1436 if(object->m_removed || object->m_pending_deactivation)
1438 removed_objects.insert(id);
1442 f32 distance_f = object->getBasePosition().getDistanceFrom(pos_f);
1443 if (object->getType() == ACTIVEOBJECT_TYPE_PLAYER) {
1444 if (distance_f <= player_radius_f || player_radius_f == 0)
1446 } else if (distance_f <= radius_f)
1449 // Object is no longer visible
1450 removed_objects.insert(id);
1454 ActiveObjectMessage ServerEnvironment::getActiveObjectMessage()
1456 if(m_active_object_messages.empty())
1457 return ActiveObjectMessage(0);
1459 ActiveObjectMessage message = m_active_object_messages.front();
1460 m_active_object_messages.pop_front();
1465 ************ Private methods *************
1468 u16 ServerEnvironment::addActiveObjectRaw(ServerActiveObject *object,
1469 bool set_changed, u32 dtime_s)
1472 if(object->getId() == 0){
1473 u16 new_id = getFreeServerActiveObjectId(m_active_objects);
1476 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1477 <<"no free ids available"<<std::endl;
1478 if(object->environmentDeletes())
1482 object->setId(new_id);
1485 verbosestream<<"ServerEnvironment::addActiveObjectRaw(): "
1486 <<"supplied with id "<<object->getId()<<std::endl;
1488 if(isFreeServerActiveObjectId(object->getId(), m_active_objects) == false)
1490 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1491 <<"id is not free ("<<object->getId()<<")"<<std::endl;
1492 if(object->environmentDeletes())
1496 /*infostream<<"ServerEnvironment::addActiveObjectRaw(): "
1497 <<"added (id="<<object->getId()<<")"<<std::endl;*/
1499 m_active_objects[object->getId()] = object;
1501 verbosestream<<"ServerEnvironment::addActiveObjectRaw(): "
1502 <<"Added id="<<object->getId()<<"; there are now "
1503 <<m_active_objects.size()<<" active objects."
1506 // Register reference in scripting api (must be done before post-init)
1507 m_script->addObjectReference(object);
1508 // Post-initialize object
1509 object->addedToEnvironment(dtime_s);
1511 // Add static data to block
1512 if(object->isStaticAllowed())
1514 // Add static object to active static list of the block
1515 v3f objectpos = object->getBasePosition();
1516 std::string staticdata = object->getStaticData();
1517 StaticObject s_obj(object->getType(), objectpos, staticdata);
1518 // Add to the block where the object is located in
1519 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1520 MapBlock *block = m_map->emergeBlock(blockpos);
1522 block->m_static_objects.m_active[object->getId()] = s_obj;
1523 object->m_static_exists = true;
1524 object->m_static_block = blockpos;
1527 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1528 "addActiveObjectRaw");
1530 v3s16 p = floatToInt(objectpos, BS);
1531 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1532 <<"could not emerge block for storing id="<<object->getId()
1533 <<" statically (pos="<<PP(p)<<")"<<std::endl;
1537 return object->getId();
1541 Remove objects that satisfy (m_removed && m_known_by_count==0)
1543 void ServerEnvironment::removeRemovedObjects()
1545 std::list<u16> objects_to_remove;
1546 for(std::map<u16, ServerActiveObject*>::iterator
1547 i = m_active_objects.begin();
1548 i != m_active_objects.end(); ++i)
1551 ServerActiveObject* obj = i->second;
1552 // This shouldn't happen but check it
1555 infostream<<"NULL object found in ServerEnvironment"
1556 <<" while finding removed objects. id="<<id<<std::endl;
1557 // Id to be removed from m_active_objects
1558 objects_to_remove.push_back(id);
1563 We will delete objects that are marked as removed or thatare
1564 waiting for deletion after deactivation
1566 if(obj->m_removed == false && obj->m_pending_deactivation == false)
1570 Delete static data from block if is marked as removed
1572 if(obj->m_static_exists && obj->m_removed)
1574 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
1576 block->m_static_objects.remove(id);
1577 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1578 "removeRemovedObjects/remove");
1579 obj->m_static_exists = false;
1581 infostream<<"Failed to emerge block from which an object to "
1582 <<"be removed was loaded from. id="<<id<<std::endl;
1586 // If m_known_by_count > 0, don't actually remove. On some future
1587 // invocation this will be 0, which is when removal will continue.
1588 if(obj->m_known_by_count > 0)
1592 Move static data from active to stored if not marked as removed
1594 if(obj->m_static_exists && !obj->m_removed){
1595 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
1597 std::map<u16, StaticObject>::iterator i =
1598 block->m_static_objects.m_active.find(id);
1599 if(i != block->m_static_objects.m_active.end()){
1600 block->m_static_objects.m_stored.push_back(i->second);
1601 block->m_static_objects.m_active.erase(id);
1602 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1603 "removeRemovedObjects/deactivate");
1606 infostream<<"Failed to emerge block from which an object to "
1607 <<"be deactivated was loaded from. id="<<id<<std::endl;
1611 // Tell the object about removal
1612 obj->removingFromEnvironment();
1613 // Deregister in scripting api
1614 m_script->removeObjectReference(obj);
1617 if(obj->environmentDeletes())
1619 // Id to be removed from m_active_objects
1620 objects_to_remove.push_back(id);
1622 // Remove references from m_active_objects
1623 for(std::list<u16>::iterator i = objects_to_remove.begin();
1624 i != objects_to_remove.end(); ++i)
1626 m_active_objects.erase(*i);
1630 static void print_hexdump(std::ostream &o, const std::string &data)
1632 const int linelength = 16;
1633 for(int l=0; ; l++){
1634 int i0 = linelength * l;
1635 bool at_end = false;
1636 int thislinelength = linelength;
1637 if(i0 + thislinelength > (int)data.size()){
1638 thislinelength = data.size() - i0;
1641 for(int di=0; di<linelength; di++){
1644 if(di<thislinelength)
1645 snprintf(buf, 4, "%.2x ", data[i]);
1647 snprintf(buf, 4, " ");
1651 for(int di=0; di<thislinelength; di++){
1665 Convert stored objects from blocks near the players to active.
1667 void ServerEnvironment::activateObjects(MapBlock *block, u32 dtime_s)
1671 // Ignore if no stored objects (to not set changed flag)
1672 if(block->m_static_objects.m_stored.size() == 0)
1674 verbosestream<<"ServerEnvironment::activateObjects(): "
1675 <<"activating objects of block "<<PP(block->getPos())
1676 <<" ("<<block->m_static_objects.m_stored.size()
1677 <<" objects)"<<std::endl;
1678 bool large_amount = (block->m_static_objects.m_stored.size() > g_settings->getU16("max_objects_per_block"));
1680 errorstream<<"suspiciously large amount of objects detected: "
1681 <<block->m_static_objects.m_stored.size()<<" in "
1682 <<PP(block->getPos())
1683 <<"; removing all of them."<<std::endl;
1684 // Clear stored list
1685 block->m_static_objects.m_stored.clear();
1686 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1687 "stored list cleared in activateObjects due to "
1688 "large amount of objects");
1692 // Activate stored objects
1693 std::list<StaticObject> new_stored;
1694 for(std::list<StaticObject>::iterator
1695 i = block->m_static_objects.m_stored.begin();
1696 i != block->m_static_objects.m_stored.end(); ++i)
1698 /*infostream<<"Server: Creating an active object from "
1699 <<"static data"<<std::endl;*/
1700 StaticObject &s_obj = *i;
1701 // Create an active object from the data
1702 ServerActiveObject *obj = ServerActiveObject::create
1703 (s_obj.type, this, 0, s_obj.pos, s_obj.data);
1704 // If couldn't create object, store static data back.
1707 errorstream<<"ServerEnvironment::activateObjects(): "
1708 <<"failed to create active object from static object "
1709 <<"in block "<<PP(s_obj.pos/BS)
1710 <<" type="<<(int)s_obj.type<<" data:"<<std::endl;
1711 print_hexdump(verbosestream, s_obj.data);
1713 new_stored.push_back(s_obj);
1716 verbosestream<<"ServerEnvironment::activateObjects(): "
1717 <<"activated static object pos="<<PP(s_obj.pos/BS)
1718 <<" type="<<(int)s_obj.type<<std::endl;
1719 // This will also add the object to the active static list
1720 addActiveObjectRaw(obj, false, dtime_s);
1722 // Clear stored list
1723 block->m_static_objects.m_stored.clear();
1724 // Add leftover failed stuff to stored list
1725 for(std::list<StaticObject>::iterator
1726 i = new_stored.begin();
1727 i != new_stored.end(); ++i)
1729 StaticObject &s_obj = *i;
1730 block->m_static_objects.m_stored.push_back(s_obj);
1733 // Turn the active counterparts of activated objects not pending for
1735 for(std::map<u16, StaticObject>::iterator
1736 i = block->m_static_objects.m_active.begin();
1737 i != block->m_static_objects.m_active.end(); ++i)
1740 ServerActiveObject *object = getActiveObject(id);
1742 object->m_pending_deactivation = false;
1746 Note: Block hasn't really been modified here.
1747 The objects have just been activated and moved from the stored
1748 static list to the active static list.
1749 As such, the block is essentially the same.
1750 Thus, do not call block->raiseModified(MOD_STATE_WRITE_NEEDED).
1751 Otherwise there would be a huge amount of unnecessary I/O.
1756 Convert objects that are not standing inside active blocks to static.
1758 If m_known_by_count != 0, active object is not deleted, but static
1759 data is still updated.
1761 If force_delete is set, active object is deleted nevertheless. It
1762 shall only be set so in the destructor of the environment.
1764 If block wasn't generated (not in memory or on disk),
1766 void ServerEnvironment::deactivateFarObjects(bool force_delete)
1768 std::list<u16> objects_to_remove;
1769 for(std::map<u16, ServerActiveObject*>::iterator
1770 i = m_active_objects.begin();
1771 i != m_active_objects.end(); ++i)
1773 ServerActiveObject* obj = i->second;
1776 // Do not deactivate if static data creation not allowed
1777 if(!force_delete && !obj->isStaticAllowed())
1780 // If pending deactivation, let removeRemovedObjects() do it
1781 if(!force_delete && obj->m_pending_deactivation)
1785 v3f objectpos = obj->getBasePosition();
1787 // The block in which the object resides in
1788 v3s16 blockpos_o = getNodeBlockPos(floatToInt(objectpos, BS));
1790 // If object's static data is stored in a deactivated block and object
1791 // is actually located in an active block, re-save to the block in
1792 // which the object is actually located in.
1794 obj->m_static_exists &&
1795 !m_active_blocks.contains(obj->m_static_block) &&
1796 m_active_blocks.contains(blockpos_o))
1798 v3s16 old_static_block = obj->m_static_block;
1800 // Save to block where object is located
1801 MapBlock *block = m_map->emergeBlock(blockpos_o, false);
1803 errorstream<<"ServerEnvironment::deactivateFarObjects(): "
1804 <<"Could not save object id="<<id
1805 <<" to it's current block "<<PP(blockpos_o)
1809 std::string staticdata_new = obj->getStaticData();
1810 StaticObject s_obj(obj->getType(), objectpos, staticdata_new);
1811 block->m_static_objects.insert(id, s_obj);
1812 obj->m_static_block = blockpos_o;
1813 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1814 "deactivateFarObjects: Static data moved in");
1816 // Delete from block where object was located
1817 block = m_map->emergeBlock(old_static_block, false);
1819 errorstream<<"ServerEnvironment::deactivateFarObjects(): "
1820 <<"Could not delete object id="<<id
1821 <<" from it's previous block "<<PP(old_static_block)
1825 block->m_static_objects.remove(id);
1826 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1827 "deactivateFarObjects: Static data moved out");
1831 // If block is active, don't remove
1832 if(!force_delete && m_active_blocks.contains(blockpos_o))
1835 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
1836 <<"deactivating object id="<<id<<" on inactive block "
1837 <<PP(blockpos_o)<<std::endl;
1839 // If known by some client, don't immediately delete.
1840 bool pending_delete = (obj->m_known_by_count > 0 && !force_delete);
1843 Update the static data
1846 if(obj->isStaticAllowed())
1848 // Create new static object
1849 std::string staticdata_new = obj->getStaticData();
1850 StaticObject s_obj(obj->getType(), objectpos, staticdata_new);
1852 bool stays_in_same_block = false;
1853 bool data_changed = true;
1855 if(obj->m_static_exists){
1856 if(obj->m_static_block == blockpos_o)
1857 stays_in_same_block = true;
1859 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
1861 std::map<u16, StaticObject>::iterator n =
1862 block->m_static_objects.m_active.find(id);
1863 if(n != block->m_static_objects.m_active.end()){
1864 StaticObject static_old = n->second;
1866 float save_movem = obj->getMinimumSavedMovement();
1868 if(static_old.data == staticdata_new &&
1869 (static_old.pos - objectpos).getLength() < save_movem)
1870 data_changed = false;
1872 errorstream<<"ServerEnvironment::deactivateFarObjects(): "
1873 <<"id="<<id<<" m_static_exists=true but "
1874 <<"static data doesn't actually exist in "
1875 <<PP(obj->m_static_block)<<std::endl;
1879 bool shall_be_written = (!stays_in_same_block || data_changed);
1881 // Delete old static object
1882 if(obj->m_static_exists)
1884 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
1887 block->m_static_objects.remove(id);
1888 obj->m_static_exists = false;
1889 // Only mark block as modified if data changed considerably
1890 if(shall_be_written)
1891 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1892 "deactivateFarObjects: Static data "
1893 "changed considerably");
1897 // Add to the block where the object is located in
1898 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1899 // Get or generate the block
1900 MapBlock *block = NULL;
1902 block = m_map->emergeBlock(blockpos);
1903 } catch(InvalidPositionException &e){
1904 // Handled via NULL pointer
1905 // NOTE: emergeBlock's failure is usually determined by it
1906 // actually returning NULL
1911 if(block->m_static_objects.m_stored.size() >= g_settings->getU16("max_objects_per_block")){
1912 errorstream<<"ServerEnv: Trying to store id="<<obj->getId()
1913 <<" statically but block "<<PP(blockpos)
1914 <<" already contains "
1915 <<block->m_static_objects.m_stored.size()
1917 <<" Forcing delete."<<std::endl;
1918 force_delete = true;
1920 // If static counterpart already exists in target block,
1922 // This shouldn't happen because the object is removed from
1923 // the previous block before this according to
1924 // obj->m_static_block, but happens rarely for some unknown
1925 // reason. Unsuccessful attempts have been made to find
1927 if(id && block->m_static_objects.m_active.find(id) != block->m_static_objects.m_active.end()){
1928 infostream<<"ServerEnv: WARNING: Performing hack #83274"
1930 block->m_static_objects.remove(id);
1932 // Store static data
1933 u16 store_id = pending_delete ? id : 0;
1934 block->m_static_objects.insert(store_id, s_obj);
1936 // Only mark block as modified if data changed considerably
1937 if(shall_be_written)
1938 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1939 "deactivateFarObjects: Static data "
1940 "changed considerably");
1942 obj->m_static_exists = true;
1943 obj->m_static_block = block->getPos();
1948 v3s16 p = floatToInt(objectpos, BS);
1949 errorstream<<"ServerEnv: Could not find or generate "
1950 <<"a block for storing id="<<obj->getId()
1951 <<" statically (pos="<<PP(p)<<")"<<std::endl;
1958 If known by some client, set pending deactivation.
1959 Otherwise delete it immediately.
1962 if(pending_delete && !force_delete)
1964 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
1965 <<"object id="<<id<<" is known by clients"
1966 <<"; not deleting yet"<<std::endl;
1968 obj->m_pending_deactivation = true;
1972 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
1973 <<"object id="<<id<<" is not known by clients"
1974 <<"; deleting"<<std::endl;
1976 // Tell the object about removal
1977 obj->removingFromEnvironment();
1978 // Deregister in scripting api
1979 m_script->removeObjectReference(obj);
1981 // Delete active object
1982 if(obj->environmentDeletes())
1984 // Id to be removed from m_active_objects
1985 objects_to_remove.push_back(id);
1988 // Remove references from m_active_objects
1989 for(std::list<u16>::iterator i = objects_to_remove.begin();
1990 i != objects_to_remove.end(); ++i)
1992 m_active_objects.erase(*i);
1999 #include "clientsimpleobject.h"
2005 ClientEnvironment::ClientEnvironment(ClientMap *map, scene::ISceneManager *smgr,
2006 ITextureSource *texturesource, IGameDef *gamedef,
2007 IrrlichtDevice *irr):
2010 m_texturesource(texturesource),
2015 memset(m_attachements, zero, sizeof(m_attachements));
2018 ClientEnvironment::~ClientEnvironment()
2020 // delete active objects
2021 for(std::map<u16, ClientActiveObject*>::iterator
2022 i = m_active_objects.begin();
2023 i != m_active_objects.end(); ++i)
2028 for(std::list<ClientSimpleObject*>::iterator
2029 i = m_simple_objects.begin(); i != m_simple_objects.end(); ++i)
2038 Map & ClientEnvironment::getMap()
2043 ClientMap & ClientEnvironment::getClientMap()
2048 void ClientEnvironment::addPlayer(Player *player)
2050 DSTACK(__FUNCTION_NAME);
2052 It is a failure if player is local and there already is a local
2055 assert(!(player->isLocal() == true && getLocalPlayer() != NULL));
2057 Environment::addPlayer(player);
2060 LocalPlayer * ClientEnvironment::getLocalPlayer()
2062 for(std::list<Player*>::iterator i = m_players.begin();
2063 i != m_players.end(); ++i)
2065 Player *player = *i;
2066 if(player->isLocal())
2067 return (LocalPlayer*)player;
2072 void ClientEnvironment::step(float dtime)
2074 DSTACK(__FUNCTION_NAME);
2076 /* Step time of day */
2077 stepTimeOfDay(dtime);
2079 // Get some settings
2080 bool fly_allowed = m_gamedef->checkLocalPrivilege("fly");
2081 bool free_move = fly_allowed && g_settings->getBool("free_move");
2084 LocalPlayer *lplayer = getLocalPlayer();
2086 // collision info queue
2087 std::list<CollisionInfo> player_collisions;
2090 Get the speed the player is going
2092 bool is_climbing = lplayer->is_climbing;
2094 f32 player_speed = lplayer->getSpeed().getLength();
2097 Maximum position increment
2099 //f32 position_max_increment = 0.05*BS;
2100 f32 position_max_increment = 0.1*BS;
2102 // Maximum time increment (for collision detection etc)
2103 // time = distance / speed
2104 f32 dtime_max_increment = 1;
2105 if(player_speed > 0.001)
2106 dtime_max_increment = position_max_increment / player_speed;
2108 // Maximum time increment is 10ms or lower
2109 if(dtime_max_increment > 0.01)
2110 dtime_max_increment = 0.01;
2112 // Don't allow overly huge dtime
2116 f32 dtime_downcount = dtime;
2119 Stuff that has a maximum time increment
2128 if(dtime_downcount > dtime_max_increment)
2130 dtime_part = dtime_max_increment;
2131 dtime_downcount -= dtime_part;
2135 dtime_part = dtime_downcount;
2137 Setting this to 0 (no -=dtime_part) disables an infinite loop
2138 when dtime_part is so small that dtime_downcount -= dtime_part
2141 dtime_downcount = 0;
2150 if(free_move == false && is_climbing == false)
2153 v3f speed = lplayer->getSpeed();
2154 if(lplayer->in_liquid == false)
2155 speed.Y -= lplayer->movement_gravity * lplayer->physics_override_gravity * dtime_part * 2;
2157 // Liquid floating / sinking
2158 if(lplayer->in_liquid && !lplayer->swimming_vertical)
2159 speed.Y -= lplayer->movement_liquid_sink * dtime_part * 2;
2161 // Liquid resistance
2162 if(lplayer->in_liquid_stable || lplayer->in_liquid)
2164 // How much the node's viscosity blocks movement, ranges between 0 and 1
2165 // Should match the scale at which viscosity increase affects other liquid attributes
2166 const f32 viscosity_factor = 0.3;
2168 v3f d_wanted = -speed / lplayer->movement_liquid_fluidity;
2169 f32 dl = d_wanted.getLength();
2170 if(dl > lplayer->movement_liquid_fluidity_smooth)
2171 dl = lplayer->movement_liquid_fluidity_smooth;
2172 dl *= (lplayer->liquid_viscosity * viscosity_factor) + (1 - viscosity_factor);
2174 v3f d = d_wanted.normalize() * dl;
2178 if(speed.X > lplayer->movement_liquid_fluidity + lplayer->movement_liquid_fluidity_smooth) speed.X -= lplayer->movement_liquid_fluidity_smooth;
2179 if(speed.X < -lplayer->movement_liquid_fluidity - lplayer->movement_liquid_fluidity_smooth) speed.X += 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.Y < -lplayer->movement_liquid_fluidity - lplayer->movement_liquid_fluidity_smooth) speed.Y += 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;
2183 if(speed.Z < -lplayer->movement_liquid_fluidity - lplayer->movement_liquid_fluidity_smooth) speed.Z += lplayer->movement_liquid_fluidity_smooth;
2187 lplayer->setSpeed(speed);
2192 This also does collision detection.
2194 lplayer->move(dtime_part, this, position_max_increment,
2195 &player_collisions);
2198 while(dtime_downcount > 0.001);
2200 //std::cout<<"Looped "<<loopcount<<" times."<<std::endl;
2202 for(std::list<CollisionInfo>::iterator
2203 i = player_collisions.begin();
2204 i != player_collisions.end(); ++i)
2206 CollisionInfo &info = *i;
2207 v3f speed_diff = info.new_speed - info.old_speed;;
2208 // Handle only fall damage
2209 // (because otherwise walking against something in fast_move kills you)
2210 if(speed_diff.Y < 0 || info.old_speed.Y >= 0)
2212 // Get rid of other components
2215 f32 pre_factor = 1; // 1 hp per node/s
2216 f32 tolerance = BS*14; // 5 without damage
2217 f32 post_factor = 1; // 1 hp per node/s
2218 if(info.type == COLLISION_NODE)
2220 const ContentFeatures &f = m_gamedef->ndef()->
2221 get(m_map->getNodeNoEx(info.node_p));
2222 // Determine fall damage multiplier
2223 int addp = itemgroup_get(f.groups, "fall_damage_add_percent");
2224 pre_factor = 1.0 + (float)addp/100.0;
2226 float speed = pre_factor * speed_diff.getLength();
2227 if(speed > tolerance)
2229 f32 damage_f = (speed - tolerance)/BS * post_factor;
2230 u16 damage = (u16)(damage_f+0.5);
2232 damageLocalPlayer(damage, true);
2233 MtEvent *e = new SimpleTriggerEvent("PlayerFallingDamage");
2234 m_gamedef->event()->put(e);
2240 A quick draft of lava damage
2242 if(m_lava_hurt_interval.step(dtime, 1.0))
2244 v3f pf = lplayer->getPosition();
2246 // Feet, middle and head
2247 v3s16 p1 = floatToInt(pf + v3f(0, BS*0.1, 0), BS);
2248 MapNode n1 = m_map->getNodeNoEx(p1);
2249 v3s16 p2 = floatToInt(pf + v3f(0, BS*0.8, 0), BS);
2250 MapNode n2 = m_map->getNodeNoEx(p2);
2251 v3s16 p3 = floatToInt(pf + v3f(0, BS*1.6, 0), BS);
2252 MapNode n3 = m_map->getNodeNoEx(p3);
2254 u32 damage_per_second = 0;
2255 damage_per_second = MYMAX(damage_per_second,
2256 m_gamedef->ndef()->get(n1).damage_per_second);
2257 damage_per_second = MYMAX(damage_per_second,
2258 m_gamedef->ndef()->get(n2).damage_per_second);
2259 damage_per_second = MYMAX(damage_per_second,
2260 m_gamedef->ndef()->get(n3).damage_per_second);
2262 if(damage_per_second != 0)
2264 damageLocalPlayer(damage_per_second, true);
2271 if(m_drowning_interval.step(dtime, 2.0))
2273 v3f pf = lplayer->getPosition();
2276 v3s16 p = floatToInt(pf + v3f(0, BS*1.6, 0), BS);
2277 MapNode n = m_map->getNodeNoEx(p);
2278 ContentFeatures c = m_gamedef->ndef()->get(n);
2279 u8 drowning_damage = c.drowning;
2280 if(drowning_damage > 0 && lplayer->hp > 0){
2281 u16 breath = lplayer->getBreath();
2288 lplayer->setBreath(breath);
2289 updateLocalPlayerBreath(breath);
2292 if(lplayer->getBreath() == 0 && drowning_damage > 0){
2293 damageLocalPlayer(drowning_damage, true);
2296 if(m_breathing_interval.step(dtime, 0.5))
2298 v3f pf = lplayer->getPosition();
2301 v3s16 p = floatToInt(pf + v3f(0, BS*1.6, 0), BS);
2302 MapNode n = m_map->getNodeNoEx(p);
2303 ContentFeatures c = m_gamedef->ndef()->get(n);
2305 lplayer->setBreath(11);
2307 else if(c.drowning == 0){
2308 u16 breath = lplayer->getBreath();
2311 lplayer->setBreath(breath);
2312 updateLocalPlayerBreath(breath);
2318 Stuff that can be done in an arbitarily large dtime
2320 for(std::list<Player*>::iterator i = m_players.begin();
2321 i != m_players.end(); ++i)
2323 Player *player = *i;
2326 Handle non-local players
2328 if(player->isLocal() == false)
2331 player->move(dtime, this, 100*BS);
2336 // Update lighting on local player (used for wield item)
2337 u32 day_night_ratio = getDayNightRatio();
2341 // On InvalidPositionException, use this as default
2342 // (day: LIGHT_SUN, night: 0)
2343 MapNode node_at_lplayer(CONTENT_AIR, 0x0f, 0);
2345 v3s16 p = lplayer->getLightPosition();
2346 node_at_lplayer = m_map->getNodeNoEx(p);
2348 u16 light = getInteriorLight(node_at_lplayer, 0, m_gamedef->ndef());
2349 u8 day = light & 0xff;
2350 u8 night = (light >> 8) & 0xff;
2351 finalColorBlend(lplayer->light_color, day, night, day_night_ratio);
2355 Step active objects and update lighting of them
2358 g_profiler->avg("CEnv: num of objects", m_active_objects.size());
2359 bool update_lighting = m_active_object_light_update_interval.step(dtime, 0.21);
2360 for(std::map<u16, ClientActiveObject*>::iterator
2361 i = m_active_objects.begin();
2362 i != m_active_objects.end(); ++i)
2364 ClientActiveObject* obj = i->second;
2366 obj->step(dtime, this);
2375 v3s16 p = obj->getLightPosition();
2376 MapNode n = m_map->getNodeNoEx(p, &pos_ok);
2378 light = n.getLightBlend(day_night_ratio, m_gamedef->ndef());
2380 light = blend_light(day_night_ratio, LIGHT_SUN, 0);
2382 obj->updateLight(light);
2387 Step and handle simple objects
2389 g_profiler->avg("CEnv: num of simple objects", m_simple_objects.size());
2390 for(std::list<ClientSimpleObject*>::iterator
2391 i = m_simple_objects.begin(); i != m_simple_objects.end();)
2393 ClientSimpleObject *simple = *i;
2394 std::list<ClientSimpleObject*>::iterator cur = i;
2396 simple->step(dtime);
2397 if(simple->m_to_be_removed){
2399 m_simple_objects.erase(cur);
2404 void ClientEnvironment::addSimpleObject(ClientSimpleObject *simple)
2406 m_simple_objects.push_back(simple);
2409 ClientActiveObject* ClientEnvironment::getActiveObject(u16 id)
2411 std::map<u16, ClientActiveObject*>::iterator n;
2412 n = m_active_objects.find(id);
2413 if(n == m_active_objects.end())
2418 bool isFreeClientActiveObjectId(u16 id,
2419 std::map<u16, ClientActiveObject*> &objects)
2424 return objects.find(id) == objects.end();
2427 u16 getFreeClientActiveObjectId(
2428 std::map<u16, ClientActiveObject*> &objects)
2430 //try to reuse id's as late as possible
2431 static u16 last_used_id = 0;
2432 u16 startid = last_used_id;
2436 if(isFreeClientActiveObjectId(last_used_id, objects))
2437 return last_used_id;
2439 if(last_used_id == startid)
2444 u16 ClientEnvironment::addActiveObject(ClientActiveObject *object)
2447 if(object->getId() == 0)
2449 u16 new_id = getFreeClientActiveObjectId(m_active_objects);
2452 infostream<<"ClientEnvironment::addActiveObject(): "
2453 <<"no free ids available"<<std::endl;
2457 object->setId(new_id);
2459 if(isFreeClientActiveObjectId(object->getId(), m_active_objects) == false)
2461 infostream<<"ClientEnvironment::addActiveObject(): "
2462 <<"id is not free ("<<object->getId()<<")"<<std::endl;
2466 infostream<<"ClientEnvironment::addActiveObject(): "
2467 <<"added (id="<<object->getId()<<")"<<std::endl;
2468 m_active_objects[object->getId()] = object;
2469 object->addToScene(m_smgr, m_texturesource, m_irr);
2470 { // Update lighting immediately
2475 v3s16 p = object->getLightPosition();
2476 MapNode n = m_map->getNodeNoEx(p, &pos_ok);
2478 light = n.getLightBlend(getDayNightRatio(), m_gamedef->ndef());
2480 light = blend_light(getDayNightRatio(), LIGHT_SUN, 0);
2482 object->updateLight(light);
2484 return object->getId();
2487 void ClientEnvironment::addActiveObject(u16 id, u8 type,
2488 const std::string &init_data)
2490 ClientActiveObject* obj =
2491 ClientActiveObject::create(type, m_gamedef, this);
2494 infostream<<"ClientEnvironment::addActiveObject(): "
2495 <<"id="<<id<<" type="<<type<<": Couldn't create object"
2504 obj->initialize(init_data);
2506 catch(SerializationError &e)
2508 errorstream<<"ClientEnvironment::addActiveObject():"
2509 <<" id="<<id<<" type="<<type
2510 <<": SerializationError in initialize(): "
2512 <<": init_data="<<serializeJsonString(init_data)
2516 addActiveObject(obj);
2519 void ClientEnvironment::removeActiveObject(u16 id)
2521 verbosestream<<"ClientEnvironment::removeActiveObject(): "
2522 <<"id="<<id<<std::endl;
2523 ClientActiveObject* obj = getActiveObject(id);
2526 infostream<<"ClientEnvironment::removeActiveObject(): "
2527 <<"id="<<id<<" not found"<<std::endl;
2530 obj->removeFromScene(true);
2532 m_active_objects.erase(id);
2535 void ClientEnvironment::processActiveObjectMessage(u16 id,
2536 const std::string &data)
2538 ClientActiveObject* obj = getActiveObject(id);
2541 infostream<<"ClientEnvironment::processActiveObjectMessage():"
2542 <<" got message for id="<<id<<", which doesn't exist."
2548 obj->processMessage(data);
2550 catch(SerializationError &e)
2552 errorstream<<"ClientEnvironment::processActiveObjectMessage():"
2553 <<" id="<<id<<" type="<<obj->getType()
2554 <<" SerializationError in processMessage(),"
2555 <<" message="<<serializeJsonString(data)
2561 Callbacks for activeobjects
2564 void ClientEnvironment::damageLocalPlayer(u8 damage, bool handle_hp)
2566 LocalPlayer *lplayer = getLocalPlayer();
2570 if(lplayer->hp > damage)
2571 lplayer->hp -= damage;
2576 ClientEnvEvent event;
2577 event.type = CEE_PLAYER_DAMAGE;
2578 event.player_damage.amount = damage;
2579 event.player_damage.send_to_server = handle_hp;
2580 m_client_event_queue.push_back(event);
2583 void ClientEnvironment::updateLocalPlayerBreath(u16 breath)
2585 ClientEnvEvent event;
2586 event.type = CEE_PLAYER_BREATH;
2587 event.player_breath.amount = breath;
2588 m_client_event_queue.push_back(event);
2592 Client likes to call these
2595 void ClientEnvironment::getActiveObjects(v3f origin, f32 max_d,
2596 std::vector<DistanceSortedActiveObject> &dest)
2598 for(std::map<u16, ClientActiveObject*>::iterator
2599 i = m_active_objects.begin();
2600 i != m_active_objects.end(); ++i)
2602 ClientActiveObject* obj = i->second;
2604 f32 d = (obj->getPosition() - origin).getLength();
2609 DistanceSortedActiveObject dso(obj, d);
2611 dest.push_back(dso);
2615 ClientEnvEvent ClientEnvironment::getClientEvent()
2617 ClientEnvEvent event;
2618 if(m_client_event_queue.empty())
2619 event.type = CEE_NONE;
2621 event = m_client_event_queue.front();
2622 m_client_event_queue.pop_front();
2627 #endif // #ifndef SERVER