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.
23 #include "environment.h"
26 #include "collision.h"
27 #include "content_mapnode.h"
29 #include "serverobject.h"
30 #include "content_sao.h"
35 #include "cpp_api/scriptapi.h"
37 #include "nodemetadata.h"
38 #include "main.h" // For g_settings, g_profiler
41 #include "clientmap.h"
42 #include "localplayer.h"
45 #include "daynightratio.h"
47 #include "util/serialize.h"
49 #define PP(x) "("<<(x).X<<","<<(x).Y<<","<<(x).Z<<")"
51 Environment::Environment():
53 m_time_of_day_f(9000./24000),
54 m_time_of_day_speed(0),
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(); ++i)
94 if(player->peer_id != peer_id)
99 // See if there is an another one
100 // (shouldn't be, but just to be sure)
105 Player * Environment::getPlayer(u16 peer_id)
107 for(std::list<Player*>::iterator i = m_players.begin();
108 i != m_players.end(); ++i)
111 if(player->peer_id == peer_id)
117 Player * Environment::getPlayer(const char *name)
119 for(std::list<Player*>::iterator i = m_players.begin();
120 i != m_players.end(); ++i)
123 if(strcmp(player->getName(), name) == 0)
129 Player * Environment::getRandomConnectedPlayer()
131 std::list<Player*> connected_players = getPlayers(true);
132 u32 chosen_one = myrand() % connected_players.size();
134 for(std::list<Player*>::iterator
135 i = connected_players.begin();
136 i != connected_players.end(); ++i)
148 Player * Environment::getNearestConnectedPlayer(v3f pos)
150 std::list<Player*> connected_players = getPlayers(true);
152 Player *nearest_player = NULL;
153 for(std::list<Player*>::iterator
154 i = connected_players.begin();
155 i != connected_players.end(); ++i)
158 f32 d = player->getPosition().getDistanceFrom(pos);
159 if(d < nearest_d || nearest_player == NULL)
162 nearest_player = player;
165 return nearest_player;
168 std::list<Player*> Environment::getPlayers()
173 std::list<Player*> Environment::getPlayers(bool ignore_disconnected)
175 std::list<Player*> newlist;
176 for(std::list<Player*>::iterator
177 i = m_players.begin();
178 i != m_players.end(); ++i)
182 if(ignore_disconnected)
184 // Ignore disconnected players
185 if(player->peer_id == 0)
189 newlist.push_back(player);
194 void Environment::printPlayers(std::ostream &o)
196 o<<"Players in environment:"<<std::endl;
197 for(std::list<Player*>::iterator i = m_players.begin();
198 i != m_players.end(); i++)
201 o<<"Player peer_id="<<player->peer_id<<std::endl;
205 u32 Environment::getDayNightRatio()
207 bool smooth = (g_settings->getS32("enable_shaders") != 0);
208 return time_to_daynight_ratio(m_time_of_day_f*24000, smooth);
211 void Environment::stepTimeOfDay(float dtime)
213 m_time_counter += dtime;
214 f32 speed = m_time_of_day_speed * 24000./(24.*3600);
215 u32 units = (u32)(m_time_counter*speed);
216 m_time_counter -= (f32)units / speed;
220 if(m_time_of_day + units >= 24000)
222 m_time_of_day = (m_time_of_day + units) % 24000;
224 m_time_of_day_f = (float)m_time_of_day / 24000.0;
227 m_time_of_day_f += m_time_of_day_speed/24/3600*dtime;
228 if(m_time_of_day_f > 1.0)
229 m_time_of_day_f -= 1.0;
230 if(m_time_of_day_f < 0.0)
231 m_time_of_day_f += 1.0;
239 ABMWithState::ABMWithState(ActiveBlockModifier *abm_):
243 // Initialize timer to random value to spread processing
244 float itv = abm->getTriggerInterval();
245 itv = MYMAX(0.001, itv); // No less than 1ms
246 int minval = MYMAX(-0.51*itv, -60); // Clamp to
247 int maxval = MYMIN(0.51*itv, 60); // +-60 seconds
248 timer = myrand_range(minval, maxval);
255 void fillRadiusBlock(v3s16 p0, s16 r, std::set<v3s16> &list)
258 for(p.X=p0.X-r; p.X<=p0.X+r; p.X++)
259 for(p.Y=p0.Y-r; p.Y<=p0.Y+r; p.Y++)
260 for(p.Z=p0.Z-r; p.Z<=p0.Z+r; p.Z++)
267 void ActiveBlockList::update(std::list<v3s16> &active_positions,
269 std::set<v3s16> &blocks_removed,
270 std::set<v3s16> &blocks_added)
275 std::set<v3s16> newlist;
276 for(std::list<v3s16>::iterator i = active_positions.begin();
277 i != active_positions.end(); ++i)
279 fillRadiusBlock(*i, radius, newlist);
283 Find out which blocks on the old list are not on the new list
285 // Go through old list
286 for(std::set<v3s16>::iterator i = m_list.begin();
287 i != m_list.end(); ++i)
290 // If not on new list, it's been removed
291 if(newlist.find(p) == newlist.end())
292 blocks_removed.insert(p);
296 Find out which blocks on the new list are not on the old list
298 // Go through new list
299 for(std::set<v3s16>::iterator i = newlist.begin();
300 i != newlist.end(); ++i)
303 // If not on old list, it's been added
304 if(m_list.find(p) == m_list.end())
305 blocks_added.insert(p);
312 for(std::set<v3s16>::iterator i = newlist.begin();
313 i != newlist.end(); ++i)
324 ServerEnvironment::ServerEnvironment(ServerMap *map, ScriptApi *scriptIface,
325 IGameDef *gamedef, IBackgroundBlockEmerger *emerger):
327 m_script(scriptIface),
330 m_random_spawn_timer(3),
331 m_send_recommended_timer(0),
332 m_active_block_interval_overload_skip(0),
334 m_game_time_fraction_counter(0),
335 m_recommended_send_interval(0.1)
339 ServerEnvironment::~ServerEnvironment()
341 // Clear active block list.
342 // This makes the next one delete all active objects.
343 m_active_blocks.clear();
345 // Convert all objects to static and delete the active objects
346 deactivateFarObjects(true);
351 // Delete ActiveBlockModifiers
352 for(std::list<ABMWithState>::iterator
353 i = m_abms.begin(); i != m_abms.end(); ++i){
358 Map & ServerEnvironment::getMap()
363 ServerMap & ServerEnvironment::getServerMap()
368 bool ServerEnvironment::line_of_sight(v3f pos1, v3f pos2, float stepsize)
370 float distance = pos1.getDistanceFrom(pos2);
372 //calculate normalized direction vector
373 v3f normalized_vector = v3f((pos2.X - pos1.X)/distance,
374 (pos2.Y - pos1.Y)/distance,
375 (pos2.Z - pos1.Z)/distance);
377 //find out if there's a node on path between pos1 and pos2
378 for (float i = 1; i < distance; i += stepsize) {
379 v3s16 pos = floatToInt(v3f(normalized_vector.X * i,
380 normalized_vector.Y * i,
381 normalized_vector.Z * i) +pos1,BS);
383 MapNode n = getMap().getNodeNoEx(pos);
385 if(n.param0 != CONTENT_AIR) {
392 void ServerEnvironment::serializePlayers(const std::string &savedir)
394 std::string players_path = savedir + "/players";
395 fs::CreateDir(players_path);
397 std::set<Player*> saved_players;
399 std::vector<fs::DirListNode> player_files = fs::GetDirListing(players_path);
400 for(u32 i=0; i<player_files.size(); i++)
402 if(player_files[i].dir)
405 // Full path to this file
406 std::string path = players_path + "/" + player_files[i].name;
408 //infostream<<"Checking player file "<<path<<std::endl;
410 // Load player to see what is its name
411 RemotePlayer testplayer(m_gamedef);
413 // Open file and deserialize
414 std::ifstream is(path.c_str(), std::ios_base::binary);
415 if(is.good() == false)
417 infostream<<"Failed to read "<<path<<std::endl;
420 testplayer.deSerialize(is);
423 //infostream<<"Loaded test player with name "<<testplayer.getName()<<std::endl;
425 // Search for the player
426 std::string playername = testplayer.getName();
427 Player *player = getPlayer(playername.c_str());
430 infostream<<"Didn't find matching player, ignoring file "<<path<<std::endl;
434 //infostream<<"Found matching player, overwriting."<<std::endl;
436 // OK, found. Save player there.
438 // Open file and serialize
439 std::ofstream os(path.c_str(), std::ios_base::binary);
440 if(os.good() == false)
442 infostream<<"Failed to overwrite "<<path<<std::endl;
445 player->serialize(os);
446 saved_players.insert(player);
450 for(std::list<Player*>::iterator i = m_players.begin();
451 i != m_players.end(); ++i)
454 if(saved_players.find(player) != saved_players.end())
456 /*infostream<<"Player "<<player->getName()
457 <<" was already saved."<<std::endl;*/
460 std::string playername = player->getName();
461 // Don't save unnamed player
464 //infostream<<"Not saving unnamed player."<<std::endl;
470 if(string_allowed(playername, PLAYERNAME_ALLOWED_CHARS) == false)
471 playername = "player";
472 std::string path = players_path + "/" + playername;
474 for(u32 i=0; i<1000; i++)
476 if(fs::PathExists(path) == false)
481 path = players_path + "/" + playername + itos(i);
485 infostream<<"Didn't find free file for player"<<std::endl;
490 /*infostream<<"Saving player "<<player->getName()<<" to "
492 // Open file and serialize
493 std::ofstream os(path.c_str(), std::ios_base::binary);
494 if(os.good() == false)
496 infostream<<"Failed to overwrite "<<path<<std::endl;
499 player->serialize(os);
500 saved_players.insert(player);
504 //infostream<<"Saved "<<saved_players.size()<<" players."<<std::endl;
507 void ServerEnvironment::deSerializePlayers(const std::string &savedir)
509 std::string players_path = savedir + "/players";
511 std::vector<fs::DirListNode> player_files = fs::GetDirListing(players_path);
512 for(u32 i=0; i<player_files.size(); i++)
514 if(player_files[i].dir)
517 // Full path to this file
518 std::string path = players_path + "/" + player_files[i].name;
520 //infostream<<"Checking player file "<<path<<std::endl;
522 // Load player to see what is its name
523 RemotePlayer testplayer(m_gamedef);
525 // Open file and deserialize
526 std::ifstream is(path.c_str(), std::ios_base::binary);
527 if(is.good() == false)
529 infostream<<"Failed to read "<<path<<std::endl;
532 testplayer.deSerialize(is);
535 if(!string_allowed(testplayer.getName(), PLAYERNAME_ALLOWED_CHARS))
537 infostream<<"Not loading player with invalid name: "
538 <<testplayer.getName()<<std::endl;
541 /*infostream<<"Loaded test player with name "<<testplayer.getName()
544 // Search for the player
545 std::string playername = testplayer.getName();
546 Player *player = getPlayer(playername.c_str());
547 bool newplayer = false;
550 //infostream<<"Is a new player"<<std::endl;
551 player = new RemotePlayer(m_gamedef);
557 verbosestream<<"Reading player "<<testplayer.getName()<<" from "
559 // Open file and deserialize
560 std::ifstream is(path.c_str(), std::ios_base::binary);
561 if(is.good() == false)
563 infostream<<"Failed to read "<<path<<std::endl;
566 player->deSerialize(is);
576 void ServerEnvironment::saveMeta(const std::string &savedir)
578 std::string path = savedir + "/env_meta.txt";
580 // Open file and serialize
581 std::ofstream os(path.c_str(), std::ios_base::binary);
582 if(os.good() == false)
584 infostream<<"ServerEnvironment::saveMeta(): Failed to open "
586 throw SerializationError("Couldn't save env meta");
590 args.setU64("game_time", m_game_time);
591 args.setU64("time_of_day", getTimeOfDay());
596 void ServerEnvironment::loadMeta(const std::string &savedir)
598 std::string path = savedir + "/env_meta.txt";
600 // Open file and deserialize
601 std::ifstream is(path.c_str(), std::ios_base::binary);
602 if(is.good() == false)
604 infostream<<"ServerEnvironment::loadMeta(): Failed to open "
606 throw SerializationError("Couldn't load env meta");
614 throw SerializationError
615 ("ServerEnvironment::loadMeta(): EnvArgsEnd not found");
617 std::getline(is, line);
618 std::string trimmedline = trim(line);
619 if(trimmedline == "EnvArgsEnd")
621 args.parseConfigLine(line);
625 m_game_time = args.getU64("game_time");
626 }catch(SettingNotFoundException &e){
627 // Getting this is crucial, otherwise timestamps are useless
628 throw SerializationError("Couldn't load env meta game_time");
632 m_time_of_day = args.getU64("time_of_day");
633 }catch(SettingNotFoundException &e){
634 // This is not as important
635 m_time_of_day = 9000;
641 ActiveBlockModifier *abm;
643 std::set<content_t> required_neighbors;
649 ServerEnvironment *m_env;
650 std::map<content_t, std::list<ActiveABM> > m_aabms;
652 ABMHandler(std::list<ABMWithState> &abms,
653 float dtime_s, ServerEnvironment *env,
659 INodeDefManager *ndef = env->getGameDef()->ndef();
660 for(std::list<ABMWithState>::iterator
661 i = abms.begin(); i != abms.end(); ++i){
662 ActiveBlockModifier *abm = i->abm;
663 float trigger_interval = abm->getTriggerInterval();
664 if(trigger_interval < 0.001)
665 trigger_interval = 0.001;
666 float actual_interval = dtime_s;
669 if(i->timer < trigger_interval)
671 i->timer -= trigger_interval;
672 actual_interval = trigger_interval;
674 float intervals = actual_interval / trigger_interval;
677 float chance = abm->getTriggerChance();
682 aabm.chance = chance / intervals;
686 std::set<std::string> required_neighbors_s
687 = abm->getRequiredNeighbors();
688 for(std::set<std::string>::iterator
689 i = required_neighbors_s.begin();
690 i != required_neighbors_s.end(); i++)
692 ndef->getIds(*i, aabm.required_neighbors);
695 std::set<std::string> contents_s = abm->getTriggerContents();
696 for(std::set<std::string>::iterator
697 i = contents_s.begin(); i != contents_s.end(); i++)
699 std::set<content_t> ids;
700 ndef->getIds(*i, ids);
701 for(std::set<content_t>::const_iterator k = ids.begin();
705 std::map<content_t, std::list<ActiveABM> >::iterator j;
707 if(j == m_aabms.end()){
708 std::list<ActiveABM> aabmlist;
709 m_aabms[c] = aabmlist;
712 j->second.push_back(aabm);
717 void apply(MapBlock *block)
722 ServerMap *map = &m_env->getServerMap();
725 for(p0.X=0; p0.X<MAP_BLOCKSIZE; p0.X++)
726 for(p0.Y=0; p0.Y<MAP_BLOCKSIZE; p0.Y++)
727 for(p0.Z=0; p0.Z<MAP_BLOCKSIZE; p0.Z++)
729 MapNode n = block->getNodeNoEx(p0);
730 content_t c = n.getContent();
731 v3s16 p = p0 + block->getPosRelative();
733 std::map<content_t, std::list<ActiveABM> >::iterator j;
735 if(j == m_aabms.end())
738 for(std::list<ActiveABM>::iterator
739 i = j->second.begin(); i != j->second.end(); i++)
741 if(myrand() % i->chance != 0)
745 if(!i->required_neighbors.empty())
748 for(p1.X = p.X-1; p1.X <= p.X+1; p1.X++)
749 for(p1.Y = p.Y-1; p1.Y <= p.Y+1; p1.Y++)
750 for(p1.Z = p.Z-1; p1.Z <= p.Z+1; p1.Z++)
754 MapNode n = map->getNodeNoEx(p1);
755 content_t c = n.getContent();
756 std::set<content_t>::const_iterator k;
757 k = i->required_neighbors.find(c);
758 if(k != i->required_neighbors.end()){
762 // No required neighbor found
767 // Find out how many objects the block contains
768 u32 active_object_count = block->m_static_objects.m_active.size();
769 // Find out how many objects this and all the neighbors contain
770 u32 active_object_count_wider = 0;
771 u32 wider_unknown_count = 0;
772 for(s16 x=-1; x<=1; x++)
773 for(s16 y=-1; y<=1; y++)
774 for(s16 z=-1; z<=1; z++)
776 MapBlock *block2 = map->getBlockNoCreateNoEx(
777 block->getPos() + v3s16(x,y,z));
779 wider_unknown_count = 0;
782 active_object_count_wider +=
783 block2->m_static_objects.m_active.size()
784 + block2->m_static_objects.m_stored.size();
787 u32 wider_known_count = 3*3*3 - wider_unknown_count;
788 active_object_count_wider += wider_unknown_count * active_object_count_wider / wider_known_count;
790 // Call all the trigger variations
791 i->abm->trigger(m_env, p, n);
792 i->abm->trigger(m_env, p, n,
793 active_object_count, active_object_count_wider);
799 void ServerEnvironment::activateBlock(MapBlock *block, u32 additional_dtime)
801 // Get time difference
803 u32 stamp = block->getTimestamp();
804 if(m_game_time > stamp && stamp != BLOCK_TIMESTAMP_UNDEFINED)
805 dtime_s = m_game_time - block->getTimestamp();
806 dtime_s += additional_dtime;
808 /*infostream<<"ServerEnvironment::activateBlock(): block timestamp: "
809 <<stamp<<", game time: "<<m_game_time<<std::endl;*/
811 // Set current time as timestamp
812 block->setTimestampNoChangedFlag(m_game_time);
814 /*infostream<<"ServerEnvironment::activateBlock(): block is "
815 <<dtime_s<<" seconds old."<<std::endl;*/
817 // Activate stored objects
818 activateObjects(block, dtime_s);
821 std::map<v3s16, NodeTimer> elapsed_timers =
822 block->m_node_timers.step((float)dtime_s);
823 if(!elapsed_timers.empty()){
825 for(std::map<v3s16, NodeTimer>::iterator
826 i = elapsed_timers.begin();
827 i != elapsed_timers.end(); i++){
828 n = block->getNodeNoEx(i->first);
829 v3s16 p = i->first + block->getPosRelative();
830 if(m_script->node_on_timer(p,n,i->second.elapsed))
831 block->setNodeTimer(i->first,NodeTimer(i->second.timeout,0));
835 /* Handle ActiveBlockModifiers */
836 ABMHandler abmhandler(m_abms, dtime_s, this, false);
837 abmhandler.apply(block);
840 void ServerEnvironment::addActiveBlockModifier(ActiveBlockModifier *abm)
842 m_abms.push_back(ABMWithState(abm));
845 bool ServerEnvironment::setNode(v3s16 p, const MapNode &n)
847 INodeDefManager *ndef = m_gamedef->ndef();
848 MapNode n_old = m_map->getNodeNoEx(p);
850 if(ndef->get(n_old).has_on_destruct)
851 m_script->node_on_destruct(p, n_old);
853 bool succeeded = m_map->addNodeWithEvent(p, n);
856 // Call post-destructor
857 if(ndef->get(n_old).has_after_destruct)
858 m_script->node_after_destruct(p, n_old);
860 if(ndef->get(n).has_on_construct)
861 m_script->node_on_construct(p, n);
865 bool ServerEnvironment::removeNode(v3s16 p)
867 INodeDefManager *ndef = m_gamedef->ndef();
868 MapNode n_old = m_map->getNodeNoEx(p);
870 if(ndef->get(n_old).has_on_destruct)
871 m_script->node_on_destruct(p, n_old);
873 // This is slightly optimized compared to addNodeWithEvent(air)
874 bool succeeded = m_map->removeNodeWithEvent(p);
877 // Call post-destructor
878 if(ndef->get(n_old).has_after_destruct)
879 m_script->node_after_destruct(p, n_old);
880 // Air doesn't require constructor
884 std::set<u16> ServerEnvironment::getObjectsInsideRadius(v3f pos, float radius)
886 std::set<u16> objects;
887 for(std::map<u16, ServerActiveObject*>::iterator
888 i = m_active_objects.begin();
889 i != m_active_objects.end(); ++i)
891 ServerActiveObject* obj = i->second;
893 v3f objectpos = obj->getBasePosition();
894 if(objectpos.getDistanceFrom(pos) > radius)
901 void ServerEnvironment::clearAllObjects()
903 infostream<<"ServerEnvironment::clearAllObjects(): "
904 <<"Removing all active objects"<<std::endl;
905 std::list<u16> objects_to_remove;
906 for(std::map<u16, ServerActiveObject*>::iterator
907 i = m_active_objects.begin();
908 i != m_active_objects.end(); ++i)
910 ServerActiveObject* obj = i->second;
911 if(obj->getType() == ACTIVEOBJECT_TYPE_PLAYER)
914 // Delete static object if block is loaded
915 if(obj->m_static_exists){
916 MapBlock *block = m_map->getBlockNoCreateNoEx(obj->m_static_block);
918 block->m_static_objects.remove(id);
919 block->raiseModified(MOD_STATE_WRITE_NEEDED,
921 obj->m_static_exists = false;
924 // If known by some client, don't delete immediately
925 if(obj->m_known_by_count > 0){
926 obj->m_pending_deactivation = true;
927 obj->m_removed = true;
931 // Tell the object about removal
932 obj->removingFromEnvironment();
933 // Deregister in scripting api
934 m_script->removeObjectReference(obj);
936 // Delete active object
937 if(obj->environmentDeletes())
939 // Id to be removed from m_active_objects
940 objects_to_remove.push_back(id);
942 // Remove references from m_active_objects
943 for(std::list<u16>::iterator i = objects_to_remove.begin();
944 i != objects_to_remove.end(); ++i)
946 m_active_objects.erase(*i);
949 // Get list of loaded blocks
950 std::list<v3s16> loaded_blocks;
951 infostream<<"ServerEnvironment::clearAllObjects(): "
952 <<"Listing all loaded blocks"<<std::endl;
953 m_map->listAllLoadedBlocks(loaded_blocks);
954 infostream<<"ServerEnvironment::clearAllObjects(): "
955 <<"Done listing all loaded blocks: "
956 <<loaded_blocks.size()<<std::endl;
958 // Get list of loadable blocks
959 std::list<v3s16> loadable_blocks;
960 infostream<<"ServerEnvironment::clearAllObjects(): "
961 <<"Listing all loadable blocks"<<std::endl;
962 m_map->listAllLoadableBlocks(loadable_blocks);
963 infostream<<"ServerEnvironment::clearAllObjects(): "
964 <<"Done listing all loadable blocks: "
965 <<loadable_blocks.size()
966 <<", now clearing"<<std::endl;
968 // Grab a reference on each loaded block to avoid unloading it
969 for(std::list<v3s16>::iterator i = loaded_blocks.begin();
970 i != loaded_blocks.end(); ++i)
973 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
978 // Remove objects in all loadable blocks
979 u32 unload_interval = g_settings->getS32("max_clearobjects_extra_loaded_blocks");
980 unload_interval = MYMAX(unload_interval, 1);
981 u32 report_interval = loadable_blocks.size() / 10;
982 u32 num_blocks_checked = 0;
983 u32 num_blocks_cleared = 0;
984 u32 num_objs_cleared = 0;
985 for(std::list<v3s16>::iterator i = loadable_blocks.begin();
986 i != loadable_blocks.end(); ++i)
989 MapBlock *block = m_map->emergeBlock(p, false);
991 errorstream<<"ServerEnvironment::clearAllObjects(): "
992 <<"Failed to emerge block "<<PP(p)<<std::endl;
995 u32 num_stored = block->m_static_objects.m_stored.size();
996 u32 num_active = block->m_static_objects.m_active.size();
997 if(num_stored != 0 || num_active != 0){
998 block->m_static_objects.m_stored.clear();
999 block->m_static_objects.m_active.clear();
1000 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1002 num_objs_cleared += num_stored + num_active;
1003 num_blocks_cleared++;
1005 num_blocks_checked++;
1007 if(num_blocks_checked % report_interval == 0){
1008 float percent = 100.0 * (float)num_blocks_checked /
1009 loadable_blocks.size();
1010 infostream<<"ServerEnvironment::clearAllObjects(): "
1011 <<"Cleared "<<num_objs_cleared<<" objects"
1012 <<" in "<<num_blocks_cleared<<" blocks ("
1013 <<percent<<"%)"<<std::endl;
1015 if(num_blocks_checked % unload_interval == 0){
1016 m_map->unloadUnreferencedBlocks();
1019 m_map->unloadUnreferencedBlocks();
1021 // Drop references that were added above
1022 for(std::list<v3s16>::iterator i = loaded_blocks.begin();
1023 i != loaded_blocks.end(); ++i)
1026 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1031 infostream<<"ServerEnvironment::clearAllObjects(): "
1032 <<"Finished: Cleared "<<num_objs_cleared<<" objects"
1033 <<" in "<<num_blocks_cleared<<" blocks"<<std::endl;
1036 void ServerEnvironment::step(float dtime)
1038 DSTACK(__FUNCTION_NAME);
1040 //TimeTaker timer("ServerEnv step");
1042 /* Step time of day */
1043 stepTimeOfDay(dtime);
1046 // NOTE: This is kind of funny on a singleplayer game, but doesn't
1047 // really matter that much.
1048 m_recommended_send_interval = g_settings->getFloat("dedicated_server_step");
1054 m_game_time_fraction_counter += dtime;
1055 u32 inc_i = (u32)m_game_time_fraction_counter;
1056 m_game_time += inc_i;
1057 m_game_time_fraction_counter -= (float)inc_i;
1064 ScopeProfiler sp(g_profiler, "SEnv: handle players avg", SPT_AVG);
1065 for(std::list<Player*>::iterator i = m_players.begin();
1066 i != m_players.end(); ++i)
1068 Player *player = *i;
1070 // Ignore disconnected players
1071 if(player->peer_id == 0)
1075 player->move(dtime, *m_map, 100*BS);
1080 Manage active block list
1082 if(m_active_blocks_management_interval.step(dtime, 2.0))
1084 ScopeProfiler sp(g_profiler, "SEnv: manage act. block list avg /2s", SPT_AVG);
1086 Get player block positions
1088 std::list<v3s16> players_blockpos;
1089 for(std::list<Player*>::iterator
1090 i = m_players.begin();
1091 i != m_players.end(); ++i)
1093 Player *player = *i;
1094 // Ignore disconnected players
1095 if(player->peer_id == 0)
1097 v3s16 blockpos = getNodeBlockPos(
1098 floatToInt(player->getPosition(), BS));
1099 players_blockpos.push_back(blockpos);
1103 Update list of active blocks, collecting changes
1105 const s16 active_block_range = g_settings->getS16("active_block_range");
1106 std::set<v3s16> blocks_removed;
1107 std::set<v3s16> blocks_added;
1108 m_active_blocks.update(players_blockpos, active_block_range,
1109 blocks_removed, blocks_added);
1112 Handle removed blocks
1115 // Convert active objects that are no more in active blocks to static
1116 deactivateFarObjects(false);
1118 for(std::set<v3s16>::iterator
1119 i = blocks_removed.begin();
1120 i != blocks_removed.end(); ++i)
1124 /* infostream<<"Server: Block " << PP(p)
1125 << " became inactive"<<std::endl; */
1127 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1131 // Set current time as timestamp (and let it set ChangedFlag)
1132 block->setTimestamp(m_game_time);
1139 for(std::set<v3s16>::iterator
1140 i = blocks_added.begin();
1141 i != blocks_added.end(); ++i)
1145 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1147 // Block needs to be fetched first
1148 m_emerger->queueBlockEmerge(p, false);
1149 m_active_blocks.m_list.erase(p);
1153 activateBlock(block);
1154 /* infostream<<"Server: Block " << PP(p)
1155 << " became active"<<std::endl; */
1160 Mess around in active blocks
1162 if(m_active_blocks_nodemetadata_interval.step(dtime, 1.0))
1164 ScopeProfiler sp(g_profiler, "SEnv: mess in act. blocks avg /1s", SPT_AVG);
1168 for(std::set<v3s16>::iterator
1169 i = m_active_blocks.m_list.begin();
1170 i != m_active_blocks.m_list.end(); ++i)
1174 /*infostream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
1175 <<") being handled"<<std::endl;*/
1177 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1181 // Reset block usage timer
1182 block->resetUsageTimer();
1184 // Set current time as timestamp
1185 block->setTimestampNoChangedFlag(m_game_time);
1186 // If time has changed much from the one on disk,
1187 // set block to be saved when it is unloaded
1188 if(block->getTimestamp() > block->getDiskTimestamp() + 60)
1189 block->raiseModified(MOD_STATE_WRITE_AT_UNLOAD,
1190 "Timestamp older than 60s (step)");
1193 std::map<v3s16, NodeTimer> elapsed_timers =
1194 block->m_node_timers.step((float)dtime);
1195 if(!elapsed_timers.empty()){
1197 for(std::map<v3s16, NodeTimer>::iterator
1198 i = elapsed_timers.begin();
1199 i != elapsed_timers.end(); i++){
1200 n = block->getNodeNoEx(i->first);
1201 p = i->first + block->getPosRelative();
1202 if(m_script->node_on_timer(p,n,i->second.elapsed))
1203 block->setNodeTimer(i->first,NodeTimer(i->second.timeout,0));
1209 const float abm_interval = 1.0;
1210 if(m_active_block_modifier_interval.step(dtime, abm_interval))
1212 if(m_active_block_interval_overload_skip > 0){
1213 ScopeProfiler sp(g_profiler, "SEnv: ABM overload skips");
1214 m_active_block_interval_overload_skip--;
1217 ScopeProfiler sp(g_profiler, "SEnv: modify in blocks avg /1s", SPT_AVG);
1218 TimeTaker timer("modify in active blocks");
1220 // Initialize handling of ActiveBlockModifiers
1221 ABMHandler abmhandler(m_abms, abm_interval, this, true);
1223 for(std::set<v3s16>::iterator
1224 i = m_active_blocks.m_list.begin();
1225 i != m_active_blocks.m_list.end(); ++i)
1229 /*infostream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
1230 <<") being handled"<<std::endl;*/
1232 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1236 // Set current time as timestamp
1237 block->setTimestampNoChangedFlag(m_game_time);
1239 /* Handle ActiveBlockModifiers */
1240 abmhandler.apply(block);
1243 u32 time_ms = timer.stop(true);
1244 u32 max_time_ms = 200;
1245 if(time_ms > max_time_ms){
1246 infostream<<"WARNING: active block modifiers took "
1247 <<time_ms<<"ms (longer than "
1248 <<max_time_ms<<"ms)"<<std::endl;
1249 m_active_block_interval_overload_skip = (time_ms / max_time_ms) + 1;
1254 Step script environment (run global on_step())
1256 m_script->environment_Step(dtime);
1262 ScopeProfiler sp(g_profiler, "SEnv: step act. objs avg", SPT_AVG);
1263 //TimeTaker timer("Step active objects");
1265 g_profiler->avg("SEnv: num of objects", m_active_objects.size());
1267 // This helps the objects to send data at the same time
1268 bool send_recommended = false;
1269 m_send_recommended_timer += dtime;
1270 if(m_send_recommended_timer > getSendRecommendedInterval())
1272 m_send_recommended_timer -= getSendRecommendedInterval();
1273 send_recommended = true;
1276 for(std::map<u16, ServerActiveObject*>::iterator
1277 i = m_active_objects.begin();
1278 i != m_active_objects.end(); ++i)
1280 ServerActiveObject* obj = i->second;
1281 // Remove non-peaceful mobs on peaceful mode
1282 if(g_settings->getBool("only_peaceful_mobs")){
1283 if(!obj->isPeaceful())
1284 obj->m_removed = true;
1286 // Don't step if is to be removed or stored statically
1287 if(obj->m_removed || obj->m_pending_deactivation)
1290 obj->step(dtime, send_recommended);
1291 // Read messages from object
1292 while(!obj->m_messages_out.empty())
1294 m_active_object_messages.push_back(
1295 obj->m_messages_out.pop_front());
1301 Manage active objects
1303 if(m_object_management_interval.step(dtime, 0.5))
1305 ScopeProfiler sp(g_profiler, "SEnv: remove removed objs avg /.5s", SPT_AVG);
1307 Remove objects that satisfy (m_removed && m_known_by_count==0)
1309 removeRemovedObjects();
1313 ServerActiveObject* ServerEnvironment::getActiveObject(u16 id)
1315 std::map<u16, ServerActiveObject*>::iterator n;
1316 n = m_active_objects.find(id);
1317 if(n == m_active_objects.end())
1322 bool isFreeServerActiveObjectId(u16 id,
1323 std::map<u16, ServerActiveObject*> &objects)
1328 return objects.find(id) == objects.end();
1331 u16 getFreeServerActiveObjectId(
1332 std::map<u16, ServerActiveObject*> &objects)
1334 //try to reuse id's as late as possible
1335 static u16 last_used_id = 0;
1336 u16 startid = last_used_id;
1340 if(isFreeServerActiveObjectId(last_used_id, objects))
1341 return last_used_id;
1343 if(last_used_id == startid)
1348 u16 ServerEnvironment::addActiveObject(ServerActiveObject *object)
1351 u16 id = addActiveObjectRaw(object, true, 0);
1356 bool ServerEnvironment::addActiveObjectAsStatic(ServerActiveObject *obj)
1360 v3f objectpos = obj->getBasePosition();
1362 // The block in which the object resides in
1363 v3s16 blockpos_o = getNodeBlockPos(floatToInt(objectpos, BS));
1366 Update the static data
1369 // Create new static object
1370 std::string staticdata = obj->getStaticData();
1371 StaticObject s_obj(obj->getType(), objectpos, staticdata);
1372 // Add to the block where the object is located in
1373 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1374 // Get or generate the block
1375 MapBlock *block = m_map->emergeBlock(blockpos);
1377 bool succeeded = false;
1381 block->m_static_objects.insert(0, s_obj);
1382 block->raiseModified(MOD_STATE_WRITE_AT_UNLOAD,
1383 "addActiveObjectAsStatic");
1387 infostream<<"ServerEnvironment::addActiveObjectAsStatic: "
1388 <<"Could not find or generate "
1389 <<"a block for storing static object"<<std::endl;
1393 if(obj->environmentDeletes())
1401 Finds out what new objects have been added to
1402 inside a radius around a position
1404 void ServerEnvironment::getAddedActiveObjects(v3s16 pos, s16 radius,
1405 std::set<u16> ¤t_objects,
1406 std::set<u16> &added_objects)
1408 v3f pos_f = intToFloat(pos, BS);
1409 f32 radius_f = radius * BS;
1411 Go through the object list,
1412 - discard m_removed objects,
1413 - discard objects that are too far away,
1414 - discard objects that are found in current_objects.
1415 - add remaining objects to added_objects
1417 for(std::map<u16, ServerActiveObject*>::iterator
1418 i = m_active_objects.begin();
1419 i != m_active_objects.end(); ++i)
1423 ServerActiveObject *object = i->second;
1426 // Discard if removed
1427 if(object->m_removed)
1429 if(object->unlimitedTransferDistance() == false){
1430 // Discard if too far
1431 f32 distance_f = object->getBasePosition().getDistanceFrom(pos_f);
1432 if(distance_f > radius_f)
1435 // Discard if already on current_objects
1436 std::set<u16>::iterator n;
1437 n = current_objects.find(id);
1438 if(n != current_objects.end())
1440 // Add to added_objects
1441 added_objects.insert(id);
1446 Finds out what objects have been removed from
1447 inside a radius around a position
1449 void ServerEnvironment::getRemovedActiveObjects(v3s16 pos, s16 radius,
1450 std::set<u16> ¤t_objects,
1451 std::set<u16> &removed_objects)
1453 v3f pos_f = intToFloat(pos, BS);
1454 f32 radius_f = radius * BS;
1456 Go through current_objects; object is removed if:
1457 - object is not found in m_active_objects (this is actually an
1458 error condition; objects should be set m_removed=true and removed
1459 only after all clients have been informed about removal), or
1460 - object has m_removed=true, or
1461 - object is too far away
1463 for(std::set<u16>::iterator
1464 i = current_objects.begin();
1465 i != current_objects.end(); ++i)
1468 ServerActiveObject *object = getActiveObject(id);
1471 infostream<<"ServerEnvironment::getRemovedActiveObjects():"
1472 <<" object in current_objects is NULL"<<std::endl;
1473 removed_objects.insert(id);
1477 if(object->m_removed)
1479 removed_objects.insert(id);
1483 // If transfer distance is unlimited, don't remove
1484 if(object->unlimitedTransferDistance())
1487 f32 distance_f = object->getBasePosition().getDistanceFrom(pos_f);
1489 if(distance_f >= radius_f)
1491 removed_objects.insert(id);
1499 ActiveObjectMessage ServerEnvironment::getActiveObjectMessage()
1501 if(m_active_object_messages.empty())
1502 return ActiveObjectMessage(0);
1504 return m_active_object_messages.pop_front();
1508 ************ Private methods *************
1511 u16 ServerEnvironment::addActiveObjectRaw(ServerActiveObject *object,
1512 bool set_changed, u32 dtime_s)
1515 if(object->getId() == 0){
1516 u16 new_id = getFreeServerActiveObjectId(m_active_objects);
1519 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1520 <<"no free ids available"<<std::endl;
1521 if(object->environmentDeletes())
1525 object->setId(new_id);
1528 verbosestream<<"ServerEnvironment::addActiveObjectRaw(): "
1529 <<"supplied with id "<<object->getId()<<std::endl;
1531 if(isFreeServerActiveObjectId(object->getId(), m_active_objects) == false)
1533 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1534 <<"id is not free ("<<object->getId()<<")"<<std::endl;
1535 if(object->environmentDeletes())
1539 /*infostream<<"ServerEnvironment::addActiveObjectRaw(): "
1540 <<"added (id="<<object->getId()<<")"<<std::endl;*/
1542 m_active_objects[object->getId()] = object;
1544 verbosestream<<"ServerEnvironment::addActiveObjectRaw(): "
1545 <<"Added id="<<object->getId()<<"; there are now "
1546 <<m_active_objects.size()<<" active objects."
1549 // Register reference in scripting api (must be done before post-init)
1550 m_script->addObjectReference(object);
1551 // Post-initialize object
1552 object->addedToEnvironment(dtime_s);
1554 // Add static data to block
1555 if(object->isStaticAllowed())
1557 // Add static object to active static list of the block
1558 v3f objectpos = object->getBasePosition();
1559 std::string staticdata = object->getStaticData();
1560 StaticObject s_obj(object->getType(), objectpos, staticdata);
1561 // Add to the block where the object is located in
1562 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1563 MapBlock *block = m_map->getBlockNoCreateNoEx(blockpos);
1566 block->m_static_objects.m_active[object->getId()] = s_obj;
1567 object->m_static_exists = true;
1568 object->m_static_block = blockpos;
1571 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1572 "addActiveObjectRaw");
1575 v3s16 p = floatToInt(objectpos, BS);
1576 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1577 <<"could not find block for storing id="<<object->getId()
1578 <<" statically (pos="<<PP(p)<<")"<<std::endl;
1582 return object->getId();
1586 Remove objects that satisfy (m_removed && m_known_by_count==0)
1588 void ServerEnvironment::removeRemovedObjects()
1590 std::list<u16> objects_to_remove;
1591 for(std::map<u16, ServerActiveObject*>::iterator
1592 i = m_active_objects.begin();
1593 i != m_active_objects.end(); ++i)
1596 ServerActiveObject* obj = i->second;
1597 // This shouldn't happen but check it
1600 infostream<<"NULL object found in ServerEnvironment"
1601 <<" while finding removed objects. id="<<id<<std::endl;
1602 // Id to be removed from m_active_objects
1603 objects_to_remove.push_back(id);
1608 We will delete objects that are marked as removed or thatare
1609 waiting for deletion after deactivation
1611 if(obj->m_removed == false && obj->m_pending_deactivation == false)
1615 Delete static data from block if is marked as removed
1617 if(obj->m_static_exists && obj->m_removed)
1619 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
1621 block->m_static_objects.remove(id);
1622 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1623 "removeRemovedObjects");
1624 obj->m_static_exists = false;
1626 infostream << "failed to emerge block from which "
1627 "an object to be removed was loaded from. id="<<id<<std::endl;
1631 // If m_known_by_count > 0, don't actually remove.
1632 if(obj->m_known_by_count > 0)
1635 // Tell the object about removal
1636 obj->removingFromEnvironment();
1637 // Deregister in scripting api
1638 m_script->removeObjectReference(obj);
1641 if(obj->environmentDeletes())
1643 // Id to be removed from m_active_objects
1644 objects_to_remove.push_back(id);
1646 // Remove references from m_active_objects
1647 for(std::list<u16>::iterator i = objects_to_remove.begin();
1648 i != objects_to_remove.end(); ++i)
1650 m_active_objects.erase(*i);
1654 static void print_hexdump(std::ostream &o, const std::string &data)
1656 const int linelength = 16;
1657 for(int l=0; ; l++){
1658 int i0 = linelength * l;
1659 bool at_end = false;
1660 int thislinelength = linelength;
1661 if(i0 + thislinelength > (int)data.size()){
1662 thislinelength = data.size() - i0;
1665 for(int di=0; di<linelength; di++){
1668 if(di<thislinelength)
1669 snprintf(buf, 4, "%.2x ", data[i]);
1671 snprintf(buf, 4, " ");
1675 for(int di=0; di<thislinelength; di++){
1689 Convert stored objects from blocks near the players to active.
1691 void ServerEnvironment::activateObjects(MapBlock *block, u32 dtime_s)
1695 // Ignore if no stored objects (to not set changed flag)
1696 if(block->m_static_objects.m_stored.size() == 0)
1698 verbosestream<<"ServerEnvironment::activateObjects(): "
1699 <<"activating objects of block "<<PP(block->getPos())
1700 <<" ("<<block->m_static_objects.m_stored.size()
1701 <<" objects)"<<std::endl;
1702 bool large_amount = (block->m_static_objects.m_stored.size() > 49);
1704 errorstream<<"suspiciously large amount of objects detected: "
1705 <<block->m_static_objects.m_stored.size()<<" in "
1706 <<PP(block->getPos())
1707 <<"; removing all of them."<<std::endl;
1708 // Clear stored list
1709 block->m_static_objects.m_stored.clear();
1710 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1711 "stored list cleared in activateObjects due to "
1712 "large amount of objects");
1715 // A list for objects that couldn't be converted to active for some
1716 // reason. They will be stored back.
1717 std::list<StaticObject> new_stored;
1718 // Loop through stored static objects
1719 for(std::list<StaticObject>::iterator
1720 i = block->m_static_objects.m_stored.begin();
1721 i != block->m_static_objects.m_stored.end(); ++i)
1723 /*infostream<<"Server: Creating an active object from "
1724 <<"static data"<<std::endl;*/
1725 StaticObject &s_obj = *i;
1726 // Create an active object from the data
1727 ServerActiveObject *obj = ServerActiveObject::create
1728 (s_obj.type, this, 0, s_obj.pos, s_obj.data);
1729 // If couldn't create object, store static data back.
1732 errorstream<<"ServerEnvironment::activateObjects(): "
1733 <<"failed to create active object from static object "
1734 <<"in block "<<PP(s_obj.pos/BS)
1735 <<" type="<<(int)s_obj.type<<" data:"<<std::endl;
1736 print_hexdump(verbosestream, s_obj.data);
1738 new_stored.push_back(s_obj);
1741 verbosestream<<"ServerEnvironment::activateObjects(): "
1742 <<"activated static object pos="<<PP(s_obj.pos/BS)
1743 <<" type="<<(int)s_obj.type<<std::endl;
1744 // This will also add the object to the active static list
1745 addActiveObjectRaw(obj, false, dtime_s);
1747 // Clear stored list
1748 block->m_static_objects.m_stored.clear();
1749 // Add leftover failed stuff to stored list
1750 for(std::list<StaticObject>::iterator
1751 i = new_stored.begin();
1752 i != new_stored.end(); ++i)
1754 StaticObject &s_obj = *i;
1755 block->m_static_objects.m_stored.push_back(s_obj);
1758 Note: Block hasn't really been modified here.
1759 The objects have just been activated and moved from the stored
1760 static list to the active static list.
1761 As such, the block is essentially the same.
1762 Thus, do not call block->raiseModified(MOD_STATE_WRITE_NEEDED).
1763 Otherwise there would be a huge amount of unnecessary I/O.
1768 Convert objects that are not standing inside active blocks to static.
1770 If m_known_by_count != 0, active object is not deleted, but static
1771 data is still updated.
1773 If force_delete is set, active object is deleted nevertheless. It
1774 shall only be set so in the destructor of the environment.
1776 If block wasn't generated (not in memory or on disk),
1778 void ServerEnvironment::deactivateFarObjects(bool force_delete)
1780 std::list<u16> objects_to_remove;
1781 for(std::map<u16, ServerActiveObject*>::iterator
1782 i = m_active_objects.begin();
1783 i != m_active_objects.end(); ++i)
1785 ServerActiveObject* obj = i->second;
1788 // Do not deactivate if static data creation not allowed
1789 if(!force_delete && !obj->isStaticAllowed())
1792 // If pending deactivation, let removeRemovedObjects() do it
1793 if(!force_delete && obj->m_pending_deactivation)
1797 v3f objectpos = obj->getBasePosition();
1799 // The block in which the object resides in
1800 v3s16 blockpos_o = getNodeBlockPos(floatToInt(objectpos, BS));
1802 // If block is active, don't remove
1803 if(!force_delete && m_active_blocks.contains(blockpos_o))
1806 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
1807 <<"deactivating object id="<<id<<" on inactive block "
1808 <<PP(blockpos_o)<<std::endl;
1810 // If known by some client, don't immediately delete.
1811 bool pending_delete = (obj->m_known_by_count > 0 && !force_delete);
1814 Update the static data
1817 if(obj->isStaticAllowed())
1819 // Create new static object
1820 std::string staticdata_new = obj->getStaticData();
1821 StaticObject s_obj(obj->getType(), objectpos, staticdata_new);
1823 bool stays_in_same_block = false;
1824 bool data_changed = true;
1826 if(obj->m_static_exists){
1827 if(obj->m_static_block == blockpos_o)
1828 stays_in_same_block = true;
1830 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
1832 std::map<u16, StaticObject>::iterator n =
1833 block->m_static_objects.m_active.find(id);
1834 if(n != block->m_static_objects.m_active.end()){
1835 StaticObject static_old = n->second;
1837 float save_movem = obj->getMinimumSavedMovement();
1839 if(static_old.data == staticdata_new &&
1840 (static_old.pos - objectpos).getLength() < save_movem)
1841 data_changed = false;
1843 errorstream<<"ServerEnvironment::deactivateFarObjects(): "
1844 <<"id="<<id<<" m_static_exists=true but "
1845 <<"static data doesn't actually exist in "
1846 <<PP(obj->m_static_block)<<std::endl;
1850 bool shall_be_written = (!stays_in_same_block || data_changed);
1852 // Delete old static object
1853 if(obj->m_static_exists)
1855 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
1858 block->m_static_objects.remove(id);
1859 obj->m_static_exists = false;
1860 // Only mark block as modified if data changed considerably
1861 if(shall_be_written)
1862 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1863 "deactivateFarObjects: Static data "
1864 "changed considerably");
1868 // Add to the block where the object is located in
1869 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1870 // Get or generate the block
1871 MapBlock *block = NULL;
1873 block = m_map->emergeBlock(blockpos);
1874 } catch(InvalidPositionException &e){
1875 // Handled via NULL pointer
1880 if(block->m_static_objects.m_stored.size() >= 49){
1881 errorstream<<"ServerEnv: Trying to store id="<<obj->getId()
1882 <<" statically but block "<<PP(blockpos)
1883 <<" already contains "
1884 <<block->m_static_objects.m_stored.size()
1885 <<" (over 49) objects."
1886 <<" Forcing delete."<<std::endl;
1887 force_delete = true;
1889 // If static counterpart already exists, remove it first.
1890 // This shouldn't happen, but happens rarely for some
1891 // unknown reason. Unsuccessful attempts have been made to
1892 // find said reason.
1893 if(id && block->m_static_objects.m_active.find(id) != block->m_static_objects.m_active.end()){
1894 infostream<<"ServerEnv: WARNING: Performing hack #83274"
1896 block->m_static_objects.remove(id);
1899 block->m_static_objects.insert(0, s_obj);
1901 // Only mark block as modified if data changed considerably
1902 if(shall_be_written)
1903 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1904 "deactivateFarObjects: Static data "
1905 "changed considerably");
1907 obj->m_static_exists = true;
1908 obj->m_static_block = block->getPos();
1913 v3s16 p = floatToInt(objectpos, BS);
1914 errorstream<<"ServerEnv: Could not find or generate "
1915 <<"a block for storing id="<<obj->getId()
1916 <<" statically (pos="<<PP(p)<<")"<<std::endl;
1923 If known by some client, set pending deactivation.
1924 Otherwise delete it immediately.
1927 if(pending_delete && !force_delete)
1929 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
1930 <<"object id="<<id<<" is known by clients"
1931 <<"; not deleting yet"<<std::endl;
1933 obj->m_pending_deactivation = true;
1937 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
1938 <<"object id="<<id<<" is not known by clients"
1939 <<"; deleting"<<std::endl;
1941 // Tell the object about removal
1942 obj->removingFromEnvironment();
1943 // Deregister in scripting api
1944 m_script->removeObjectReference(obj);
1946 // Delete active object
1947 if(obj->environmentDeletes())
1949 // Id to be removed from m_active_objects
1950 objects_to_remove.push_back(id);
1953 // Remove references from m_active_objects
1954 for(std::list<u16>::iterator i = objects_to_remove.begin();
1955 i != objects_to_remove.end(); ++i)
1957 m_active_objects.erase(*i);
1964 #include "clientsimpleobject.h"
1970 ClientEnvironment::ClientEnvironment(ClientMap *map, scene::ISceneManager *smgr,
1971 ITextureSource *texturesource, IGameDef *gamedef,
1972 IrrlichtDevice *irr):
1975 m_texturesource(texturesource),
1981 ClientEnvironment::~ClientEnvironment()
1983 // delete active objects
1984 for(std::map<u16, ClientActiveObject*>::iterator
1985 i = m_active_objects.begin();
1986 i != m_active_objects.end(); ++i)
1991 for(std::list<ClientSimpleObject*>::iterator
1992 i = m_simple_objects.begin(); i != m_simple_objects.end(); ++i)
2001 Map & ClientEnvironment::getMap()
2006 ClientMap & ClientEnvironment::getClientMap()
2011 void ClientEnvironment::addPlayer(Player *player)
2013 DSTACK(__FUNCTION_NAME);
2015 It is a failure if player is local and there already is a local
2018 assert(!(player->isLocal() == true && getLocalPlayer() != NULL));
2020 Environment::addPlayer(player);
2023 LocalPlayer * ClientEnvironment::getLocalPlayer()
2025 for(std::list<Player*>::iterator i = m_players.begin();
2026 i != m_players.end(); ++i)
2028 Player *player = *i;
2029 if(player->isLocal())
2030 return (LocalPlayer*)player;
2035 void ClientEnvironment::step(float dtime)
2037 DSTACK(__FUNCTION_NAME);
2039 /* Step time of day */
2040 stepTimeOfDay(dtime);
2042 // Get some settings
2043 bool fly_allowed = m_gamedef->checkLocalPrivilege("fly");
2044 bool free_move = fly_allowed && g_settings->getBool("free_move");
2047 LocalPlayer *lplayer = getLocalPlayer();
2049 // collision info queue
2050 std::list<CollisionInfo> player_collisions;
2053 Get the speed the player is going
2055 bool is_climbing = lplayer->is_climbing;
2057 f32 player_speed = lplayer->getSpeed().getLength();
2060 Maximum position increment
2062 //f32 position_max_increment = 0.05*BS;
2063 f32 position_max_increment = 0.1*BS;
2065 // Maximum time increment (for collision detection etc)
2066 // time = distance / speed
2067 f32 dtime_max_increment = 1;
2068 if(player_speed > 0.001)
2069 dtime_max_increment = position_max_increment / player_speed;
2071 // Maximum time increment is 10ms or lower
2072 if(dtime_max_increment > 0.01)
2073 dtime_max_increment = 0.01;
2075 // Don't allow overly huge dtime
2079 f32 dtime_downcount = dtime;
2082 Stuff that has a maximum time increment
2091 if(dtime_downcount > dtime_max_increment)
2093 dtime_part = dtime_max_increment;
2094 dtime_downcount -= dtime_part;
2098 dtime_part = dtime_downcount;
2100 Setting this to 0 (no -=dtime_part) disables an infinite loop
2101 when dtime_part is so small that dtime_downcount -= dtime_part
2104 dtime_downcount = 0;
2113 if(free_move == false && is_climbing == false)
2116 v3f speed = lplayer->getSpeed();
2117 if(lplayer->in_liquid == false)
2118 speed.Y -= lplayer->movement_gravity * lplayer->physics_override_gravity * dtime_part * 2;
2120 // Liquid floating / sinking
2121 if(lplayer->in_liquid && !lplayer->swimming_vertical)
2122 speed.Y -= lplayer->movement_liquid_sink * dtime_part * 2;
2124 // Liquid resistance
2125 if(lplayer->in_liquid_stable || lplayer->in_liquid)
2127 // How much the node's viscosity blocks movement, ranges between 0 and 1
2128 // Should match the scale at which viscosity increase affects other liquid attributes
2129 const f32 viscosity_factor = 0.3;
2131 v3f d_wanted = -speed / lplayer->movement_liquid_fluidity;
2132 f32 dl = d_wanted.getLength();
2133 if(dl > lplayer->movement_liquid_fluidity_smooth)
2134 dl = lplayer->movement_liquid_fluidity_smooth;
2135 dl *= (lplayer->liquid_viscosity * viscosity_factor) + (1 - viscosity_factor);
2137 v3f d = d_wanted.normalize() * dl;
2141 if(speed.X > lplayer->movement_liquid_fluidity + lplayer->movement_liquid_fluidity_smooth) speed.X -= lplayer->movement_liquid_fluidity_smooth;
2142 if(speed.X < -lplayer->movement_liquid_fluidity - lplayer->movement_liquid_fluidity_smooth) speed.X += lplayer->movement_liquid_fluidity_smooth;
2143 if(speed.Y > lplayer->movement_liquid_fluidity + lplayer->movement_liquid_fluidity_smooth) speed.Y -= lplayer->movement_liquid_fluidity_smooth;
2144 if(speed.Y < -lplayer->movement_liquid_fluidity - lplayer->movement_liquid_fluidity_smooth) speed.Y += lplayer->movement_liquid_fluidity_smooth;
2145 if(speed.Z > lplayer->movement_liquid_fluidity + lplayer->movement_liquid_fluidity_smooth) speed.Z -= lplayer->movement_liquid_fluidity_smooth;
2146 if(speed.Z < -lplayer->movement_liquid_fluidity - lplayer->movement_liquid_fluidity_smooth) speed.Z += lplayer->movement_liquid_fluidity_smooth;
2150 lplayer->setSpeed(speed);
2155 This also does collision detection.
2157 lplayer->move(dtime_part, this, position_max_increment,
2158 &player_collisions);
2161 while(dtime_downcount > 0.001);
2163 //std::cout<<"Looped "<<loopcount<<" times."<<std::endl;
2165 for(std::list<CollisionInfo>::iterator
2166 i = player_collisions.begin();
2167 i != player_collisions.end(); ++i)
2169 CollisionInfo &info = *i;
2170 v3f speed_diff = info.new_speed - info.old_speed;;
2171 // Handle only fall damage
2172 // (because otherwise walking against something in fast_move kills you)
2173 if(speed_diff.Y < 0 || info.old_speed.Y >= 0)
2175 // Get rid of other components
2178 f32 pre_factor = 1; // 1 hp per node/s
2179 f32 tolerance = BS*14; // 5 without damage
2180 f32 post_factor = 1; // 1 hp per node/s
2181 if(info.type == COLLISION_NODE)
2183 const ContentFeatures &f = m_gamedef->ndef()->
2184 get(m_map->getNodeNoEx(info.node_p));
2185 // Determine fall damage multiplier
2186 int addp = itemgroup_get(f.groups, "fall_damage_add_percent");
2187 pre_factor = 1.0 + (float)addp/100.0;
2189 float speed = pre_factor * speed_diff.getLength();
2190 if(speed > tolerance)
2192 f32 damage_f = (speed - tolerance)/BS * post_factor;
2193 u16 damage = (u16)(damage_f+0.5);
2195 damageLocalPlayer(damage, true);
2196 MtEvent *e = new SimpleTriggerEvent("PlayerFallingDamage");
2197 m_gamedef->event()->put(e);
2203 A quick draft of lava damage
2205 if(m_lava_hurt_interval.step(dtime, 1.0))
2207 v3f pf = lplayer->getPosition();
2209 // Feet, middle and head
2210 v3s16 p1 = floatToInt(pf + v3f(0, BS*0.1, 0), BS);
2211 MapNode n1 = m_map->getNodeNoEx(p1);
2212 v3s16 p2 = floatToInt(pf + v3f(0, BS*0.8, 0), BS);
2213 MapNode n2 = m_map->getNodeNoEx(p2);
2214 v3s16 p3 = floatToInt(pf + v3f(0, BS*1.6, 0), BS);
2215 MapNode n3 = m_map->getNodeNoEx(p3);
2217 u32 damage_per_second = 0;
2218 damage_per_second = MYMAX(damage_per_second,
2219 m_gamedef->ndef()->get(n1).damage_per_second);
2220 damage_per_second = MYMAX(damage_per_second,
2221 m_gamedef->ndef()->get(n2).damage_per_second);
2222 damage_per_second = MYMAX(damage_per_second,
2223 m_gamedef->ndef()->get(n3).damage_per_second);
2225 if(damage_per_second != 0)
2227 damageLocalPlayer(damage_per_second, true);
2232 Stuff that can be done in an arbitarily large dtime
2234 for(std::list<Player*>::iterator i = m_players.begin();
2235 i != m_players.end(); ++i)
2237 Player *player = *i;
2240 Handle non-local players
2242 if(player->isLocal() == false)
2245 player->move(dtime, *m_map, 100*BS);
2249 // Update lighting on all players on client
2253 v3s16 p = player->getLightPosition();
2254 MapNode n = m_map->getNode(p);
2255 light = n.getLightBlendF1((float)getDayNightRatio()/1000, m_gamedef->ndef());
2257 catch(InvalidPositionException &e){
2258 light = blend_light_f1((float)getDayNightRatio()/1000, LIGHT_SUN, 0);
2260 player->light = light;
2264 Step active objects and update lighting of them
2267 g_profiler->avg("CEnv: num of objects", m_active_objects.size());
2268 bool update_lighting = m_active_object_light_update_interval.step(dtime, 0.21);
2269 for(std::map<u16, ClientActiveObject*>::iterator
2270 i = m_active_objects.begin();
2271 i != m_active_objects.end(); ++i)
2273 ClientActiveObject* obj = i->second;
2275 obj->step(dtime, this);
2283 v3s16 p = obj->getLightPosition();
2284 MapNode n = m_map->getNode(p);
2285 light = n.getLightBlend(getDayNightRatio(), m_gamedef->ndef());
2287 catch(InvalidPositionException &e){
2288 light = blend_light(getDayNightRatio(), LIGHT_SUN, 0);
2290 obj->updateLight(light);
2295 Step and handle simple objects
2297 g_profiler->avg("CEnv: num of simple objects", m_simple_objects.size());
2298 for(std::list<ClientSimpleObject*>::iterator
2299 i = m_simple_objects.begin(); i != m_simple_objects.end();)
2301 ClientSimpleObject *simple = *i;
2302 std::list<ClientSimpleObject*>::iterator cur = i;
2304 simple->step(dtime);
2305 if(simple->m_to_be_removed){
2307 m_simple_objects.erase(cur);
2312 void ClientEnvironment::addSimpleObject(ClientSimpleObject *simple)
2314 m_simple_objects.push_back(simple);
2317 ClientActiveObject* ClientEnvironment::getActiveObject(u16 id)
2319 std::map<u16, ClientActiveObject*>::iterator n;
2320 n = m_active_objects.find(id);
2321 if(n == m_active_objects.end())
2326 bool isFreeClientActiveObjectId(u16 id,
2327 std::map<u16, ClientActiveObject*> &objects)
2332 return objects.find(id) == objects.end();
2335 u16 getFreeClientActiveObjectId(
2336 std::map<u16, ClientActiveObject*> &objects)
2338 //try to reuse id's as late as possible
2339 static u16 last_used_id = 0;
2340 u16 startid = last_used_id;
2344 if(isFreeClientActiveObjectId(last_used_id, objects))
2345 return last_used_id;
2347 if(last_used_id == startid)
2352 u16 ClientEnvironment::addActiveObject(ClientActiveObject *object)
2355 if(object->getId() == 0)
2357 u16 new_id = getFreeClientActiveObjectId(m_active_objects);
2360 infostream<<"ClientEnvironment::addActiveObject(): "
2361 <<"no free ids available"<<std::endl;
2365 object->setId(new_id);
2367 if(isFreeClientActiveObjectId(object->getId(), m_active_objects) == false)
2369 infostream<<"ClientEnvironment::addActiveObject(): "
2370 <<"id is not free ("<<object->getId()<<")"<<std::endl;
2374 infostream<<"ClientEnvironment::addActiveObject(): "
2375 <<"added (id="<<object->getId()<<")"<<std::endl;
2376 m_active_objects[object->getId()] = object;
2377 object->addToScene(m_smgr, m_texturesource, m_irr);
2378 { // Update lighting immediately
2382 v3s16 p = object->getLightPosition();
2383 MapNode n = m_map->getNode(p);
2384 light = n.getLightBlend(getDayNightRatio(), m_gamedef->ndef());
2386 catch(InvalidPositionException &e){
2387 light = blend_light(getDayNightRatio(), LIGHT_SUN, 0);
2389 object->updateLight(light);
2391 return object->getId();
2394 void ClientEnvironment::addActiveObject(u16 id, u8 type,
2395 const std::string &init_data)
2397 ClientActiveObject* obj =
2398 ClientActiveObject::create(type, m_gamedef, this);
2401 infostream<<"ClientEnvironment::addActiveObject(): "
2402 <<"id="<<id<<" type="<<type<<": Couldn't create object"
2411 obj->initialize(init_data);
2413 catch(SerializationError &e)
2415 errorstream<<"ClientEnvironment::addActiveObject():"
2416 <<" id="<<id<<" type="<<type
2417 <<": SerializationError in initialize(): "
2419 <<": init_data="<<serializeJsonString(init_data)
2423 addActiveObject(obj);
2426 void ClientEnvironment::removeActiveObject(u16 id)
2428 verbosestream<<"ClientEnvironment::removeActiveObject(): "
2429 <<"id="<<id<<std::endl;
2430 ClientActiveObject* obj = getActiveObject(id);
2433 infostream<<"ClientEnvironment::removeActiveObject(): "
2434 <<"id="<<id<<" not found"<<std::endl;
2437 obj->removeFromScene(true);
2439 m_active_objects.erase(id);
2442 void ClientEnvironment::processActiveObjectMessage(u16 id,
2443 const std::string &data)
2445 ClientActiveObject* obj = getActiveObject(id);
2448 infostream<<"ClientEnvironment::processActiveObjectMessage():"
2449 <<" got message for id="<<id<<", which doesn't exist."
2455 obj->processMessage(data);
2457 catch(SerializationError &e)
2459 errorstream<<"ClientEnvironment::processActiveObjectMessage():"
2460 <<" id="<<id<<" type="<<obj->getType()
2461 <<" SerializationError in processMessage(),"
2462 <<" message="<<serializeJsonString(data)
2468 Callbacks for activeobjects
2471 void ClientEnvironment::damageLocalPlayer(u8 damage, bool handle_hp)
2473 LocalPlayer *lplayer = getLocalPlayer();
2477 if(lplayer->hp > damage)
2478 lplayer->hp -= damage;
2483 ClientEnvEvent event;
2484 event.type = CEE_PLAYER_DAMAGE;
2485 event.player_damage.amount = damage;
2486 event.player_damage.send_to_server = handle_hp;
2487 m_client_event_queue.push_back(event);
2491 Client likes to call these
2494 void ClientEnvironment::getActiveObjects(v3f origin, f32 max_d,
2495 std::vector<DistanceSortedActiveObject> &dest)
2497 for(std::map<u16, ClientActiveObject*>::iterator
2498 i = m_active_objects.begin();
2499 i != m_active_objects.end(); ++i)
2501 ClientActiveObject* obj = i->second;
2503 f32 d = (obj->getPosition() - origin).getLength();
2508 DistanceSortedActiveObject dso(obj, d);
2510 dest.push_back(dso);
2514 ClientEnvEvent ClientEnvironment::getClientEvent()
2516 if(m_client_event_queue.empty())
2518 ClientEnvEvent event;
2519 event.type = CEE_NONE;
2522 return m_client_event_queue.pop_front();
2525 #endif // #ifndef SERVER