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);
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;
484 void ServerEnvironment::saveMeta()
486 std::string path = m_path_world + DIR_DELIM "env_meta.txt";
488 // Open file and serialize
489 std::ostringstream ss(std::ios_base::binary);
492 args.setU64("game_time", m_game_time);
493 args.setU64("time_of_day", getTimeOfDay());
497 if(!fs::safeWriteToFile(path, ss.str()))
499 infostream<<"ServerEnvironment::saveMeta(): Failed to write "
501 throw SerializationError("Couldn't save env meta");
505 void ServerEnvironment::loadMeta()
507 std::string path = m_path_world + DIR_DELIM "env_meta.txt";
509 // Open file and deserialize
510 std::ifstream is(path.c_str(), std::ios_base::binary);
511 if(is.good() == false)
513 infostream<<"ServerEnvironment::loadMeta(): Failed to open "
515 throw SerializationError("Couldn't load env meta");
523 throw SerializationError
524 ("ServerEnvironment::loadMeta(): EnvArgsEnd not found");
526 std::getline(is, line);
527 std::string trimmedline = trim(line);
528 if(trimmedline == "EnvArgsEnd")
530 args.parseConfigLine(line);
534 m_game_time = args.getU64("game_time");
535 }catch(SettingNotFoundException &e){
536 // Getting this is crucial, otherwise timestamps are useless
537 throw SerializationError("Couldn't load env meta game_time");
541 m_time_of_day = args.getU64("time_of_day");
542 }catch(SettingNotFoundException &e){
543 // This is not as important
544 m_time_of_day = 9000;
550 ActiveBlockModifier *abm;
552 std::set<content_t> required_neighbors;
558 ServerEnvironment *m_env;
559 std::map<content_t, std::list<ActiveABM> > m_aabms;
561 ABMHandler(std::list<ABMWithState> &abms,
562 float dtime_s, ServerEnvironment *env,
568 INodeDefManager *ndef = env->getGameDef()->ndef();
569 for(std::list<ABMWithState>::iterator
570 i = abms.begin(); i != abms.end(); ++i){
571 ActiveBlockModifier *abm = i->abm;
572 float trigger_interval = abm->getTriggerInterval();
573 if(trigger_interval < 0.001)
574 trigger_interval = 0.001;
575 float actual_interval = dtime_s;
578 if(i->timer < trigger_interval)
580 i->timer -= trigger_interval;
581 actual_interval = trigger_interval;
583 float intervals = actual_interval / trigger_interval;
586 float chance = abm->getTriggerChance();
591 aabm.chance = chance / intervals;
595 std::set<std::string> required_neighbors_s
596 = abm->getRequiredNeighbors();
597 for(std::set<std::string>::iterator
598 i = required_neighbors_s.begin();
599 i != required_neighbors_s.end(); i++)
601 ndef->getIds(*i, aabm.required_neighbors);
604 std::set<std::string> contents_s = abm->getTriggerContents();
605 for(std::set<std::string>::iterator
606 i = contents_s.begin(); i != contents_s.end(); i++)
608 std::set<content_t> ids;
609 ndef->getIds(*i, ids);
610 for(std::set<content_t>::const_iterator k = ids.begin();
614 std::map<content_t, std::list<ActiveABM> >::iterator j;
616 if(j == m_aabms.end()){
617 std::list<ActiveABM> aabmlist;
618 m_aabms[c] = aabmlist;
621 j->second.push_back(aabm);
626 // Find out how many objects the given block and its neighbours contain.
627 // Returns the number of objects in the block, and also in 'wider' the
628 // number of objects in the block and all its neighbours. The latter
629 // may an estimate if any neighbours are unloaded.
630 u32 countObjects(MapBlock *block, ServerMap * map, u32 &wider)
633 u32 wider_unknown_count = 0;
634 for(s16 x=-1; x<=1; x++)
635 for(s16 y=-1; y<=1; y++)
636 for(s16 z=-1; z<=1; z++)
638 MapBlock *block2 = map->getBlockNoCreateNoEx(
639 block->getPos() + v3s16(x,y,z));
641 wider_unknown_count++;
644 wider += block2->m_static_objects.m_active.size()
645 + block2->m_static_objects.m_stored.size();
648 u32 active_object_count = block->m_static_objects.m_active.size();
649 u32 wider_known_count = 3*3*3 - wider_unknown_count;
650 wider += wider_unknown_count * wider / wider_known_count;
651 return active_object_count;
654 void apply(MapBlock *block)
659 ServerMap *map = &m_env->getServerMap();
661 u32 active_object_count_wider;
662 u32 active_object_count = this->countObjects(block, map, active_object_count_wider);
663 m_env->m_added_objects = 0;
666 for(p0.X=0; p0.X<MAP_BLOCKSIZE; p0.X++)
667 for(p0.Y=0; p0.Y<MAP_BLOCKSIZE; p0.Y++)
668 for(p0.Z=0; p0.Z<MAP_BLOCKSIZE; p0.Z++)
670 MapNode n = block->getNodeNoEx(p0);
671 content_t c = n.getContent();
672 v3s16 p = p0 + block->getPosRelative();
674 std::map<content_t, std::list<ActiveABM> >::iterator j;
676 if(j == m_aabms.end())
679 for(std::list<ActiveABM>::iterator
680 i = j->second.begin(); i != j->second.end(); i++)
682 if(myrand() % i->chance != 0)
686 if(!i->required_neighbors.empty())
689 for(p1.X = p.X-1; p1.X <= p.X+1; p1.X++)
690 for(p1.Y = p.Y-1; p1.Y <= p.Y+1; p1.Y++)
691 for(p1.Z = p.Z-1; p1.Z <= p.Z+1; p1.Z++)
695 MapNode n = map->getNodeNoEx(p1);
696 content_t c = n.getContent();
697 std::set<content_t>::const_iterator k;
698 k = i->required_neighbors.find(c);
699 if(k != i->required_neighbors.end()){
703 // No required neighbor found
708 // Call all the trigger variations
709 i->abm->trigger(m_env, p, n);
710 i->abm->trigger(m_env, p, n,
711 active_object_count, active_object_count_wider);
713 // Count surrounding objects again if the abms added any
714 if(m_env->m_added_objects > 0) {
715 active_object_count = countObjects(block, map, active_object_count_wider);
716 m_env->m_added_objects = 0;
723 void ServerEnvironment::activateBlock(MapBlock *block, u32 additional_dtime)
725 // Reset usage timer immediately, otherwise a block that becomes active
726 // again at around the same time as it would normally be unloaded will
727 // get unloaded incorrectly. (I think this still leaves a small possibility
728 // of a race condition between this and server::AsyncRunStep, which only
729 // some kind of synchronisation will fix, but it at least reduces the window
730 // of opportunity for it to break from seconds to nanoseconds)
731 block->resetUsageTimer();
733 // Get time difference
735 u32 stamp = block->getTimestamp();
736 if(m_game_time > stamp && stamp != BLOCK_TIMESTAMP_UNDEFINED)
737 dtime_s = m_game_time - block->getTimestamp();
738 dtime_s += additional_dtime;
740 /*infostream<<"ServerEnvironment::activateBlock(): block timestamp: "
741 <<stamp<<", game time: "<<m_game_time<<std::endl;*/
743 // Set current time as timestamp
744 block->setTimestampNoChangedFlag(m_game_time);
746 /*infostream<<"ServerEnvironment::activateBlock(): block is "
747 <<dtime_s<<" seconds old."<<std::endl;*/
749 // Activate stored objects
750 activateObjects(block, dtime_s);
753 std::map<v3s16, NodeTimer> elapsed_timers =
754 block->m_node_timers.step((float)dtime_s);
755 if(!elapsed_timers.empty()){
757 for(std::map<v3s16, NodeTimer>::iterator
758 i = elapsed_timers.begin();
759 i != elapsed_timers.end(); i++){
760 n = block->getNodeNoEx(i->first);
761 v3s16 p = i->first + block->getPosRelative();
762 if(m_script->node_on_timer(p,n,i->second.elapsed))
763 block->setNodeTimer(i->first,NodeTimer(i->second.timeout,0));
767 /* Handle ActiveBlockModifiers */
768 ABMHandler abmhandler(m_abms, dtime_s, this, false);
769 abmhandler.apply(block);
772 void ServerEnvironment::addActiveBlockModifier(ActiveBlockModifier *abm)
774 m_abms.push_back(ABMWithState(abm));
777 bool ServerEnvironment::setNode(v3s16 p, const MapNode &n)
779 INodeDefManager *ndef = m_gamedef->ndef();
780 MapNode n_old = m_map->getNodeNoEx(p);
783 if (ndef->get(n_old).has_on_destruct)
784 m_script->node_on_destruct(p, n_old);
787 if (!m_map->addNodeWithEvent(p, n))
790 // Update active VoxelManipulator if a mapgen thread
791 m_map->updateVManip(p);
793 // Call post-destructor
794 if (ndef->get(n_old).has_after_destruct)
795 m_script->node_after_destruct(p, n_old);
798 if (ndef->get(n).has_on_construct)
799 m_script->node_on_construct(p, n);
804 bool ServerEnvironment::removeNode(v3s16 p)
806 INodeDefManager *ndef = m_gamedef->ndef();
807 MapNode n_old = m_map->getNodeNoEx(p);
810 if (ndef->get(n_old).has_on_destruct)
811 m_script->node_on_destruct(p, n_old);
814 // This is slightly optimized compared to addNodeWithEvent(air)
815 if (!m_map->removeNodeWithEvent(p))
818 // Update active VoxelManipulator if a mapgen thread
819 m_map->updateVManip(p);
821 // Call post-destructor
822 if (ndef->get(n_old).has_after_destruct)
823 m_script->node_after_destruct(p, n_old);
825 // Air doesn't require constructor
829 bool ServerEnvironment::swapNode(v3s16 p, const MapNode &n)
831 if (!m_map->addNodeWithEvent(p, n, false))
834 // Update active VoxelManipulator if a mapgen thread
835 m_map->updateVManip(p);
840 std::set<u16> ServerEnvironment::getObjectsInsideRadius(v3f pos, float radius)
842 std::set<u16> objects;
843 for(std::map<u16, ServerActiveObject*>::iterator
844 i = m_active_objects.begin();
845 i != m_active_objects.end(); ++i)
847 ServerActiveObject* obj = i->second;
849 v3f objectpos = obj->getBasePosition();
850 if(objectpos.getDistanceFrom(pos) > radius)
857 void ServerEnvironment::clearAllObjects()
859 infostream<<"ServerEnvironment::clearAllObjects(): "
860 <<"Removing all active objects"<<std::endl;
861 std::list<u16> objects_to_remove;
862 for(std::map<u16, ServerActiveObject*>::iterator
863 i = m_active_objects.begin();
864 i != m_active_objects.end(); ++i)
866 ServerActiveObject* obj = i->second;
867 if(obj->getType() == ACTIVEOBJECT_TYPE_PLAYER)
870 // Delete static object if block is loaded
871 if(obj->m_static_exists){
872 MapBlock *block = m_map->getBlockNoCreateNoEx(obj->m_static_block);
874 block->m_static_objects.remove(id);
875 block->raiseModified(MOD_STATE_WRITE_NEEDED,
877 obj->m_static_exists = false;
880 // If known by some client, don't delete immediately
881 if(obj->m_known_by_count > 0){
882 obj->m_pending_deactivation = true;
883 obj->m_removed = true;
887 // Tell the object about removal
888 obj->removingFromEnvironment();
889 // Deregister in scripting api
890 m_script->removeObjectReference(obj);
892 // Delete active object
893 if(obj->environmentDeletes())
895 // Id to be removed from m_active_objects
896 objects_to_remove.push_back(id);
898 // Remove references from m_active_objects
899 for(std::list<u16>::iterator i = objects_to_remove.begin();
900 i != objects_to_remove.end(); ++i)
902 m_active_objects.erase(*i);
905 // Get list of loaded blocks
906 std::list<v3s16> loaded_blocks;
907 infostream<<"ServerEnvironment::clearAllObjects(): "
908 <<"Listing all loaded blocks"<<std::endl;
909 m_map->listAllLoadedBlocks(loaded_blocks);
910 infostream<<"ServerEnvironment::clearAllObjects(): "
911 <<"Done listing all loaded blocks: "
912 <<loaded_blocks.size()<<std::endl;
914 // Get list of loadable blocks
915 std::list<v3s16> loadable_blocks;
916 infostream<<"ServerEnvironment::clearAllObjects(): "
917 <<"Listing all loadable blocks"<<std::endl;
918 m_map->listAllLoadableBlocks(loadable_blocks);
919 infostream<<"ServerEnvironment::clearAllObjects(): "
920 <<"Done listing all loadable blocks: "
921 <<loadable_blocks.size()
922 <<", now clearing"<<std::endl;
924 // Grab a reference on each loaded block to avoid unloading it
925 for(std::list<v3s16>::iterator i = loaded_blocks.begin();
926 i != loaded_blocks.end(); ++i)
929 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
934 // Remove objects in all loadable blocks
935 u32 unload_interval = g_settings->getS32("max_clearobjects_extra_loaded_blocks");
936 unload_interval = MYMAX(unload_interval, 1);
937 u32 report_interval = loadable_blocks.size() / 10;
938 u32 num_blocks_checked = 0;
939 u32 num_blocks_cleared = 0;
940 u32 num_objs_cleared = 0;
941 for(std::list<v3s16>::iterator i = loadable_blocks.begin();
942 i != loadable_blocks.end(); ++i)
945 MapBlock *block = m_map->emergeBlock(p, false);
947 errorstream<<"ServerEnvironment::clearAllObjects(): "
948 <<"Failed to emerge block "<<PP(p)<<std::endl;
951 u32 num_stored = block->m_static_objects.m_stored.size();
952 u32 num_active = block->m_static_objects.m_active.size();
953 if(num_stored != 0 || num_active != 0){
954 block->m_static_objects.m_stored.clear();
955 block->m_static_objects.m_active.clear();
956 block->raiseModified(MOD_STATE_WRITE_NEEDED,
958 num_objs_cleared += num_stored + num_active;
959 num_blocks_cleared++;
961 num_blocks_checked++;
963 if(report_interval != 0 &&
964 num_blocks_checked % report_interval == 0){
965 float percent = 100.0 * (float)num_blocks_checked /
966 loadable_blocks.size();
967 infostream<<"ServerEnvironment::clearAllObjects(): "
968 <<"Cleared "<<num_objs_cleared<<" objects"
969 <<" in "<<num_blocks_cleared<<" blocks ("
970 <<percent<<"%)"<<std::endl;
972 if(num_blocks_checked % unload_interval == 0){
973 m_map->unloadUnreferencedBlocks();
976 m_map->unloadUnreferencedBlocks();
978 // Drop references that were added above
979 for(std::list<v3s16>::iterator i = loaded_blocks.begin();
980 i != loaded_blocks.end(); ++i)
983 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
988 infostream<<"ServerEnvironment::clearAllObjects(): "
989 <<"Finished: Cleared "<<num_objs_cleared<<" objects"
990 <<" in "<<num_blocks_cleared<<" blocks"<<std::endl;
993 void ServerEnvironment::step(float dtime)
995 DSTACK(__FUNCTION_NAME);
997 //TimeTaker timer("ServerEnv step");
999 /* Step time of day */
1000 stepTimeOfDay(dtime);
1003 // NOTE: This is kind of funny on a singleplayer game, but doesn't
1004 // really matter that much.
1005 m_recommended_send_interval = g_settings->getFloat("dedicated_server_step");
1011 m_game_time_fraction_counter += dtime;
1012 u32 inc_i = (u32)m_game_time_fraction_counter;
1013 m_game_time += inc_i;
1014 m_game_time_fraction_counter -= (float)inc_i;
1021 ScopeProfiler sp(g_profiler, "SEnv: handle players avg", SPT_AVG);
1022 for(std::list<Player*>::iterator i = m_players.begin();
1023 i != m_players.end(); ++i)
1025 Player *player = *i;
1027 // Ignore disconnected players
1028 if(player->peer_id == 0)
1032 player->move(dtime, this, 100*BS);
1037 Manage active block list
1039 if(m_active_blocks_management_interval.step(dtime, 2.0))
1041 ScopeProfiler sp(g_profiler, "SEnv: manage act. block list avg /2s", SPT_AVG);
1043 Get player block positions
1045 std::list<v3s16> players_blockpos;
1046 for(std::list<Player*>::iterator
1047 i = m_players.begin();
1048 i != m_players.end(); ++i)
1050 Player *player = *i;
1051 // Ignore disconnected players
1052 if(player->peer_id == 0)
1054 v3s16 blockpos = getNodeBlockPos(
1055 floatToInt(player->getPosition(), BS));
1056 players_blockpos.push_back(blockpos);
1060 Update list of active blocks, collecting changes
1062 const s16 active_block_range = g_settings->getS16("active_block_range");
1063 std::set<v3s16> blocks_removed;
1064 std::set<v3s16> blocks_added;
1065 m_active_blocks.update(players_blockpos, active_block_range,
1066 blocks_removed, blocks_added);
1069 Handle removed blocks
1072 // Convert active objects that are no more in active blocks to static
1073 deactivateFarObjects(false);
1075 for(std::set<v3s16>::iterator
1076 i = blocks_removed.begin();
1077 i != blocks_removed.end(); ++i)
1081 /* infostream<<"Server: Block " << PP(p)
1082 << " became inactive"<<std::endl; */
1084 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1088 // Set current time as timestamp (and let it set ChangedFlag)
1089 block->setTimestamp(m_game_time);
1096 for(std::set<v3s16>::iterator
1097 i = blocks_added.begin();
1098 i != blocks_added.end(); ++i)
1102 MapBlock *block = m_map->getBlockOrEmerge(p);
1104 m_active_blocks.m_list.erase(p);
1108 activateBlock(block);
1109 /* infostream<<"Server: Block " << PP(p)
1110 << " became active"<<std::endl; */
1115 Mess around in active blocks
1117 if(m_active_blocks_nodemetadata_interval.step(dtime, 1.0))
1119 ScopeProfiler sp(g_profiler, "SEnv: mess in act. blocks avg /1s", SPT_AVG);
1123 for(std::set<v3s16>::iterator
1124 i = m_active_blocks.m_list.begin();
1125 i != m_active_blocks.m_list.end(); ++i)
1129 /*infostream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
1130 <<") being handled"<<std::endl;*/
1132 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1136 // Reset block usage timer
1137 block->resetUsageTimer();
1139 // Set current time as timestamp
1140 block->setTimestampNoChangedFlag(m_game_time);
1141 // If time has changed much from the one on disk,
1142 // set block to be saved when it is unloaded
1143 if(block->getTimestamp() > block->getDiskTimestamp() + 60)
1144 block->raiseModified(MOD_STATE_WRITE_AT_UNLOAD,
1145 "Timestamp older than 60s (step)");
1148 std::map<v3s16, NodeTimer> elapsed_timers =
1149 block->m_node_timers.step((float)dtime);
1150 if(!elapsed_timers.empty()){
1152 for(std::map<v3s16, NodeTimer>::iterator
1153 i = elapsed_timers.begin();
1154 i != elapsed_timers.end(); i++){
1155 n = block->getNodeNoEx(i->first);
1156 p = i->first + block->getPosRelative();
1157 if(m_script->node_on_timer(p,n,i->second.elapsed))
1158 block->setNodeTimer(i->first,NodeTimer(i->second.timeout,0));
1164 const float abm_interval = 1.0;
1165 if(m_active_block_modifier_interval.step(dtime, abm_interval))
1167 if(m_active_block_interval_overload_skip > 0){
1168 ScopeProfiler sp(g_profiler, "SEnv: ABM overload skips");
1169 m_active_block_interval_overload_skip--;
1172 ScopeProfiler sp(g_profiler, "SEnv: modify in blocks avg /1s", SPT_AVG);
1173 TimeTaker timer("modify in active blocks");
1175 // Initialize handling of ActiveBlockModifiers
1176 ABMHandler abmhandler(m_abms, abm_interval, this, true);
1178 for(std::set<v3s16>::iterator
1179 i = m_active_blocks.m_list.begin();
1180 i != m_active_blocks.m_list.end(); ++i)
1184 /*infostream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
1185 <<") being handled"<<std::endl;*/
1187 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1191 // Set current time as timestamp
1192 block->setTimestampNoChangedFlag(m_game_time);
1194 /* Handle ActiveBlockModifiers */
1195 abmhandler.apply(block);
1198 u32 time_ms = timer.stop(true);
1199 u32 max_time_ms = 200;
1200 if(time_ms > max_time_ms){
1201 infostream<<"WARNING: active block modifiers took "
1202 <<time_ms<<"ms (longer than "
1203 <<max_time_ms<<"ms)"<<std::endl;
1204 m_active_block_interval_overload_skip = (time_ms / max_time_ms) + 1;
1209 Step script environment (run global on_step())
1211 m_script->environment_Step(dtime);
1217 ScopeProfiler sp(g_profiler, "SEnv: step act. objs avg", SPT_AVG);
1218 //TimeTaker timer("Step active objects");
1220 g_profiler->avg("SEnv: num of objects", m_active_objects.size());
1222 // This helps the objects to send data at the same time
1223 bool send_recommended = false;
1224 m_send_recommended_timer += dtime;
1225 if(m_send_recommended_timer > getSendRecommendedInterval())
1227 m_send_recommended_timer -= getSendRecommendedInterval();
1228 send_recommended = true;
1231 for(std::map<u16, ServerActiveObject*>::iterator
1232 i = m_active_objects.begin();
1233 i != m_active_objects.end(); ++i)
1235 ServerActiveObject* obj = i->second;
1236 // Remove non-peaceful mobs on peaceful mode
1237 if(g_settings->getBool("only_peaceful_mobs")){
1238 if(!obj->isPeaceful())
1239 obj->m_removed = true;
1241 // Don't step if is to be removed or stored statically
1242 if(obj->m_removed || obj->m_pending_deactivation)
1245 obj->step(dtime, send_recommended);
1246 // Read messages from object
1247 while(!obj->m_messages_out.empty())
1249 m_active_object_messages.push_back(
1250 obj->m_messages_out.pop_front());
1256 Manage active objects
1258 if(m_object_management_interval.step(dtime, 0.5))
1260 ScopeProfiler sp(g_profiler, "SEnv: remove removed objs avg /.5s", SPT_AVG);
1262 Remove objects that satisfy (m_removed && m_known_by_count==0)
1264 removeRemovedObjects();
1268 ServerActiveObject* ServerEnvironment::getActiveObject(u16 id)
1270 std::map<u16, ServerActiveObject*>::iterator n;
1271 n = m_active_objects.find(id);
1272 if(n == m_active_objects.end())
1277 bool isFreeServerActiveObjectId(u16 id,
1278 std::map<u16, ServerActiveObject*> &objects)
1283 return objects.find(id) == objects.end();
1286 u16 getFreeServerActiveObjectId(
1287 std::map<u16, ServerActiveObject*> &objects)
1289 //try to reuse id's as late as possible
1290 static u16 last_used_id = 0;
1291 u16 startid = last_used_id;
1295 if(isFreeServerActiveObjectId(last_used_id, objects))
1296 return last_used_id;
1298 if(last_used_id == startid)
1303 u16 ServerEnvironment::addActiveObject(ServerActiveObject *object)
1307 u16 id = addActiveObjectRaw(object, true, 0);
1312 bool ServerEnvironment::addActiveObjectAsStatic(ServerActiveObject *obj)
1316 v3f objectpos = obj->getBasePosition();
1318 // The block in which the object resides in
1319 v3s16 blockpos_o = getNodeBlockPos(floatToInt(objectpos, BS));
1322 Update the static data
1325 // Create new static object
1326 std::string staticdata = obj->getStaticData();
1327 StaticObject s_obj(obj->getType(), objectpos, staticdata);
1328 // Add to the block where the object is located in
1329 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1330 // Get or generate the block
1331 MapBlock *block = m_map->emergeBlock(blockpos);
1333 bool succeeded = false;
1337 block->m_static_objects.insert(0, s_obj);
1338 block->raiseModified(MOD_STATE_WRITE_AT_UNLOAD,
1339 "addActiveObjectAsStatic");
1343 infostream<<"ServerEnvironment::addActiveObjectAsStatic: "
1344 <<"Could not find or generate "
1345 <<"a block for storing static object"<<std::endl;
1349 if(obj->environmentDeletes())
1357 Finds out what new objects have been added to
1358 inside a radius around a position
1360 void ServerEnvironment::getAddedActiveObjects(v3s16 pos, s16 radius,
1361 std::set<u16> ¤t_objects,
1362 std::set<u16> &added_objects)
1364 v3f pos_f = intToFloat(pos, BS);
1365 f32 radius_f = radius * BS;
1367 Go through the object list,
1368 - discard m_removed objects,
1369 - discard objects that are too far away,
1370 - discard objects that are found in current_objects.
1371 - add remaining objects to added_objects
1373 for(std::map<u16, ServerActiveObject*>::iterator
1374 i = m_active_objects.begin();
1375 i != m_active_objects.end(); ++i)
1379 ServerActiveObject *object = i->second;
1382 // Discard if removed or deactivating
1383 if(object->m_removed || object->m_pending_deactivation)
1385 if(object->unlimitedTransferDistance() == false){
1386 // Discard if too far
1387 f32 distance_f = object->getBasePosition().getDistanceFrom(pos_f);
1388 if(distance_f > radius_f)
1391 // Discard if already on current_objects
1392 std::set<u16>::iterator n;
1393 n = current_objects.find(id);
1394 if(n != current_objects.end())
1396 // Add to added_objects
1397 added_objects.insert(id);
1402 Finds out what objects have been removed from
1403 inside a radius around a position
1405 void ServerEnvironment::getRemovedActiveObjects(v3s16 pos, s16 radius,
1406 std::set<u16> ¤t_objects,
1407 std::set<u16> &removed_objects)
1409 v3f pos_f = intToFloat(pos, BS);
1410 f32 radius_f = radius * BS;
1412 Go through current_objects; object is removed if:
1413 - object is not found in m_active_objects (this is actually an
1414 error condition; objects should be set m_removed=true and removed
1415 only after all clients have been informed about removal), or
1416 - object has m_removed=true, or
1417 - object is too far away
1419 for(std::set<u16>::iterator
1420 i = current_objects.begin();
1421 i != current_objects.end(); ++i)
1424 ServerActiveObject *object = getActiveObject(id);
1427 infostream<<"ServerEnvironment::getRemovedActiveObjects():"
1428 <<" object in current_objects is NULL"<<std::endl;
1429 removed_objects.insert(id);
1433 if(object->m_removed || object->m_pending_deactivation)
1435 removed_objects.insert(id);
1439 // If transfer distance is unlimited, don't remove
1440 if(object->unlimitedTransferDistance())
1443 f32 distance_f = object->getBasePosition().getDistanceFrom(pos_f);
1445 if(distance_f >= radius_f)
1447 removed_objects.insert(id);
1455 ActiveObjectMessage ServerEnvironment::getActiveObjectMessage()
1457 if(m_active_object_messages.empty())
1458 return ActiveObjectMessage(0);
1460 ActiveObjectMessage message = m_active_object_messages.front();
1461 m_active_object_messages.pop_front();
1466 ************ Private methods *************
1469 u16 ServerEnvironment::addActiveObjectRaw(ServerActiveObject *object,
1470 bool set_changed, u32 dtime_s)
1473 if(object->getId() == 0){
1474 u16 new_id = getFreeServerActiveObjectId(m_active_objects);
1477 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1478 <<"no free ids available"<<std::endl;
1479 if(object->environmentDeletes())
1483 object->setId(new_id);
1486 verbosestream<<"ServerEnvironment::addActiveObjectRaw(): "
1487 <<"supplied with id "<<object->getId()<<std::endl;
1489 if(isFreeServerActiveObjectId(object->getId(), m_active_objects) == false)
1491 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1492 <<"id is not free ("<<object->getId()<<")"<<std::endl;
1493 if(object->environmentDeletes())
1497 /*infostream<<"ServerEnvironment::addActiveObjectRaw(): "
1498 <<"added (id="<<object->getId()<<")"<<std::endl;*/
1500 m_active_objects[object->getId()] = object;
1502 verbosestream<<"ServerEnvironment::addActiveObjectRaw(): "
1503 <<"Added id="<<object->getId()<<"; there are now "
1504 <<m_active_objects.size()<<" active objects."
1507 // Register reference in scripting api (must be done before post-init)
1508 m_script->addObjectReference(object);
1509 // Post-initialize object
1510 object->addedToEnvironment(dtime_s);
1512 // Add static data to block
1513 if(object->isStaticAllowed())
1515 // Add static object to active static list of the block
1516 v3f objectpos = object->getBasePosition();
1517 std::string staticdata = object->getStaticData();
1518 StaticObject s_obj(object->getType(), objectpos, staticdata);
1519 // Add to the block where the object is located in
1520 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1521 MapBlock *block = m_map->emergeBlock(blockpos);
1523 block->m_static_objects.m_active[object->getId()] = s_obj;
1524 object->m_static_exists = true;
1525 object->m_static_block = blockpos;
1528 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1529 "addActiveObjectRaw");
1531 v3s16 p = floatToInt(objectpos, BS);
1532 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1533 <<"could not emerge block for storing id="<<object->getId()
1534 <<" statically (pos="<<PP(p)<<")"<<std::endl;
1538 return object->getId();
1542 Remove objects that satisfy (m_removed && m_known_by_count==0)
1544 void ServerEnvironment::removeRemovedObjects()
1546 std::list<u16> objects_to_remove;
1547 for(std::map<u16, ServerActiveObject*>::iterator
1548 i = m_active_objects.begin();
1549 i != m_active_objects.end(); ++i)
1552 ServerActiveObject* obj = i->second;
1553 // This shouldn't happen but check it
1556 infostream<<"NULL object found in ServerEnvironment"
1557 <<" while finding removed objects. id="<<id<<std::endl;
1558 // Id to be removed from m_active_objects
1559 objects_to_remove.push_back(id);
1564 We will delete objects that are marked as removed or thatare
1565 waiting for deletion after deactivation
1567 if(obj->m_removed == false && obj->m_pending_deactivation == false)
1571 Delete static data from block if is marked as removed
1573 if(obj->m_static_exists && obj->m_removed)
1575 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
1577 block->m_static_objects.remove(id);
1578 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1579 "removeRemovedObjects/remove");
1580 obj->m_static_exists = false;
1582 infostream<<"Failed to emerge block from which an object to "
1583 <<"be removed was loaded from. id="<<id<<std::endl;
1587 // If m_known_by_count > 0, don't actually remove. On some future
1588 // invocation this will be 0, which is when removal will continue.
1589 if(obj->m_known_by_count > 0)
1593 Move static data from active to stored if not marked as removed
1595 if(obj->m_static_exists && !obj->m_removed){
1596 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
1598 std::map<u16, StaticObject>::iterator i =
1599 block->m_static_objects.m_active.find(id);
1600 if(i != block->m_static_objects.m_active.end()){
1601 block->m_static_objects.m_stored.push_back(i->second);
1602 block->m_static_objects.m_active.erase(id);
1603 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1604 "removeRemovedObjects/deactivate");
1607 infostream<<"Failed to emerge block from which an object to "
1608 <<"be deactivated was loaded from. id="<<id<<std::endl;
1612 // Tell the object about removal
1613 obj->removingFromEnvironment();
1614 // Deregister in scripting api
1615 m_script->removeObjectReference(obj);
1618 if(obj->environmentDeletes())
1620 // Id to be removed from m_active_objects
1621 objects_to_remove.push_back(id);
1623 // Remove references from m_active_objects
1624 for(std::list<u16>::iterator i = objects_to_remove.begin();
1625 i != objects_to_remove.end(); ++i)
1627 m_active_objects.erase(*i);
1631 static void print_hexdump(std::ostream &o, const std::string &data)
1633 const int linelength = 16;
1634 for(int l=0; ; l++){
1635 int i0 = linelength * l;
1636 bool at_end = false;
1637 int thislinelength = linelength;
1638 if(i0 + thislinelength > (int)data.size()){
1639 thislinelength = data.size() - i0;
1642 for(int di=0; di<linelength; di++){
1645 if(di<thislinelength)
1646 snprintf(buf, 4, "%.2x ", data[i]);
1648 snprintf(buf, 4, " ");
1652 for(int di=0; di<thislinelength; di++){
1666 Convert stored objects from blocks near the players to active.
1668 void ServerEnvironment::activateObjects(MapBlock *block, u32 dtime_s)
1672 // Ignore if no stored objects (to not set changed flag)
1673 if(block->m_static_objects.m_stored.size() == 0)
1675 verbosestream<<"ServerEnvironment::activateObjects(): "
1676 <<"activating objects of block "<<PP(block->getPos())
1677 <<" ("<<block->m_static_objects.m_stored.size()
1678 <<" objects)"<<std::endl;
1679 bool large_amount = (block->m_static_objects.m_stored.size() > g_settings->getU16("max_objects_per_block"));
1681 errorstream<<"suspiciously large amount of objects detected: "
1682 <<block->m_static_objects.m_stored.size()<<" in "
1683 <<PP(block->getPos())
1684 <<"; removing all of them."<<std::endl;
1685 // Clear stored list
1686 block->m_static_objects.m_stored.clear();
1687 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1688 "stored list cleared in activateObjects due to "
1689 "large amount of objects");
1693 // Activate stored objects
1694 std::list<StaticObject> new_stored;
1695 for(std::list<StaticObject>::iterator
1696 i = block->m_static_objects.m_stored.begin();
1697 i != block->m_static_objects.m_stored.end(); ++i)
1699 /*infostream<<"Server: Creating an active object from "
1700 <<"static data"<<std::endl;*/
1701 StaticObject &s_obj = *i;
1702 // Create an active object from the data
1703 ServerActiveObject *obj = ServerActiveObject::create
1704 (s_obj.type, this, 0, s_obj.pos, s_obj.data);
1705 // If couldn't create object, store static data back.
1708 errorstream<<"ServerEnvironment::activateObjects(): "
1709 <<"failed to create active object from static object "
1710 <<"in block "<<PP(s_obj.pos/BS)
1711 <<" type="<<(int)s_obj.type<<" data:"<<std::endl;
1712 print_hexdump(verbosestream, s_obj.data);
1714 new_stored.push_back(s_obj);
1717 verbosestream<<"ServerEnvironment::activateObjects(): "
1718 <<"activated static object pos="<<PP(s_obj.pos/BS)
1719 <<" type="<<(int)s_obj.type<<std::endl;
1720 // This will also add the object to the active static list
1721 addActiveObjectRaw(obj, false, dtime_s);
1723 // Clear stored list
1724 block->m_static_objects.m_stored.clear();
1725 // Add leftover failed stuff to stored list
1726 for(std::list<StaticObject>::iterator
1727 i = new_stored.begin();
1728 i != new_stored.end(); ++i)
1730 StaticObject &s_obj = *i;
1731 block->m_static_objects.m_stored.push_back(s_obj);
1734 // Turn the active counterparts of activated objects not pending for
1736 for(std::map<u16, StaticObject>::iterator
1737 i = block->m_static_objects.m_active.begin();
1738 i != block->m_static_objects.m_active.end(); ++i)
1741 ServerActiveObject *object = getActiveObject(id);
1743 object->m_pending_deactivation = false;
1747 Note: Block hasn't really been modified here.
1748 The objects have just been activated and moved from the stored
1749 static list to the active static list.
1750 As such, the block is essentially the same.
1751 Thus, do not call block->raiseModified(MOD_STATE_WRITE_NEEDED).
1752 Otherwise there would be a huge amount of unnecessary I/O.
1757 Convert objects that are not standing inside active blocks to static.
1759 If m_known_by_count != 0, active object is not deleted, but static
1760 data is still updated.
1762 If force_delete is set, active object is deleted nevertheless. It
1763 shall only be set so in the destructor of the environment.
1765 If block wasn't generated (not in memory or on disk),
1767 void ServerEnvironment::deactivateFarObjects(bool force_delete)
1769 std::list<u16> objects_to_remove;
1770 for(std::map<u16, ServerActiveObject*>::iterator
1771 i = m_active_objects.begin();
1772 i != m_active_objects.end(); ++i)
1774 ServerActiveObject* obj = i->second;
1777 // Do not deactivate if static data creation not allowed
1778 if(!force_delete && !obj->isStaticAllowed())
1781 // If pending deactivation, let removeRemovedObjects() do it
1782 if(!force_delete && obj->m_pending_deactivation)
1786 v3f objectpos = obj->getBasePosition();
1788 // The block in which the object resides in
1789 v3s16 blockpos_o = getNodeBlockPos(floatToInt(objectpos, BS));
1791 // If object's static data is stored in a deactivated block and object
1792 // is actually located in an active block, re-save to the block in
1793 // which the object is actually located in.
1795 obj->m_static_exists &&
1796 !m_active_blocks.contains(obj->m_static_block) &&
1797 m_active_blocks.contains(blockpos_o))
1799 v3s16 old_static_block = obj->m_static_block;
1801 // Save to block where object is located
1802 MapBlock *block = m_map->emergeBlock(blockpos_o, false);
1804 errorstream<<"ServerEnvironment::deactivateFarObjects(): "
1805 <<"Could not save object id="<<id
1806 <<" to it's current block "<<PP(blockpos_o)
1810 std::string staticdata_new = obj->getStaticData();
1811 StaticObject s_obj(obj->getType(), objectpos, staticdata_new);
1812 block->m_static_objects.insert(id, s_obj);
1813 obj->m_static_block = blockpos_o;
1814 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1815 "deactivateFarObjects: Static data moved in");
1817 // Delete from block where object was located
1818 block = m_map->emergeBlock(old_static_block, false);
1820 errorstream<<"ServerEnvironment::deactivateFarObjects(): "
1821 <<"Could not delete object id="<<id
1822 <<" from it's previous block "<<PP(old_static_block)
1826 block->m_static_objects.remove(id);
1827 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1828 "deactivateFarObjects: Static data moved out");
1832 // If block is active, don't remove
1833 if(!force_delete && m_active_blocks.contains(blockpos_o))
1836 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
1837 <<"deactivating object id="<<id<<" on inactive block "
1838 <<PP(blockpos_o)<<std::endl;
1840 // If known by some client, don't immediately delete.
1841 bool pending_delete = (obj->m_known_by_count > 0 && !force_delete);
1844 Update the static data
1847 if(obj->isStaticAllowed())
1849 // Create new static object
1850 std::string staticdata_new = obj->getStaticData();
1851 StaticObject s_obj(obj->getType(), objectpos, staticdata_new);
1853 bool stays_in_same_block = false;
1854 bool data_changed = true;
1856 if(obj->m_static_exists){
1857 if(obj->m_static_block == blockpos_o)
1858 stays_in_same_block = true;
1860 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
1862 std::map<u16, StaticObject>::iterator n =
1863 block->m_static_objects.m_active.find(id);
1864 if(n != block->m_static_objects.m_active.end()){
1865 StaticObject static_old = n->second;
1867 float save_movem = obj->getMinimumSavedMovement();
1869 if(static_old.data == staticdata_new &&
1870 (static_old.pos - objectpos).getLength() < save_movem)
1871 data_changed = false;
1873 errorstream<<"ServerEnvironment::deactivateFarObjects(): "
1874 <<"id="<<id<<" m_static_exists=true but "
1875 <<"static data doesn't actually exist in "
1876 <<PP(obj->m_static_block)<<std::endl;
1880 bool shall_be_written = (!stays_in_same_block || data_changed);
1882 // Delete old static object
1883 if(obj->m_static_exists)
1885 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
1888 block->m_static_objects.remove(id);
1889 obj->m_static_exists = false;
1890 // Only mark block as modified if data changed considerably
1891 if(shall_be_written)
1892 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1893 "deactivateFarObjects: Static data "
1894 "changed considerably");
1898 // Add to the block where the object is located in
1899 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1900 // Get or generate the block
1901 MapBlock *block = NULL;
1903 block = m_map->emergeBlock(blockpos);
1904 } catch(InvalidPositionException &e){
1905 // Handled via NULL pointer
1906 // NOTE: emergeBlock's failure is usually determined by it
1907 // actually returning NULL
1912 if(block->m_static_objects.m_stored.size() >= g_settings->getU16("max_objects_per_block")){
1913 errorstream<<"ServerEnv: Trying to store id="<<obj->getId()
1914 <<" statically but block "<<PP(blockpos)
1915 <<" already contains "
1916 <<block->m_static_objects.m_stored.size()
1918 <<" Forcing delete."<<std::endl;
1919 force_delete = true;
1921 // If static counterpart already exists in target block,
1923 // This shouldn't happen because the object is removed from
1924 // the previous block before this according to
1925 // obj->m_static_block, but happens rarely for some unknown
1926 // reason. Unsuccessful attempts have been made to find
1928 if(id && block->m_static_objects.m_active.find(id) != block->m_static_objects.m_active.end()){
1929 infostream<<"ServerEnv: WARNING: Performing hack #83274"
1931 block->m_static_objects.remove(id);
1933 // Store static data
1934 u16 store_id = pending_delete ? id : 0;
1935 block->m_static_objects.insert(store_id, s_obj);
1937 // Only mark block as modified if data changed considerably
1938 if(shall_be_written)
1939 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1940 "deactivateFarObjects: Static data "
1941 "changed considerably");
1943 obj->m_static_exists = true;
1944 obj->m_static_block = block->getPos();
1949 v3s16 p = floatToInt(objectpos, BS);
1950 errorstream<<"ServerEnv: Could not find or generate "
1951 <<"a block for storing id="<<obj->getId()
1952 <<" statically (pos="<<PP(p)<<")"<<std::endl;
1959 If known by some client, set pending deactivation.
1960 Otherwise delete it immediately.
1963 if(pending_delete && !force_delete)
1965 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
1966 <<"object id="<<id<<" is known by clients"
1967 <<"; not deleting yet"<<std::endl;
1969 obj->m_pending_deactivation = true;
1973 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
1974 <<"object id="<<id<<" is not known by clients"
1975 <<"; deleting"<<std::endl;
1977 // Tell the object about removal
1978 obj->removingFromEnvironment();
1979 // Deregister in scripting api
1980 m_script->removeObjectReference(obj);
1982 // Delete active object
1983 if(obj->environmentDeletes())
1985 // Id to be removed from m_active_objects
1986 objects_to_remove.push_back(id);
1989 // Remove references from m_active_objects
1990 for(std::list<u16>::iterator i = objects_to_remove.begin();
1991 i != objects_to_remove.end(); ++i)
1993 m_active_objects.erase(*i);
2000 #include "clientsimpleobject.h"
2006 ClientEnvironment::ClientEnvironment(ClientMap *map, scene::ISceneManager *smgr,
2007 ITextureSource *texturesource, IGameDef *gamedef,
2008 IrrlichtDevice *irr):
2011 m_texturesource(texturesource),
2016 memset(m_attachements, zero, sizeof(m_attachements));
2019 ClientEnvironment::~ClientEnvironment()
2021 // delete active objects
2022 for(std::map<u16, ClientActiveObject*>::iterator
2023 i = m_active_objects.begin();
2024 i != m_active_objects.end(); ++i)
2029 for(std::list<ClientSimpleObject*>::iterator
2030 i = m_simple_objects.begin(); i != m_simple_objects.end(); ++i)
2039 Map & ClientEnvironment::getMap()
2044 ClientMap & ClientEnvironment::getClientMap()
2049 void ClientEnvironment::addPlayer(Player *player)
2051 DSTACK(__FUNCTION_NAME);
2053 It is a failure if player is local and there already is a local
2056 assert(!(player->isLocal() == true && getLocalPlayer() != NULL));
2058 Environment::addPlayer(player);
2061 LocalPlayer * ClientEnvironment::getLocalPlayer()
2063 for(std::list<Player*>::iterator i = m_players.begin();
2064 i != m_players.end(); ++i)
2066 Player *player = *i;
2067 if(player->isLocal())
2068 return (LocalPlayer*)player;
2073 void ClientEnvironment::step(float dtime)
2075 DSTACK(__FUNCTION_NAME);
2077 /* Step time of day */
2078 stepTimeOfDay(dtime);
2080 // Get some settings
2081 bool fly_allowed = m_gamedef->checkLocalPrivilege("fly");
2082 bool free_move = fly_allowed && g_settings->getBool("free_move");
2085 LocalPlayer *lplayer = getLocalPlayer();
2087 // collision info queue
2088 std::list<CollisionInfo> player_collisions;
2091 Get the speed the player is going
2093 bool is_climbing = lplayer->is_climbing;
2095 f32 player_speed = lplayer->getSpeed().getLength();
2098 Maximum position increment
2100 //f32 position_max_increment = 0.05*BS;
2101 f32 position_max_increment = 0.1*BS;
2103 // Maximum time increment (for collision detection etc)
2104 // time = distance / speed
2105 f32 dtime_max_increment = 1;
2106 if(player_speed > 0.001)
2107 dtime_max_increment = position_max_increment / player_speed;
2109 // Maximum time increment is 10ms or lower
2110 if(dtime_max_increment > 0.01)
2111 dtime_max_increment = 0.01;
2113 // Don't allow overly huge dtime
2117 f32 dtime_downcount = dtime;
2120 Stuff that has a maximum time increment
2129 if(dtime_downcount > dtime_max_increment)
2131 dtime_part = dtime_max_increment;
2132 dtime_downcount -= dtime_part;
2136 dtime_part = dtime_downcount;
2138 Setting this to 0 (no -=dtime_part) disables an infinite loop
2139 when dtime_part is so small that dtime_downcount -= dtime_part
2142 dtime_downcount = 0;
2151 if(free_move == false && is_climbing == false)
2154 v3f speed = lplayer->getSpeed();
2155 if(lplayer->in_liquid == false)
2156 speed.Y -= lplayer->movement_gravity * lplayer->physics_override_gravity * dtime_part * 2;
2158 // Liquid floating / sinking
2159 if(lplayer->in_liquid && !lplayer->swimming_vertical)
2160 speed.Y -= lplayer->movement_liquid_sink * dtime_part * 2;
2162 // Liquid resistance
2163 if(lplayer->in_liquid_stable || lplayer->in_liquid)
2165 // How much the node's viscosity blocks movement, ranges between 0 and 1
2166 // Should match the scale at which viscosity increase affects other liquid attributes
2167 const f32 viscosity_factor = 0.3;
2169 v3f d_wanted = -speed / lplayer->movement_liquid_fluidity;
2170 f32 dl = d_wanted.getLength();
2171 if(dl > lplayer->movement_liquid_fluidity_smooth)
2172 dl = lplayer->movement_liquid_fluidity_smooth;
2173 dl *= (lplayer->liquid_viscosity * viscosity_factor) + (1 - viscosity_factor);
2175 v3f d = d_wanted.normalize() * dl;
2179 if(speed.X > lplayer->movement_liquid_fluidity + lplayer->movement_liquid_fluidity_smooth) speed.X -= lplayer->movement_liquid_fluidity_smooth;
2180 if(speed.X < -lplayer->movement_liquid_fluidity - lplayer->movement_liquid_fluidity_smooth) speed.X += 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.Y < -lplayer->movement_liquid_fluidity - lplayer->movement_liquid_fluidity_smooth) speed.Y += 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;
2184 if(speed.Z < -lplayer->movement_liquid_fluidity - lplayer->movement_liquid_fluidity_smooth) speed.Z += lplayer->movement_liquid_fluidity_smooth;
2188 lplayer->setSpeed(speed);
2193 This also does collision detection.
2195 lplayer->move(dtime_part, this, position_max_increment,
2196 &player_collisions);
2199 while(dtime_downcount > 0.001);
2201 //std::cout<<"Looped "<<loopcount<<" times."<<std::endl;
2203 for(std::list<CollisionInfo>::iterator
2204 i = player_collisions.begin();
2205 i != player_collisions.end(); ++i)
2207 CollisionInfo &info = *i;
2208 v3f speed_diff = info.new_speed - info.old_speed;;
2209 // Handle only fall damage
2210 // (because otherwise walking against something in fast_move kills you)
2211 if(speed_diff.Y < 0 || info.old_speed.Y >= 0)
2213 // Get rid of other components
2216 f32 pre_factor = 1; // 1 hp per node/s
2217 f32 tolerance = BS*14; // 5 without damage
2218 f32 post_factor = 1; // 1 hp per node/s
2219 if(info.type == COLLISION_NODE)
2221 const ContentFeatures &f = m_gamedef->ndef()->
2222 get(m_map->getNodeNoEx(info.node_p));
2223 // Determine fall damage multiplier
2224 int addp = itemgroup_get(f.groups, "fall_damage_add_percent");
2225 pre_factor = 1.0 + (float)addp/100.0;
2227 float speed = pre_factor * speed_diff.getLength();
2228 if(speed > tolerance)
2230 f32 damage_f = (speed - tolerance)/BS * post_factor;
2231 u16 damage = (u16)(damage_f+0.5);
2233 damageLocalPlayer(damage, true);
2234 MtEvent *e = new SimpleTriggerEvent("PlayerFallingDamage");
2235 m_gamedef->event()->put(e);
2241 A quick draft of lava damage
2243 if(m_lava_hurt_interval.step(dtime, 1.0))
2245 v3f pf = lplayer->getPosition();
2247 // Feet, middle and head
2248 v3s16 p1 = floatToInt(pf + v3f(0, BS*0.1, 0), BS);
2249 MapNode n1 = m_map->getNodeNoEx(p1);
2250 v3s16 p2 = floatToInt(pf + v3f(0, BS*0.8, 0), BS);
2251 MapNode n2 = m_map->getNodeNoEx(p2);
2252 v3s16 p3 = floatToInt(pf + v3f(0, BS*1.6, 0), BS);
2253 MapNode n3 = m_map->getNodeNoEx(p3);
2255 u32 damage_per_second = 0;
2256 damage_per_second = MYMAX(damage_per_second,
2257 m_gamedef->ndef()->get(n1).damage_per_second);
2258 damage_per_second = MYMAX(damage_per_second,
2259 m_gamedef->ndef()->get(n2).damage_per_second);
2260 damage_per_second = MYMAX(damage_per_second,
2261 m_gamedef->ndef()->get(n3).damage_per_second);
2263 if(damage_per_second != 0)
2265 damageLocalPlayer(damage_per_second, true);
2272 if(m_drowning_interval.step(dtime, 2.0))
2274 v3f pf = lplayer->getPosition();
2277 v3s16 p = floatToInt(pf + v3f(0, BS*1.6, 0), BS);
2278 MapNode n = m_map->getNodeNoEx(p);
2279 ContentFeatures c = m_gamedef->ndef()->get(n);
2280 u8 drowning_damage = c.drowning;
2281 if(drowning_damage > 0 && lplayer->hp > 0){
2282 u16 breath = lplayer->getBreath();
2289 lplayer->setBreath(breath);
2290 updateLocalPlayerBreath(breath);
2293 if(lplayer->getBreath() == 0 && drowning_damage > 0){
2294 damageLocalPlayer(drowning_damage, true);
2297 if(m_breathing_interval.step(dtime, 0.5))
2299 v3f pf = lplayer->getPosition();
2302 v3s16 p = floatToInt(pf + v3f(0, BS*1.6, 0), BS);
2303 MapNode n = m_map->getNodeNoEx(p);
2304 ContentFeatures c = m_gamedef->ndef()->get(n);
2306 lplayer->setBreath(11);
2308 else if(c.drowning == 0){
2309 u16 breath = lplayer->getBreath();
2312 lplayer->setBreath(breath);
2313 updateLocalPlayerBreath(breath);
2319 Stuff that can be done in an arbitarily large dtime
2321 for(std::list<Player*>::iterator i = m_players.begin();
2322 i != m_players.end(); ++i)
2324 Player *player = *i;
2327 Handle non-local players
2329 if(player->isLocal() == false)
2332 player->move(dtime, this, 100*BS);
2336 // Update lighting on all players on client
2340 v3s16 p = player->getLightPosition();
2341 MapNode n = m_map->getNode(p);
2342 light = n.getLightBlendF1((float)getDayNightRatio()/1000, m_gamedef->ndef());
2344 catch(InvalidPositionException &e){
2345 light = blend_light_f1((float)getDayNightRatio()/1000, LIGHT_SUN, 0);
2347 player->light = light;
2351 Step active objects and update lighting of them
2354 g_profiler->avg("CEnv: num of objects", m_active_objects.size());
2355 bool update_lighting = m_active_object_light_update_interval.step(dtime, 0.21);
2356 for(std::map<u16, ClientActiveObject*>::iterator
2357 i = m_active_objects.begin();
2358 i != m_active_objects.end(); ++i)
2360 ClientActiveObject* obj = i->second;
2362 obj->step(dtime, this);
2370 v3s16 p = obj->getLightPosition();
2371 MapNode n = m_map->getNode(p);
2372 light = n.getLightBlend(getDayNightRatio(), m_gamedef->ndef());
2374 catch(InvalidPositionException &e){
2375 light = blend_light(getDayNightRatio(), LIGHT_SUN, 0);
2377 obj->updateLight(light);
2382 Step and handle simple objects
2384 g_profiler->avg("CEnv: num of simple objects", m_simple_objects.size());
2385 for(std::list<ClientSimpleObject*>::iterator
2386 i = m_simple_objects.begin(); i != m_simple_objects.end();)
2388 ClientSimpleObject *simple = *i;
2389 std::list<ClientSimpleObject*>::iterator cur = i;
2391 simple->step(dtime);
2392 if(simple->m_to_be_removed){
2394 m_simple_objects.erase(cur);
2399 void ClientEnvironment::addSimpleObject(ClientSimpleObject *simple)
2401 m_simple_objects.push_back(simple);
2404 ClientActiveObject* ClientEnvironment::getActiveObject(u16 id)
2406 std::map<u16, ClientActiveObject*>::iterator n;
2407 n = m_active_objects.find(id);
2408 if(n == m_active_objects.end())
2413 bool isFreeClientActiveObjectId(u16 id,
2414 std::map<u16, ClientActiveObject*> &objects)
2419 return objects.find(id) == objects.end();
2422 u16 getFreeClientActiveObjectId(
2423 std::map<u16, ClientActiveObject*> &objects)
2425 //try to reuse id's as late as possible
2426 static u16 last_used_id = 0;
2427 u16 startid = last_used_id;
2431 if(isFreeClientActiveObjectId(last_used_id, objects))
2432 return last_used_id;
2434 if(last_used_id == startid)
2439 u16 ClientEnvironment::addActiveObject(ClientActiveObject *object)
2442 if(object->getId() == 0)
2444 u16 new_id = getFreeClientActiveObjectId(m_active_objects);
2447 infostream<<"ClientEnvironment::addActiveObject(): "
2448 <<"no free ids available"<<std::endl;
2452 object->setId(new_id);
2454 if(isFreeClientActiveObjectId(object->getId(), m_active_objects) == false)
2456 infostream<<"ClientEnvironment::addActiveObject(): "
2457 <<"id is not free ("<<object->getId()<<")"<<std::endl;
2461 infostream<<"ClientEnvironment::addActiveObject(): "
2462 <<"added (id="<<object->getId()<<")"<<std::endl;
2463 m_active_objects[object->getId()] = object;
2464 object->addToScene(m_smgr, m_texturesource, m_irr);
2465 { // Update lighting immediately
2469 v3s16 p = object->getLightPosition();
2470 MapNode n = m_map->getNode(p);
2471 light = n.getLightBlend(getDayNightRatio(), m_gamedef->ndef());
2473 catch(InvalidPositionException &e){
2474 light = blend_light(getDayNightRatio(), LIGHT_SUN, 0);
2476 object->updateLight(light);
2478 return object->getId();
2481 void ClientEnvironment::addActiveObject(u16 id, u8 type,
2482 const std::string &init_data)
2484 ClientActiveObject* obj =
2485 ClientActiveObject::create(type, m_gamedef, this);
2488 infostream<<"ClientEnvironment::addActiveObject(): "
2489 <<"id="<<id<<" type="<<type<<": Couldn't create object"
2498 obj->initialize(init_data);
2500 catch(SerializationError &e)
2502 errorstream<<"ClientEnvironment::addActiveObject():"
2503 <<" id="<<id<<" type="<<type
2504 <<": SerializationError in initialize(): "
2506 <<": init_data="<<serializeJsonString(init_data)
2510 addActiveObject(obj);
2513 void ClientEnvironment::removeActiveObject(u16 id)
2515 verbosestream<<"ClientEnvironment::removeActiveObject(): "
2516 <<"id="<<id<<std::endl;
2517 ClientActiveObject* obj = getActiveObject(id);
2520 infostream<<"ClientEnvironment::removeActiveObject(): "
2521 <<"id="<<id<<" not found"<<std::endl;
2524 obj->removeFromScene(true);
2526 m_active_objects.erase(id);
2529 void ClientEnvironment::processActiveObjectMessage(u16 id,
2530 const std::string &data)
2532 ClientActiveObject* obj = getActiveObject(id);
2535 infostream<<"ClientEnvironment::processActiveObjectMessage():"
2536 <<" got message for id="<<id<<", which doesn't exist."
2542 obj->processMessage(data);
2544 catch(SerializationError &e)
2546 errorstream<<"ClientEnvironment::processActiveObjectMessage():"
2547 <<" id="<<id<<" type="<<obj->getType()
2548 <<" SerializationError in processMessage(),"
2549 <<" message="<<serializeJsonString(data)
2555 Callbacks for activeobjects
2558 void ClientEnvironment::damageLocalPlayer(u8 damage, bool handle_hp)
2560 LocalPlayer *lplayer = getLocalPlayer();
2564 if(lplayer->hp > damage)
2565 lplayer->hp -= damage;
2570 ClientEnvEvent event;
2571 event.type = CEE_PLAYER_DAMAGE;
2572 event.player_damage.amount = damage;
2573 event.player_damage.send_to_server = handle_hp;
2574 m_client_event_queue.push_back(event);
2577 void ClientEnvironment::updateLocalPlayerBreath(u16 breath)
2579 ClientEnvEvent event;
2580 event.type = CEE_PLAYER_BREATH;
2581 event.player_breath.amount = breath;
2582 m_client_event_queue.push_back(event);
2586 Client likes to call these
2589 void ClientEnvironment::getActiveObjects(v3f origin, f32 max_d,
2590 std::vector<DistanceSortedActiveObject> &dest)
2592 for(std::map<u16, ClientActiveObject*>::iterator
2593 i = m_active_objects.begin();
2594 i != m_active_objects.end(); ++i)
2596 ClientActiveObject* obj = i->second;
2598 f32 d = (obj->getPosition() - origin).getLength();
2603 DistanceSortedActiveObject dso(obj, d);
2605 dest.push_back(dso);
2609 ClientEnvEvent ClientEnvironment::getClientEvent()
2611 ClientEnvEvent event;
2612 if(m_client_event_queue.empty())
2613 event.type = CEE_NONE;
2615 event = m_client_event_queue.front();
2616 m_client_event_queue.pop_front();
2621 #endif // #ifndef SERVER