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 "scriptapi.h"
37 #include "nodemetadata.h"
38 #include "main.h" // For g_settings, g_profiler
41 #include "clientmap.h"
42 #include "localplayer.h"
44 #include "daynightratio.h"
46 #include "util/serialize.h"
48 #define PP(x) "("<<(x).X<<","<<(x).Y<<","<<(x).Z<<")"
50 Environment::Environment():
52 m_time_of_day_f(9000./24000),
53 m_time_of_day_speed(0),
58 Environment::~Environment()
61 for(std::list<Player*>::iterator i = m_players.begin();
62 i != m_players.end(); ++i)
68 void Environment::addPlayer(Player *player)
70 DSTACK(__FUNCTION_NAME);
72 Check that peer_ids are unique.
73 Also check that names are unique.
74 Exception: there can be multiple players with peer_id=0
76 // If peer id is non-zero, it has to be unique.
77 if(player->peer_id != 0)
78 assert(getPlayer(player->peer_id) == NULL);
79 // Name has to be unique.
80 assert(getPlayer(player->getName()) == NULL);
82 m_players.push_back(player);
85 void Environment::removePlayer(u16 peer_id)
87 DSTACK(__FUNCTION_NAME);
89 for(std::list<Player*>::iterator i = m_players.begin();
90 i != m_players.end(); ++i)
93 if(player->peer_id != peer_id)
98 // See if there is an another one
99 // (shouldn't be, but just to be sure)
104 Player * Environment::getPlayer(u16 peer_id)
106 for(std::list<Player*>::iterator i = m_players.begin();
107 i != m_players.end(); ++i)
110 if(player->peer_id == peer_id)
116 Player * Environment::getPlayer(const char *name)
118 for(std::list<Player*>::iterator i = m_players.begin();
119 i != m_players.end(); ++i)
122 if(strcmp(player->getName(), name) == 0)
128 Player * Environment::getRandomConnectedPlayer()
130 std::list<Player*> connected_players = getPlayers(true);
131 u32 chosen_one = myrand() % connected_players.size();
133 for(std::list<Player*>::iterator
134 i = connected_players.begin();
135 i != connected_players.end(); ++i)
147 Player * Environment::getNearestConnectedPlayer(v3f pos)
149 std::list<Player*> connected_players = getPlayers(true);
151 Player *nearest_player = NULL;
152 for(std::list<Player*>::iterator
153 i = connected_players.begin();
154 i != connected_players.end(); ++i)
157 f32 d = player->getPosition().getDistanceFrom(pos);
158 if(d < nearest_d || nearest_player == NULL)
161 nearest_player = player;
164 return nearest_player;
167 std::list<Player*> Environment::getPlayers()
172 std::list<Player*> Environment::getPlayers(bool ignore_disconnected)
174 std::list<Player*> newlist;
175 for(std::list<Player*>::iterator
176 i = m_players.begin();
177 i != m_players.end(); ++i)
181 if(ignore_disconnected)
183 // Ignore disconnected players
184 if(player->peer_id == 0)
188 newlist.push_back(player);
193 void Environment::printPlayers(std::ostream &o)
195 o<<"Players in environment:"<<std::endl;
196 for(std::list<Player*>::iterator i = m_players.begin();
197 i != m_players.end(); i++)
200 o<<"Player peer_id="<<player->peer_id<<std::endl;
204 u32 Environment::getDayNightRatio()
206 bool smooth = (g_settings->getS32("enable_shaders") != 0);
207 return time_to_daynight_ratio(m_time_of_day_f*24000, smooth);
210 void Environment::stepTimeOfDay(float dtime)
212 m_time_counter += dtime;
213 f32 speed = m_time_of_day_speed * 24000./(24.*3600);
214 u32 units = (u32)(m_time_counter*speed);
215 m_time_counter -= (f32)units / speed;
219 if(m_time_of_day + units >= 24000)
221 m_time_of_day = (m_time_of_day + units) % 24000;
223 m_time_of_day_f = (float)m_time_of_day / 24000.0;
226 m_time_of_day_f += m_time_of_day_speed/24/3600*dtime;
227 if(m_time_of_day_f > 1.0)
228 m_time_of_day_f -= 1.0;
229 if(m_time_of_day_f < 0.0)
230 m_time_of_day_f += 1.0;
238 ABMWithState::ABMWithState(ActiveBlockModifier *abm_):
242 // Initialize timer to random value to spread processing
243 float itv = abm->getTriggerInterval();
244 itv = MYMAX(0.001, itv); // No less than 1ms
245 int minval = MYMAX(-0.51*itv, -60); // Clamp to
246 int maxval = MYMIN(0.51*itv, 60); // +-60 seconds
247 timer = myrand_range(minval, maxval);
254 void fillRadiusBlock(v3s16 p0, s16 r, std::set<v3s16> &list)
257 for(p.X=p0.X-r; p.X<=p0.X+r; p.X++)
258 for(p.Y=p0.Y-r; p.Y<=p0.Y+r; p.Y++)
259 for(p.Z=p0.Z-r; p.Z<=p0.Z+r; p.Z++)
266 void ActiveBlockList::update(std::list<v3s16> &active_positions,
268 std::set<v3s16> &blocks_removed,
269 std::set<v3s16> &blocks_added)
274 std::set<v3s16> newlist;
275 for(std::list<v3s16>::iterator i = active_positions.begin();
276 i != active_positions.end(); ++i)
278 fillRadiusBlock(*i, radius, newlist);
282 Find out which blocks on the old list are not on the new list
284 // Go through old list
285 for(std::set<v3s16>::iterator i = m_list.begin();
286 i != m_list.end(); ++i)
289 // If not on new list, it's been removed
290 if(newlist.find(p) == newlist.end())
291 blocks_removed.insert(p);
295 Find out which blocks on the new list are not on the old list
297 // Go through new list
298 for(std::set<v3s16>::iterator i = newlist.begin();
299 i != newlist.end(); ++i)
302 // If not on old list, it's been added
303 if(m_list.find(p) == m_list.end())
304 blocks_added.insert(p);
311 for(std::set<v3s16>::iterator i = newlist.begin();
312 i != newlist.end(); ++i)
323 ServerEnvironment::ServerEnvironment(ServerMap *map, lua_State *L,
324 IGameDef *gamedef, IBackgroundBlockEmerger *emerger):
329 m_random_spawn_timer(3),
330 m_send_recommended_timer(0),
331 m_active_block_interval_overload_skip(0),
333 m_game_time_fraction_counter(0),
334 m_recommended_send_interval(0.1)
338 ServerEnvironment::~ServerEnvironment()
340 // Clear active block list.
341 // This makes the next one delete all active objects.
342 m_active_blocks.clear();
344 // Convert all objects to static and delete the active objects
345 deactivateFarObjects(true);
350 // Delete ActiveBlockModifiers
351 for(std::list<ABMWithState>::iterator
352 i = m_abms.begin(); i != m_abms.end(); ++i){
357 Map & ServerEnvironment::getMap()
362 ServerMap & ServerEnvironment::getServerMap()
368 void ServerEnvironment::serializePlayers(const std::string &savedir)
370 std::string players_path = savedir + "/players";
371 fs::CreateDir(players_path);
373 std::set<Player*> saved_players;
375 std::vector<fs::DirListNode> player_files = fs::GetDirListing(players_path);
376 for(u32 i=0; i<player_files.size(); i++)
378 if(player_files[i].dir)
381 // Full path to this file
382 std::string path = players_path + "/" + player_files[i].name;
384 //infostream<<"Checking player file "<<path<<std::endl;
386 // Load player to see what is its name
387 RemotePlayer testplayer(m_gamedef);
389 // Open file and deserialize
390 std::ifstream is(path.c_str(), std::ios_base::binary);
391 if(is.good() == false)
393 infostream<<"Failed to read "<<path<<std::endl;
396 testplayer.deSerialize(is);
399 //infostream<<"Loaded test player with name "<<testplayer.getName()<<std::endl;
401 // Search for the player
402 std::string playername = testplayer.getName();
403 Player *player = getPlayer(playername.c_str());
406 infostream<<"Didn't find matching player, ignoring file "<<path<<std::endl;
410 //infostream<<"Found matching player, overwriting."<<std::endl;
412 // OK, found. Save player there.
414 // Open file and serialize
415 std::ofstream os(path.c_str(), std::ios_base::binary);
416 if(os.good() == false)
418 infostream<<"Failed to overwrite "<<path<<std::endl;
421 player->serialize(os);
422 saved_players.insert(player);
426 for(std::list<Player*>::iterator i = m_players.begin();
427 i != m_players.end(); ++i)
430 if(saved_players.find(player) != saved_players.end())
432 /*infostream<<"Player "<<player->getName()
433 <<" was already saved."<<std::endl;*/
436 std::string playername = player->getName();
437 // Don't save unnamed player
440 //infostream<<"Not saving unnamed player."<<std::endl;
446 if(string_allowed(playername, PLAYERNAME_ALLOWED_CHARS) == false)
447 playername = "player";
448 std::string path = players_path + "/" + playername;
450 for(u32 i=0; i<1000; i++)
452 if(fs::PathExists(path) == false)
457 path = players_path + "/" + playername + itos(i);
461 infostream<<"Didn't find free file for player"<<std::endl;
466 /*infostream<<"Saving player "<<player->getName()<<" to "
468 // Open file and serialize
469 std::ofstream os(path.c_str(), std::ios_base::binary);
470 if(os.good() == false)
472 infostream<<"Failed to overwrite "<<path<<std::endl;
475 player->serialize(os);
476 saved_players.insert(player);
480 //infostream<<"Saved "<<saved_players.size()<<" players."<<std::endl;
483 void ServerEnvironment::deSerializePlayers(const std::string &savedir)
485 std::string players_path = savedir + "/players";
487 std::vector<fs::DirListNode> player_files = fs::GetDirListing(players_path);
488 for(u32 i=0; i<player_files.size(); i++)
490 if(player_files[i].dir)
493 // Full path to this file
494 std::string path = players_path + "/" + player_files[i].name;
496 //infostream<<"Checking player file "<<path<<std::endl;
498 // Load player to see what is its name
499 RemotePlayer testplayer(m_gamedef);
501 // Open file and deserialize
502 std::ifstream is(path.c_str(), std::ios_base::binary);
503 if(is.good() == false)
505 infostream<<"Failed to read "<<path<<std::endl;
508 testplayer.deSerialize(is);
511 if(!string_allowed(testplayer.getName(), PLAYERNAME_ALLOWED_CHARS))
513 infostream<<"Not loading player with invalid name: "
514 <<testplayer.getName()<<std::endl;
517 /*infostream<<"Loaded test player with name "<<testplayer.getName()
520 // Search for the player
521 std::string playername = testplayer.getName();
522 Player *player = getPlayer(playername.c_str());
523 bool newplayer = false;
526 //infostream<<"Is a new player"<<std::endl;
527 player = new RemotePlayer(m_gamedef);
533 verbosestream<<"Reading player "<<testplayer.getName()<<" from "
535 // Open file and deserialize
536 std::ifstream is(path.c_str(), std::ios_base::binary);
537 if(is.good() == false)
539 infostream<<"Failed to read "<<path<<std::endl;
542 player->deSerialize(is);
552 void ServerEnvironment::saveMeta(const std::string &savedir)
554 std::string path = savedir + "/env_meta.txt";
556 // Open file and serialize
557 std::ofstream os(path.c_str(), std::ios_base::binary);
558 if(os.good() == false)
560 infostream<<"ServerEnvironment::saveMeta(): Failed to open "
562 throw SerializationError("Couldn't save env meta");
566 args.setU64("game_time", m_game_time);
567 args.setU64("time_of_day", getTimeOfDay());
572 void ServerEnvironment::loadMeta(const std::string &savedir)
574 std::string path = savedir + "/env_meta.txt";
576 // Open file and deserialize
577 std::ifstream is(path.c_str(), std::ios_base::binary);
578 if(is.good() == false)
580 infostream<<"ServerEnvironment::loadMeta(): Failed to open "
582 throw SerializationError("Couldn't load env meta");
590 throw SerializationError
591 ("ServerEnvironment::loadMeta(): EnvArgsEnd not found");
593 std::getline(is, line);
594 std::string trimmedline = trim(line);
595 if(trimmedline == "EnvArgsEnd")
597 args.parseConfigLine(line);
601 m_game_time = args.getU64("game_time");
602 }catch(SettingNotFoundException &e){
603 // Getting this is crucial, otherwise timestamps are useless
604 throw SerializationError("Couldn't load env meta game_time");
608 m_time_of_day = args.getU64("time_of_day");
609 }catch(SettingNotFoundException &e){
610 // This is not as important
611 m_time_of_day = 9000;
617 ActiveBlockModifier *abm;
619 std::set<content_t> required_neighbors;
625 ServerEnvironment *m_env;
626 std::map<content_t, std::list<ActiveABM> > m_aabms;
628 ABMHandler(std::list<ABMWithState> &abms,
629 float dtime_s, ServerEnvironment *env,
635 INodeDefManager *ndef = env->getGameDef()->ndef();
636 for(std::list<ABMWithState>::iterator
637 i = abms.begin(); i != abms.end(); ++i){
638 ActiveBlockModifier *abm = i->abm;
639 float trigger_interval = abm->getTriggerInterval();
640 if(trigger_interval < 0.001)
641 trigger_interval = 0.001;
642 float actual_interval = dtime_s;
645 if(i->timer < trigger_interval)
647 i->timer -= trigger_interval;
648 actual_interval = trigger_interval;
650 float intervals = actual_interval / trigger_interval;
653 float chance = abm->getTriggerChance();
658 aabm.chance = chance / intervals;
662 std::set<std::string> required_neighbors_s
663 = abm->getRequiredNeighbors();
664 for(std::set<std::string>::iterator
665 i = required_neighbors_s.begin();
666 i != required_neighbors_s.end(); i++)
668 ndef->getIds(*i, aabm.required_neighbors);
671 std::set<std::string> contents_s = abm->getTriggerContents();
672 for(std::set<std::string>::iterator
673 i = contents_s.begin(); i != contents_s.end(); i++)
675 std::set<content_t> ids;
676 ndef->getIds(*i, ids);
677 for(std::set<content_t>::const_iterator k = ids.begin();
681 std::map<content_t, std::list<ActiveABM> >::iterator j;
683 if(j == m_aabms.end()){
684 std::list<ActiveABM> aabmlist;
685 m_aabms[c] = aabmlist;
688 j->second.push_back(aabm);
693 void apply(MapBlock *block)
698 ServerMap *map = &m_env->getServerMap();
701 for(p0.X=0; p0.X<MAP_BLOCKSIZE; p0.X++)
702 for(p0.Y=0; p0.Y<MAP_BLOCKSIZE; p0.Y++)
703 for(p0.Z=0; p0.Z<MAP_BLOCKSIZE; p0.Z++)
705 MapNode n = block->getNodeNoEx(p0);
706 content_t c = n.getContent();
707 v3s16 p = p0 + block->getPosRelative();
709 std::map<content_t, std::list<ActiveABM> >::iterator j;
711 if(j == m_aabms.end())
714 for(std::list<ActiveABM>::iterator
715 i = j->second.begin(); i != j->second.end(); i++)
717 if(myrand() % i->chance != 0)
721 if(!i->required_neighbors.empty())
724 for(p1.X = p.X-1; p1.X <= p.X+1; p1.X++)
725 for(p1.Y = p.Y-1; p1.Y <= p.Y+1; p1.Y++)
726 for(p1.Z = p.Z-1; p1.Z <= p.Z+1; p1.Z++)
730 MapNode n = map->getNodeNoEx(p1);
731 content_t c = n.getContent();
732 std::set<content_t>::const_iterator k;
733 k = i->required_neighbors.find(c);
734 if(k != i->required_neighbors.end()){
738 // No required neighbor found
743 // Find out how many objects the block contains
744 u32 active_object_count = block->m_static_objects.m_active.size();
745 // Find out how many objects this and all the neighbors contain
746 u32 active_object_count_wider = 0;
747 u32 wider_unknown_count = 0;
748 for(s16 x=-1; x<=1; x++)
749 for(s16 y=-1; y<=1; y++)
750 for(s16 z=-1; z<=1; z++)
752 MapBlock *block2 = map->getBlockNoCreateNoEx(
753 block->getPos() + v3s16(x,y,z));
755 wider_unknown_count = 0;
758 active_object_count_wider +=
759 block2->m_static_objects.m_active.size()
760 + block2->m_static_objects.m_stored.size();
763 u32 wider_known_count = 3*3*3 - wider_unknown_count;
764 active_object_count_wider += wider_unknown_count * active_object_count_wider / wider_known_count;
766 // Call all the trigger variations
767 i->abm->trigger(m_env, p, n);
768 i->abm->trigger(m_env, p, n,
769 active_object_count, active_object_count_wider);
775 void ServerEnvironment::activateBlock(MapBlock *block, u32 additional_dtime)
777 // Get time difference
779 u32 stamp = block->getTimestamp();
780 if(m_game_time > stamp && stamp != BLOCK_TIMESTAMP_UNDEFINED)
781 dtime_s = m_game_time - block->getTimestamp();
782 dtime_s += additional_dtime;
784 /*infostream<<"ServerEnvironment::activateBlock(): block timestamp: "
785 <<stamp<<", game time: "<<m_game_time<<std::endl;*/
787 // Set current time as timestamp
788 block->setTimestampNoChangedFlag(m_game_time);
790 /*infostream<<"ServerEnvironment::activateBlock(): block is "
791 <<dtime_s<<" seconds old."<<std::endl;*/
793 // Activate stored objects
794 activateObjects(block, dtime_s);
797 std::map<v3s16, NodeTimer> elapsed_timers =
798 block->m_node_timers.step((float)dtime_s);
799 if(!elapsed_timers.empty()){
801 for(std::map<v3s16, NodeTimer>::iterator
802 i = elapsed_timers.begin();
803 i != elapsed_timers.end(); i++){
804 n = block->getNodeNoEx(i->first);
805 v3s16 p = i->first + block->getPosRelative();
806 if(scriptapi_node_on_timer(m_lua,p,n,i->second.elapsed))
807 block->setNodeTimer(i->first,NodeTimer(i->second.timeout,0));
811 /* Handle ActiveBlockModifiers */
812 ABMHandler abmhandler(m_abms, dtime_s, this, false);
813 abmhandler.apply(block);
816 void ServerEnvironment::addActiveBlockModifier(ActiveBlockModifier *abm)
818 m_abms.push_back(ABMWithState(abm));
821 bool ServerEnvironment::setNode(v3s16 p, const MapNode &n)
823 INodeDefManager *ndef = m_gamedef->ndef();
824 MapNode n_old = m_map->getNodeNoEx(p);
826 if(ndef->get(n_old).has_on_destruct)
827 scriptapi_node_on_destruct(m_lua, p, n_old);
829 bool succeeded = m_map->addNodeWithEvent(p, n);
832 // Call post-destructor
833 if(ndef->get(n_old).has_after_destruct)
834 scriptapi_node_after_destruct(m_lua, p, n_old);
836 if(ndef->get(n).has_on_construct)
837 scriptapi_node_on_construct(m_lua, p, n);
841 bool ServerEnvironment::removeNode(v3s16 p)
843 INodeDefManager *ndef = m_gamedef->ndef();
844 MapNode n_old = m_map->getNodeNoEx(p);
846 if(ndef->get(n_old).has_on_destruct)
847 scriptapi_node_on_destruct(m_lua, p, n_old);
849 // This is slightly optimized compared to addNodeWithEvent(air)
850 bool succeeded = m_map->removeNodeWithEvent(p);
853 // Call post-destructor
854 if(ndef->get(n_old).has_after_destruct)
855 scriptapi_node_after_destruct(m_lua, p, n_old);
856 // Air doesn't require constructor
860 std::set<u16> ServerEnvironment::getObjectsInsideRadius(v3f pos, float radius)
862 std::set<u16> objects;
863 for(std::map<u16, ServerActiveObject*>::iterator
864 i = m_active_objects.begin();
865 i != m_active_objects.end(); ++i)
867 ServerActiveObject* obj = i->second;
869 v3f objectpos = obj->getBasePosition();
870 if(objectpos.getDistanceFrom(pos) > radius)
877 void ServerEnvironment::clearAllObjects()
879 infostream<<"ServerEnvironment::clearAllObjects(): "
880 <<"Removing all active objects"<<std::endl;
881 std::list<u16> objects_to_remove;
882 for(std::map<u16, ServerActiveObject*>::iterator
883 i = m_active_objects.begin();
884 i != m_active_objects.end(); ++i)
886 ServerActiveObject* obj = i->second;
887 if(obj->getType() == ACTIVEOBJECT_TYPE_PLAYER)
890 v3f objectpos = obj->getBasePosition();
891 // Delete static object if block is loaded
892 if(obj->m_static_exists){
893 MapBlock *block = m_map->getBlockNoCreateNoEx(obj->m_static_block);
895 block->m_static_objects.remove(id);
896 block->raiseModified(MOD_STATE_WRITE_NEEDED,
898 obj->m_static_exists = false;
901 // If known by some client, don't delete immediately
902 if(obj->m_known_by_count > 0){
903 obj->m_pending_deactivation = true;
904 obj->m_removed = true;
908 // Tell the object about removal
909 obj->removingFromEnvironment();
910 // Deregister in scripting api
911 scriptapi_rm_object_reference(m_lua, obj);
913 // Delete active object
914 if(obj->environmentDeletes())
916 // Id to be removed from m_active_objects
917 objects_to_remove.push_back(id);
919 // Remove references from m_active_objects
920 for(std::list<u16>::iterator i = objects_to_remove.begin();
921 i != objects_to_remove.end(); ++i)
923 m_active_objects.erase(*i);
926 std::list<v3s16> loadable_blocks;
927 infostream<<"ServerEnvironment::clearAllObjects(): "
928 <<"Listing all loadable blocks"<<std::endl;
929 m_map->listAllLoadableBlocks(loadable_blocks);
930 infostream<<"ServerEnvironment::clearAllObjects(): "
931 <<"Done listing all loadable blocks: "
932 <<loadable_blocks.size()
933 <<", now clearing"<<std::endl;
934 u32 report_interval = loadable_blocks.size() / 10;
935 u32 num_blocks_checked = 0;
936 u32 num_blocks_cleared = 0;
937 u32 num_objs_cleared = 0;
938 for(std::list<v3s16>::iterator i = loadable_blocks.begin();
939 i != loadable_blocks.end(); ++i)
942 MapBlock *block = m_map->emergeBlock(p, false);
944 errorstream<<"ServerEnvironment::clearAllObjects(): "
945 <<"Failed to emerge block "<<PP(p)<<std::endl;
948 u32 num_stored = block->m_static_objects.m_stored.size();
949 u32 num_active = block->m_static_objects.m_active.size();
950 if(num_stored != 0 || num_active != 0){
951 block->m_static_objects.m_stored.clear();
952 block->m_static_objects.m_active.clear();
953 block->raiseModified(MOD_STATE_WRITE_NEEDED,
955 num_objs_cleared += num_stored + num_active;
956 num_blocks_cleared++;
958 num_blocks_checked++;
960 if(num_blocks_checked % report_interval == 0){
961 float percent = 100.0 * (float)num_blocks_checked /
962 loadable_blocks.size();
963 infostream<<"ServerEnvironment::clearAllObjects(): "
964 <<"Cleared "<<num_objs_cleared<<" objects"
965 <<" in "<<num_blocks_cleared<<" blocks ("
966 <<percent<<"%)"<<std::endl;
969 infostream<<"ServerEnvironment::clearAllObjects(): "
970 <<"Finished: Cleared "<<num_objs_cleared<<" objects"
971 <<" in "<<num_blocks_cleared<<" blocks"<<std::endl;
974 void ServerEnvironment::step(float dtime)
976 DSTACK(__FUNCTION_NAME);
978 //TimeTaker timer("ServerEnv step");
980 /* Step time of day */
981 stepTimeOfDay(dtime);
984 // NOTE: This is kind of funny on a singleplayer game, but doesn't
985 // really matter that much.
986 m_recommended_send_interval = g_settings->getFloat("dedicated_server_step");
992 m_game_time_fraction_counter += dtime;
993 u32 inc_i = (u32)m_game_time_fraction_counter;
994 m_game_time += inc_i;
995 m_game_time_fraction_counter -= (float)inc_i;
1002 ScopeProfiler sp(g_profiler, "SEnv: handle players avg", SPT_AVG);
1003 for(std::list<Player*>::iterator i = m_players.begin();
1004 i != m_players.end(); ++i)
1006 Player *player = *i;
1008 // Ignore disconnected players
1009 if(player->peer_id == 0)
1012 v3f playerpos = player->getPosition();
1015 player->move(dtime, *m_map, 100*BS);
1020 Manage active block list
1022 if(m_active_blocks_management_interval.step(dtime, 2.0))
1024 ScopeProfiler sp(g_profiler, "SEnv: manage act. block list avg /2s", SPT_AVG);
1026 Get player block positions
1028 std::list<v3s16> players_blockpos;
1029 for(std::list<Player*>::iterator
1030 i = m_players.begin();
1031 i != m_players.end(); ++i)
1033 Player *player = *i;
1034 // Ignore disconnected players
1035 if(player->peer_id == 0)
1037 v3s16 blockpos = getNodeBlockPos(
1038 floatToInt(player->getPosition(), BS));
1039 players_blockpos.push_back(blockpos);
1043 Update list of active blocks, collecting changes
1045 const s16 active_block_range = g_settings->getS16("active_block_range");
1046 std::set<v3s16> blocks_removed;
1047 std::set<v3s16> blocks_added;
1048 m_active_blocks.update(players_blockpos, active_block_range,
1049 blocks_removed, blocks_added);
1052 Handle removed blocks
1055 // Convert active objects that are no more in active blocks to static
1056 deactivateFarObjects(false);
1058 for(std::set<v3s16>::iterator
1059 i = blocks_removed.begin();
1060 i != blocks_removed.end(); ++i)
1064 /*infostream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
1065 <<") became inactive"<<std::endl;*/
1067 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1071 // Set current time as timestamp (and let it set ChangedFlag)
1072 block->setTimestamp(m_game_time);
1079 for(std::set<v3s16>::iterator
1080 i = blocks_added.begin();
1081 i != blocks_added.end(); ++i)
1085 /*infostream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
1086 <<") became active"<<std::endl;*/
1088 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1090 // Block needs to be fetched first
1091 m_emerger->queueBlockEmerge(p, false);
1092 m_active_blocks.m_list.erase(p);
1096 activateBlock(block);
1101 Mess around in active blocks
1103 if(m_active_blocks_nodemetadata_interval.step(dtime, 1.0))
1105 ScopeProfiler sp(g_profiler, "SEnv: mess in act. blocks avg /1s", SPT_AVG);
1109 for(std::set<v3s16>::iterator
1110 i = m_active_blocks.m_list.begin();
1111 i != m_active_blocks.m_list.end(); ++i)
1115 /*infostream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
1116 <<") being handled"<<std::endl;*/
1118 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1122 // Reset block usage timer
1123 block->resetUsageTimer();
1125 // Set current time as timestamp
1126 block->setTimestampNoChangedFlag(m_game_time);
1127 // If time has changed much from the one on disk,
1128 // set block to be saved when it is unloaded
1129 if(block->getTimestamp() > block->getDiskTimestamp() + 60)
1130 block->raiseModified(MOD_STATE_WRITE_AT_UNLOAD,
1131 "Timestamp older than 60s (step)");
1134 std::map<v3s16, NodeTimer> elapsed_timers =
1135 block->m_node_timers.step((float)dtime);
1136 if(!elapsed_timers.empty()){
1138 for(std::map<v3s16, NodeTimer>::iterator
1139 i = elapsed_timers.begin();
1140 i != elapsed_timers.end(); i++){
1141 n = block->getNodeNoEx(i->first);
1142 p = i->first + block->getPosRelative();
1143 if(scriptapi_node_on_timer(m_lua,p,n,i->second.elapsed))
1144 block->setNodeTimer(i->first,NodeTimer(i->second.timeout,0));
1150 const float abm_interval = 1.0;
1151 if(m_active_block_modifier_interval.step(dtime, abm_interval))
1153 if(m_active_block_interval_overload_skip > 0){
1154 ScopeProfiler sp(g_profiler, "SEnv: ABM overload skips");
1155 m_active_block_interval_overload_skip--;
1158 ScopeProfiler sp(g_profiler, "SEnv: modify in blocks avg /1s", SPT_AVG);
1159 TimeTaker timer("modify in active blocks");
1161 // Initialize handling of ActiveBlockModifiers
1162 ABMHandler abmhandler(m_abms, abm_interval, this, true);
1164 for(std::set<v3s16>::iterator
1165 i = m_active_blocks.m_list.begin();
1166 i != m_active_blocks.m_list.end(); ++i)
1170 /*infostream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
1171 <<") being handled"<<std::endl;*/
1173 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1177 // Set current time as timestamp
1178 block->setTimestampNoChangedFlag(m_game_time);
1180 /* Handle ActiveBlockModifiers */
1181 abmhandler.apply(block);
1184 u32 time_ms = timer.stop(true);
1185 u32 max_time_ms = 200;
1186 if(time_ms > max_time_ms){
1187 infostream<<"WARNING: active block modifiers took "
1188 <<time_ms<<"ms (longer than "
1189 <<max_time_ms<<"ms)"<<std::endl;
1190 m_active_block_interval_overload_skip = (time_ms / max_time_ms) + 1;
1195 Step script environment (run global on_step())
1197 scriptapi_environment_step(m_lua, dtime);
1203 ScopeProfiler sp(g_profiler, "SEnv: step act. objs avg", SPT_AVG);
1204 //TimeTaker timer("Step active objects");
1206 g_profiler->avg("SEnv: num of objects", m_active_objects.size());
1208 // This helps the objects to send data at the same time
1209 bool send_recommended = false;
1210 m_send_recommended_timer += dtime;
1211 if(m_send_recommended_timer > getSendRecommendedInterval())
1213 m_send_recommended_timer -= getSendRecommendedInterval();
1214 send_recommended = true;
1217 for(std::map<u16, ServerActiveObject*>::iterator
1218 i = m_active_objects.begin();
1219 i != m_active_objects.end(); ++i)
1221 ServerActiveObject* obj = i->second;
1222 // Remove non-peaceful mobs on peaceful mode
1223 if(g_settings->getBool("only_peaceful_mobs")){
1224 if(!obj->isPeaceful())
1225 obj->m_removed = true;
1227 // Don't step if is to be removed or stored statically
1228 if(obj->m_removed || obj->m_pending_deactivation)
1231 obj->step(dtime, send_recommended);
1232 // Read messages from object
1233 while(!obj->m_messages_out.empty())
1235 m_active_object_messages.push_back(
1236 obj->m_messages_out.pop_front());
1242 Manage active objects
1244 if(m_object_management_interval.step(dtime, 0.5))
1246 ScopeProfiler sp(g_profiler, "SEnv: remove removed objs avg /.5s", SPT_AVG);
1248 Remove objects that satisfy (m_removed && m_known_by_count==0)
1250 removeRemovedObjects();
1254 ServerActiveObject* ServerEnvironment::getActiveObject(u16 id)
1256 std::map<u16, ServerActiveObject*>::iterator n;
1257 n = m_active_objects.find(id);
1258 if(n == m_active_objects.end())
1263 bool isFreeServerActiveObjectId(u16 id,
1264 std::map<u16, ServerActiveObject*> &objects)
1269 return objects.find(id) == objects.end();
1272 u16 getFreeServerActiveObjectId(
1273 std::map<u16, ServerActiveObject*> &objects)
1278 if(isFreeServerActiveObjectId(new_id, objects))
1288 u16 ServerEnvironment::addActiveObject(ServerActiveObject *object)
1291 u16 id = addActiveObjectRaw(object, true, 0);
1296 bool ServerEnvironment::addActiveObjectAsStatic(ServerActiveObject *obj)
1300 v3f objectpos = obj->getBasePosition();
1302 // The block in which the object resides in
1303 v3s16 blockpos_o = getNodeBlockPos(floatToInt(objectpos, BS));
1306 Update the static data
1309 // Create new static object
1310 std::string staticdata = obj->getStaticData();
1311 StaticObject s_obj(obj->getType(), objectpos, staticdata);
1312 // Add to the block where the object is located in
1313 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1314 // Get or generate the block
1315 MapBlock *block = m_map->emergeBlock(blockpos);
1317 bool succeeded = false;
1321 block->m_static_objects.insert(0, s_obj);
1322 block->raiseModified(MOD_STATE_WRITE_AT_UNLOAD,
1323 "addActiveObjectAsStatic");
1327 infostream<<"ServerEnvironment::addActiveObjectAsStatic: "
1328 <<"Could not find or generate "
1329 <<"a block for storing static object"<<std::endl;
1333 if(obj->environmentDeletes())
1341 Finds out what new objects have been added to
1342 inside a radius around a position
1344 void ServerEnvironment::getAddedActiveObjects(v3s16 pos, s16 radius,
1345 std::set<u16> ¤t_objects,
1346 std::set<u16> &added_objects)
1348 v3f pos_f = intToFloat(pos, BS);
1349 f32 radius_f = radius * BS;
1351 Go through the object list,
1352 - discard m_removed objects,
1353 - discard objects that are too far away,
1354 - discard objects that are found in current_objects.
1355 - add remaining objects to added_objects
1357 for(std::map<u16, ServerActiveObject*>::iterator
1358 i = m_active_objects.begin();
1359 i != m_active_objects.end(); ++i)
1363 ServerActiveObject *object = i->second;
1366 // Discard if removed
1367 if(object->m_removed)
1369 if(object->unlimitedTransferDistance() == false){
1370 // Discard if too far
1371 f32 distance_f = object->getBasePosition().getDistanceFrom(pos_f);
1372 if(distance_f > radius_f)
1375 // Discard if already on current_objects
1376 std::set<u16>::iterator n;
1377 n = current_objects.find(id);
1378 if(n != current_objects.end())
1380 // Add to added_objects
1381 added_objects.insert(id);
1386 Finds out what objects have been removed from
1387 inside a radius around a position
1389 void ServerEnvironment::getRemovedActiveObjects(v3s16 pos, s16 radius,
1390 std::set<u16> ¤t_objects,
1391 std::set<u16> &removed_objects)
1393 v3f pos_f = intToFloat(pos, BS);
1394 f32 radius_f = radius * BS;
1396 Go through current_objects; object is removed if:
1397 - object is not found in m_active_objects (this is actually an
1398 error condition; objects should be set m_removed=true and removed
1399 only after all clients have been informed about removal), or
1400 - object has m_removed=true, or
1401 - object is too far away
1403 for(std::set<u16>::iterator
1404 i = current_objects.begin();
1405 i != current_objects.end(); ++i)
1408 ServerActiveObject *object = getActiveObject(id);
1411 infostream<<"ServerEnvironment::getRemovedActiveObjects():"
1412 <<" object in current_objects is NULL"<<std::endl;
1413 removed_objects.insert(id);
1417 if(object->m_removed)
1419 removed_objects.insert(id);
1423 // If transfer distance is unlimited, don't remove
1424 if(object->unlimitedTransferDistance())
1427 f32 distance_f = object->getBasePosition().getDistanceFrom(pos_f);
1429 if(distance_f >= radius_f)
1431 removed_objects.insert(id);
1439 ActiveObjectMessage ServerEnvironment::getActiveObjectMessage()
1441 if(m_active_object_messages.empty())
1442 return ActiveObjectMessage(0);
1444 return m_active_object_messages.pop_front();
1448 ************ Private methods *************
1451 u16 ServerEnvironment::addActiveObjectRaw(ServerActiveObject *object,
1452 bool set_changed, u32 dtime_s)
1455 if(object->getId() == 0){
1456 u16 new_id = getFreeServerActiveObjectId(m_active_objects);
1459 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1460 <<"no free ids available"<<std::endl;
1461 if(object->environmentDeletes())
1465 object->setId(new_id);
1468 verbosestream<<"ServerEnvironment::addActiveObjectRaw(): "
1469 <<"supplied with id "<<object->getId()<<std::endl;
1471 if(isFreeServerActiveObjectId(object->getId(), m_active_objects) == false)
1473 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1474 <<"id is not free ("<<object->getId()<<")"<<std::endl;
1475 if(object->environmentDeletes())
1479 /*infostream<<"ServerEnvironment::addActiveObjectRaw(): "
1480 <<"added (id="<<object->getId()<<")"<<std::endl;*/
1482 m_active_objects[object->getId()] = object;
1484 verbosestream<<"ServerEnvironment::addActiveObjectRaw(): "
1485 <<"Added id="<<object->getId()<<"; there are now "
1486 <<m_active_objects.size()<<" active objects."
1489 // Register reference in scripting api (must be done before post-init)
1490 scriptapi_add_object_reference(m_lua, object);
1491 // Post-initialize object
1492 object->addedToEnvironment(dtime_s);
1494 // Add static data to block
1495 if(object->isStaticAllowed())
1497 // Add static object to active static list of the block
1498 v3f objectpos = object->getBasePosition();
1499 std::string staticdata = object->getStaticData();
1500 StaticObject s_obj(object->getType(), objectpos, staticdata);
1501 // Add to the block where the object is located in
1502 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1503 MapBlock *block = m_map->getBlockNoCreateNoEx(blockpos);
1506 block->m_static_objects.m_active[object->getId()] = s_obj;
1507 object->m_static_exists = true;
1508 object->m_static_block = blockpos;
1511 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1512 "addActiveObjectRaw");
1515 v3s16 p = floatToInt(objectpos, BS);
1516 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1517 <<"could not find block for storing id="<<object->getId()
1518 <<" statically (pos="<<PP(p)<<")"<<std::endl;
1522 return object->getId();
1526 Remove objects that satisfy (m_removed && m_known_by_count==0)
1528 void ServerEnvironment::removeRemovedObjects()
1530 std::list<u16> objects_to_remove;
1531 for(std::map<u16, ServerActiveObject*>::iterator
1532 i = m_active_objects.begin();
1533 i != m_active_objects.end(); ++i)
1536 ServerActiveObject* obj = i->second;
1537 // This shouldn't happen but check it
1540 infostream<<"NULL object found in ServerEnvironment"
1541 <<" while finding removed objects. id="<<id<<std::endl;
1542 // Id to be removed from m_active_objects
1543 objects_to_remove.push_back(id);
1548 We will delete objects that are marked as removed or thatare
1549 waiting for deletion after deactivation
1551 if(obj->m_removed == false && obj->m_pending_deactivation == false)
1555 Delete static data from block if is marked as removed
1557 if(obj->m_static_exists && obj->m_removed)
1559 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
1561 block->m_static_objects.remove(id);
1562 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1563 "removeRemovedObjects");
1564 obj->m_static_exists = false;
1566 infostream << "failed to emerge block from which "
1567 "an object to be removed was loaded from. id="<<id<<std::endl;
1571 // If m_known_by_count > 0, don't actually remove.
1572 if(obj->m_known_by_count > 0)
1575 // Tell the object about removal
1576 obj->removingFromEnvironment();
1577 // Deregister in scripting api
1578 scriptapi_rm_object_reference(m_lua, obj);
1581 if(obj->environmentDeletes())
1583 // Id to be removed from m_active_objects
1584 objects_to_remove.push_back(id);
1586 // Remove references from m_active_objects
1587 for(std::list<u16>::iterator i = objects_to_remove.begin();
1588 i != objects_to_remove.end(); ++i)
1590 m_active_objects.erase(*i);
1594 static void print_hexdump(std::ostream &o, const std::string &data)
1596 const int linelength = 16;
1597 for(int l=0; ; l++){
1598 int i0 = linelength * l;
1599 bool at_end = false;
1600 int thislinelength = linelength;
1601 if(i0 + thislinelength > (int)data.size()){
1602 thislinelength = data.size() - i0;
1605 for(int di=0; di<linelength; di++){
1608 if(di<thislinelength)
1609 snprintf(buf, 4, "%.2x ", data[i]);
1611 snprintf(buf, 4, " ");
1615 for(int di=0; di<thislinelength; di++){
1629 Convert stored objects from blocks near the players to active.
1631 void ServerEnvironment::activateObjects(MapBlock *block, u32 dtime_s)
1635 // Ignore if no stored objects (to not set changed flag)
1636 if(block->m_static_objects.m_stored.size() == 0)
1638 verbosestream<<"ServerEnvironment::activateObjects(): "
1639 <<"activating objects of block "<<PP(block->getPos())
1640 <<" ("<<block->m_static_objects.m_stored.size()
1641 <<" objects)"<<std::endl;
1642 bool large_amount = (block->m_static_objects.m_stored.size() > 49);
1644 errorstream<<"suspiciously large amount of objects detected: "
1645 <<block->m_static_objects.m_stored.size()<<" in "
1646 <<PP(block->getPos())
1647 <<"; removing all of them."<<std::endl;
1648 // Clear stored list
1649 block->m_static_objects.m_stored.clear();
1650 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1651 "stored list cleared in activateObjects due to "
1652 "large amount of objects");
1655 // A list for objects that couldn't be converted to active for some
1656 // reason. They will be stored back.
1657 std::list<StaticObject> new_stored;
1658 // Loop through stored static objects
1659 for(std::list<StaticObject>::iterator
1660 i = block->m_static_objects.m_stored.begin();
1661 i != block->m_static_objects.m_stored.end(); ++i)
1663 /*infostream<<"Server: Creating an active object from "
1664 <<"static data"<<std::endl;*/
1665 StaticObject &s_obj = *i;
1666 // Create an active object from the data
1667 ServerActiveObject *obj = ServerActiveObject::create
1668 (s_obj.type, this, 0, s_obj.pos, s_obj.data);
1669 // If couldn't create object, store static data back.
1672 errorstream<<"ServerEnvironment::activateObjects(): "
1673 <<"failed to create active object from static object "
1674 <<"in block "<<PP(s_obj.pos/BS)
1675 <<" type="<<(int)s_obj.type<<" data:"<<std::endl;
1676 print_hexdump(verbosestream, s_obj.data);
1678 new_stored.push_back(s_obj);
1681 verbosestream<<"ServerEnvironment::activateObjects(): "
1682 <<"activated static object pos="<<PP(s_obj.pos/BS)
1683 <<" type="<<(int)s_obj.type<<std::endl;
1684 // This will also add the object to the active static list
1685 addActiveObjectRaw(obj, false, dtime_s);
1687 // Clear stored list
1688 block->m_static_objects.m_stored.clear();
1689 // Add leftover failed stuff to stored list
1690 for(std::list<StaticObject>::iterator
1691 i = new_stored.begin();
1692 i != new_stored.end(); ++i)
1694 StaticObject &s_obj = *i;
1695 block->m_static_objects.m_stored.push_back(s_obj);
1698 Note: Block hasn't really been modified here.
1699 The objects have just been activated and moved from the stored
1700 static list to the active static list.
1701 As such, the block is essentially the same.
1702 Thus, do not call block->raiseModified(MOD_STATE_WRITE_NEEDED).
1703 Otherwise there would be a huge amount of unnecessary I/O.
1708 Convert objects that are not standing inside active blocks to static.
1710 If m_known_by_count != 0, active object is not deleted, but static
1711 data is still updated.
1713 If force_delete is set, active object is deleted nevertheless. It
1714 shall only be set so in the destructor of the environment.
1716 If block wasn't generated (not in memory or on disk),
1718 void ServerEnvironment::deactivateFarObjects(bool force_delete)
1720 std::list<u16> objects_to_remove;
1721 for(std::map<u16, ServerActiveObject*>::iterator
1722 i = m_active_objects.begin();
1723 i != m_active_objects.end(); ++i)
1725 ServerActiveObject* obj = i->second;
1728 // Do not deactivate if static data creation not allowed
1729 if(!force_delete && !obj->isStaticAllowed())
1732 // If pending deactivation, let removeRemovedObjects() do it
1733 if(!force_delete && obj->m_pending_deactivation)
1737 v3f objectpos = obj->getBasePosition();
1739 // The block in which the object resides in
1740 v3s16 blockpos_o = getNodeBlockPos(floatToInt(objectpos, BS));
1742 // If block is active, don't remove
1743 if(!force_delete && m_active_blocks.contains(blockpos_o))
1746 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
1747 <<"deactivating object id="<<id<<" on inactive block "
1748 <<PP(blockpos_o)<<std::endl;
1750 // If known by some client, don't immediately delete.
1751 bool pending_delete = (obj->m_known_by_count > 0 && !force_delete);
1754 Update the static data
1757 if(obj->isStaticAllowed())
1759 // Create new static object
1760 std::string staticdata_new = obj->getStaticData();
1761 StaticObject s_obj(obj->getType(), objectpos, staticdata_new);
1763 bool stays_in_same_block = false;
1764 bool data_changed = true;
1766 if(obj->m_static_exists){
1767 if(obj->m_static_block == blockpos_o)
1768 stays_in_same_block = true;
1770 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
1772 std::map<u16, StaticObject>::iterator n =
1773 block->m_static_objects.m_active.find(id);
1774 if(n != block->m_static_objects.m_active.end()){
1775 StaticObject static_old = n->second;
1777 float save_movem = obj->getMinimumSavedMovement();
1779 if(static_old.data == staticdata_new &&
1780 (static_old.pos - objectpos).getLength() < save_movem)
1781 data_changed = false;
1783 errorstream<<"ServerEnvironment::deactivateFarObjects(): "
1784 <<"id="<<id<<" m_static_exists=true but "
1785 <<"static data doesn't actually exist in "
1786 <<PP(obj->m_static_block)<<std::endl;
1790 bool shall_be_written = (!stays_in_same_block || data_changed);
1792 // Delete old static object
1793 if(obj->m_static_exists)
1795 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
1798 block->m_static_objects.remove(id);
1799 obj->m_static_exists = false;
1800 // Only mark block as modified if data changed considerably
1801 if(shall_be_written)
1802 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1803 "deactivateFarObjects: Static data "
1804 "changed considerably");
1808 // Add to the block where the object is located in
1809 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1810 // Get or generate the block
1811 MapBlock *block = NULL;
1813 block = m_map->emergeBlock(blockpos);
1814 } catch(InvalidPositionException &e){
1815 // Handled via NULL pointer
1820 if(block->m_static_objects.m_stored.size() >= 49){
1821 errorstream<<"ServerEnv: Trying to store id="<<obj->getId()
1822 <<" statically but block "<<PP(blockpos)
1823 <<" already contains "
1824 <<block->m_static_objects.m_stored.size()
1825 <<" (over 49) objects."
1826 <<" Forcing delete."<<std::endl;
1827 force_delete = true;
1829 u16 new_id = pending_delete ? id : 0;
1830 // If static counterpart already exists, remove it first.
1831 // This shouldn't happen, but happens rarely for some
1832 // unknown reason. Unsuccessful attempts have been made to
1833 // find said reason.
1834 if(new_id && block->m_static_objects.m_active.find(new_id) != block->m_static_objects.m_active.end()){
1835 infostream<<"ServerEnv: WARNING: Performing hack #83274"
1837 block->m_static_objects.remove(new_id);
1839 block->m_static_objects.insert(new_id, s_obj);
1841 // Only mark block as modified if data changed considerably
1842 if(shall_be_written)
1843 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1844 "deactivateFarObjects: Static data "
1845 "changed considerably");
1847 obj->m_static_exists = true;
1848 obj->m_static_block = block->getPos();
1853 v3s16 p = floatToInt(objectpos, BS);
1854 errorstream<<"ServerEnv: Could not find or generate "
1855 <<"a block for storing id="<<obj->getId()
1856 <<" statically (pos="<<PP(p)<<")"<<std::endl;
1863 If known by some client, set pending deactivation.
1864 Otherwise delete it immediately.
1867 if(pending_delete && !force_delete)
1869 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
1870 <<"object id="<<id<<" is known by clients"
1871 <<"; not deleting yet"<<std::endl;
1873 obj->m_pending_deactivation = true;
1877 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
1878 <<"object id="<<id<<" is not known by clients"
1879 <<"; deleting"<<std::endl;
1881 // Tell the object about removal
1882 obj->removingFromEnvironment();
1883 // Deregister in scripting api
1884 scriptapi_rm_object_reference(m_lua, obj);
1886 // Delete active object
1887 if(obj->environmentDeletes())
1889 // Id to be removed from m_active_objects
1890 objects_to_remove.push_back(id);
1893 // Remove references from m_active_objects
1894 for(std::list<u16>::iterator i = objects_to_remove.begin();
1895 i != objects_to_remove.end(); ++i)
1897 m_active_objects.erase(*i);
1904 #include "clientsimpleobject.h"
1910 ClientEnvironment::ClientEnvironment(ClientMap *map, scene::ISceneManager *smgr,
1911 ITextureSource *texturesource, IGameDef *gamedef,
1912 IrrlichtDevice *irr):
1915 m_texturesource(texturesource),
1921 ClientEnvironment::~ClientEnvironment()
1923 // delete active objects
1924 for(std::map<u16, ClientActiveObject*>::iterator
1925 i = m_active_objects.begin();
1926 i != m_active_objects.end(); ++i)
1931 for(std::list<ClientSimpleObject*>::iterator
1932 i = m_simple_objects.begin(); i != m_simple_objects.end(); ++i)
1941 Map & ClientEnvironment::getMap()
1946 ClientMap & ClientEnvironment::getClientMap()
1951 void ClientEnvironment::addPlayer(Player *player)
1953 DSTACK(__FUNCTION_NAME);
1955 It is a failure if player is local and there already is a local
1958 assert(!(player->isLocal() == true && getLocalPlayer() != NULL));
1960 Environment::addPlayer(player);
1963 LocalPlayer * ClientEnvironment::getLocalPlayer()
1965 for(std::list<Player*>::iterator i = m_players.begin();
1966 i != m_players.end(); ++i)
1968 Player *player = *i;
1969 if(player->isLocal())
1970 return (LocalPlayer*)player;
1975 void ClientEnvironment::step(float dtime)
1977 DSTACK(__FUNCTION_NAME);
1979 /* Step time of day */
1980 stepTimeOfDay(dtime);
1982 // Get some settings
1983 bool fly_allowed = m_gamedef->checkLocalPrivilege("fly");
1984 bool free_move = fly_allowed && g_settings->getBool("free_move");
1987 LocalPlayer *lplayer = getLocalPlayer();
1989 // collision info queue
1990 std::list<CollisionInfo> player_collisions;
1993 Get the speed the player is going
1995 bool is_climbing = lplayer->is_climbing;
1997 f32 player_speed = lplayer->getSpeed().getLength();
2000 Maximum position increment
2002 //f32 position_max_increment = 0.05*BS;
2003 f32 position_max_increment = 0.1*BS;
2005 // Maximum time increment (for collision detection etc)
2006 // time = distance / speed
2007 f32 dtime_max_increment = 1;
2008 if(player_speed > 0.001)
2009 dtime_max_increment = position_max_increment / player_speed;
2011 // Maximum time increment is 10ms or lower
2012 if(dtime_max_increment > 0.01)
2013 dtime_max_increment = 0.01;
2015 // Don't allow overly huge dtime
2019 f32 dtime_downcount = dtime;
2022 Stuff that has a maximum time increment
2031 if(dtime_downcount > dtime_max_increment)
2033 dtime_part = dtime_max_increment;
2034 dtime_downcount -= dtime_part;
2038 dtime_part = dtime_downcount;
2040 Setting this to 0 (no -=dtime_part) disables an infinite loop
2041 when dtime_part is so small that dtime_downcount -= dtime_part
2044 dtime_downcount = 0;
2052 v3f lplayerpos = lplayer->getPosition();
2055 if(free_move == false && is_climbing == false)
2058 v3f speed = lplayer->getSpeed();
2059 if(lplayer->in_liquid == false)
2060 speed.Y -= lplayer->movement_gravity * dtime_part * 2;
2062 // Liquid floating / sinking
2063 if(lplayer->in_liquid && !lplayer->swimming_vertical)
2064 speed.Y -= lplayer->movement_liquid_sink * dtime_part * 2;
2066 // Liquid resistance
2067 if(lplayer->in_liquid_stable || lplayer->in_liquid)
2069 // How much the node's viscosity blocks movement, ranges between 0 and 1
2070 // Should match the scale at which viscosity increase affects other liquid attributes
2071 const f32 viscosity_factor = 0.3;
2073 v3f d_wanted = -speed / lplayer->movement_liquid_fluidity;
2074 f32 dl = d_wanted.getLength();
2075 if(dl > lplayer->movement_liquid_fluidity_smooth)
2076 dl = lplayer->movement_liquid_fluidity_smooth;
2077 dl *= (lplayer->liquid_viscosity * viscosity_factor) + (1 - viscosity_factor);
2079 v3f d = d_wanted.normalize() * dl;
2083 if(speed.X > lplayer->movement_liquid_fluidity + lplayer->movement_liquid_fluidity_smooth) speed.X -= lplayer->movement_liquid_fluidity_smooth;
2084 if(speed.X < -lplayer->movement_liquid_fluidity - lplayer->movement_liquid_fluidity_smooth) speed.X += lplayer->movement_liquid_fluidity_smooth;
2085 if(speed.Y > lplayer->movement_liquid_fluidity + lplayer->movement_liquid_fluidity_smooth) speed.Y -= lplayer->movement_liquid_fluidity_smooth;
2086 if(speed.Y < -lplayer->movement_liquid_fluidity - lplayer->movement_liquid_fluidity_smooth) speed.Y += lplayer->movement_liquid_fluidity_smooth;
2087 if(speed.Z > lplayer->movement_liquid_fluidity + lplayer->movement_liquid_fluidity_smooth) speed.Z -= lplayer->movement_liquid_fluidity_smooth;
2088 if(speed.Z < -lplayer->movement_liquid_fluidity - lplayer->movement_liquid_fluidity_smooth) speed.Z += lplayer->movement_liquid_fluidity_smooth;
2092 lplayer->setSpeed(speed);
2097 This also does collision detection.
2099 lplayer->move(dtime_part, *m_map, position_max_increment,
2100 &player_collisions);
2103 while(dtime_downcount > 0.001);
2105 //std::cout<<"Looped "<<loopcount<<" times."<<std::endl;
2107 for(std::list<CollisionInfo>::iterator
2108 i = player_collisions.begin();
2109 i != player_collisions.end(); ++i)
2111 CollisionInfo &info = *i;
2112 v3f speed_diff = info.new_speed - info.old_speed;;
2113 // Handle only fall damage
2114 // (because otherwise walking against something in fast_move kills you)
2115 if(speed_diff.Y < 0 || info.old_speed.Y >= 0)
2117 // Get rid of other components
2120 f32 pre_factor = 1; // 1 hp per node/s
2121 f32 tolerance = BS*14; // 5 without damage
2122 f32 post_factor = 1; // 1 hp per node/s
2123 if(info.type == COLLISION_NODE)
2125 const ContentFeatures &f = m_gamedef->ndef()->
2126 get(m_map->getNodeNoEx(info.node_p));
2127 // Determine fall damage multiplier
2128 int addp = itemgroup_get(f.groups, "fall_damage_add_percent");
2129 pre_factor = 1.0 + (float)addp/100.0;
2131 float speed = pre_factor * speed_diff.getLength();
2132 if(speed > tolerance)
2134 f32 damage_f = (speed - tolerance)/BS * post_factor;
2135 u16 damage = (u16)(damage_f+0.5);
2137 damageLocalPlayer(damage, true);
2142 A quick draft of lava damage
2144 if(m_lava_hurt_interval.step(dtime, 1.0))
2146 v3f pf = lplayer->getPosition();
2148 // Feet, middle and head
2149 v3s16 p1 = floatToInt(pf + v3f(0, BS*0.1, 0), BS);
2150 MapNode n1 = m_map->getNodeNoEx(p1);
2151 v3s16 p2 = floatToInt(pf + v3f(0, BS*0.8, 0), BS);
2152 MapNode n2 = m_map->getNodeNoEx(p2);
2153 v3s16 p3 = floatToInt(pf + v3f(0, BS*1.6, 0), BS);
2154 MapNode n3 = m_map->getNodeNoEx(p2);
2156 u32 damage_per_second = 0;
2157 damage_per_second = MYMAX(damage_per_second,
2158 m_gamedef->ndef()->get(n1).damage_per_second);
2159 damage_per_second = MYMAX(damage_per_second,
2160 m_gamedef->ndef()->get(n2).damage_per_second);
2161 damage_per_second = MYMAX(damage_per_second,
2162 m_gamedef->ndef()->get(n3).damage_per_second);
2164 if(damage_per_second != 0)
2166 damageLocalPlayer(damage_per_second, true);
2171 Stuff that can be done in an arbitarily large dtime
2173 for(std::list<Player*>::iterator i = m_players.begin();
2174 i != m_players.end(); ++i)
2176 Player *player = *i;
2177 v3f playerpos = player->getPosition();
2180 Handle non-local players
2182 if(player->isLocal() == false)
2185 player->move(dtime, *m_map, 100*BS);
2189 // Update lighting on all players on client
2193 v3s16 p = player->getLightPosition();
2194 MapNode n = m_map->getNode(p);
2195 light = n.getLightBlendF1((float)getDayNightRatio()/1000, m_gamedef->ndef());
2197 catch(InvalidPositionException &e){
2198 light = blend_light_f1((float)getDayNightRatio()/1000, LIGHT_SUN, 0);
2200 player->light = light;
2204 Step active objects and update lighting of them
2207 bool update_lighting = m_active_object_light_update_interval.step(dtime, 0.21);
2208 for(std::map<u16, ClientActiveObject*>::iterator
2209 i = m_active_objects.begin();
2210 i != m_active_objects.end(); ++i)
2212 ClientActiveObject* obj = i->second;
2214 obj->step(dtime, this);
2222 v3s16 p = obj->getLightPosition();
2223 MapNode n = m_map->getNode(p);
2224 light = n.getLightBlend(getDayNightRatio(), m_gamedef->ndef());
2226 catch(InvalidPositionException &e){
2227 light = blend_light(getDayNightRatio(), LIGHT_SUN, 0);
2229 obj->updateLight(light);
2234 Step and handle simple objects
2236 for(std::list<ClientSimpleObject*>::iterator
2237 i = m_simple_objects.begin(); i != m_simple_objects.end();)
2239 ClientSimpleObject *simple = *i;
2240 std::list<ClientSimpleObject*>::iterator cur = i;
2242 simple->step(dtime);
2243 if(simple->m_to_be_removed){
2245 m_simple_objects.erase(cur);
2250 void ClientEnvironment::addSimpleObject(ClientSimpleObject *simple)
2252 m_simple_objects.push_back(simple);
2255 ClientActiveObject* ClientEnvironment::getActiveObject(u16 id)
2257 std::map<u16, ClientActiveObject*>::iterator n;
2258 n = m_active_objects.find(id);
2259 if(n == m_active_objects.end())
2264 bool isFreeClientActiveObjectId(u16 id,
2265 std::map<u16, ClientActiveObject*> &objects)
2270 return objects.find(id) == objects.end();
2273 u16 getFreeClientActiveObjectId(
2274 std::map<u16, ClientActiveObject*> &objects)
2279 if(isFreeClientActiveObjectId(new_id, objects))
2289 u16 ClientEnvironment::addActiveObject(ClientActiveObject *object)
2292 if(object->getId() == 0)
2294 u16 new_id = getFreeClientActiveObjectId(m_active_objects);
2297 infostream<<"ClientEnvironment::addActiveObject(): "
2298 <<"no free ids available"<<std::endl;
2302 object->setId(new_id);
2304 if(isFreeClientActiveObjectId(object->getId(), m_active_objects) == false)
2306 infostream<<"ClientEnvironment::addActiveObject(): "
2307 <<"id is not free ("<<object->getId()<<")"<<std::endl;
2311 infostream<<"ClientEnvironment::addActiveObject(): "
2312 <<"added (id="<<object->getId()<<")"<<std::endl;
2313 m_active_objects[object->getId()] = object;
2314 object->addToScene(m_smgr, m_texturesource, m_irr);
2315 { // Update lighting immediately
2319 v3s16 p = object->getLightPosition();
2320 MapNode n = m_map->getNode(p);
2321 light = n.getLightBlend(getDayNightRatio(), m_gamedef->ndef());
2323 catch(InvalidPositionException &e){
2324 light = blend_light(getDayNightRatio(), LIGHT_SUN, 0);
2326 object->updateLight(light);
2328 return object->getId();
2331 void ClientEnvironment::addActiveObject(u16 id, u8 type,
2332 const std::string &init_data)
2334 ClientActiveObject* obj =
2335 ClientActiveObject::create(type, m_gamedef, this);
2338 infostream<<"ClientEnvironment::addActiveObject(): "
2339 <<"id="<<id<<" type="<<type<<": Couldn't create object"
2348 obj->initialize(init_data);
2350 catch(SerializationError &e)
2352 errorstream<<"ClientEnvironment::addActiveObject():"
2353 <<" id="<<id<<" type="<<type
2354 <<": SerializationError in initialize(): "
2356 <<": init_data="<<serializeJsonString(init_data)
2360 addActiveObject(obj);
2363 void ClientEnvironment::removeActiveObject(u16 id)
2365 verbosestream<<"ClientEnvironment::removeActiveObject(): "
2366 <<"id="<<id<<std::endl;
2367 ClientActiveObject* obj = getActiveObject(id);
2370 infostream<<"ClientEnvironment::removeActiveObject(): "
2371 <<"id="<<id<<" not found"<<std::endl;
2374 obj->removeFromScene(true);
2376 m_active_objects.erase(id);
2379 void ClientEnvironment::processActiveObjectMessage(u16 id,
2380 const std::string &data)
2382 ClientActiveObject* obj = getActiveObject(id);
2385 infostream<<"ClientEnvironment::processActiveObjectMessage():"
2386 <<" got message for id="<<id<<", which doesn't exist."
2392 obj->processMessage(data);
2394 catch(SerializationError &e)
2396 errorstream<<"ClientEnvironment::processActiveObjectMessage():"
2397 <<" id="<<id<<" type="<<obj->getType()
2398 <<" SerializationError in processMessage(),"
2399 <<" message="<<serializeJsonString(data)
2405 Callbacks for activeobjects
2408 void ClientEnvironment::damageLocalPlayer(u8 damage, bool handle_hp)
2410 LocalPlayer *lplayer = getLocalPlayer();
2414 if(lplayer->hp > damage)
2415 lplayer->hp -= damage;
2420 ClientEnvEvent event;
2421 event.type = CEE_PLAYER_DAMAGE;
2422 event.player_damage.amount = damage;
2423 event.player_damage.send_to_server = handle_hp;
2424 m_client_event_queue.push_back(event);
2428 Client likes to call these
2431 void ClientEnvironment::getActiveObjects(v3f origin, f32 max_d,
2432 std::vector<DistanceSortedActiveObject> &dest)
2434 for(std::map<u16, ClientActiveObject*>::iterator
2435 i = m_active_objects.begin();
2436 i != m_active_objects.end(); ++i)
2438 ClientActiveObject* obj = i->second;
2440 f32 d = (obj->getPosition() - origin).getLength();
2445 DistanceSortedActiveObject dso(obj, d);
2447 dest.push_back(dso);
2451 ClientEnvEvent ClientEnvironment::getClientEvent()
2453 if(m_client_event_queue.empty())
2455 ClientEnvEvent event;
2456 event.type = CEE_NONE;
2459 return m_client_event_queue.pop_front();
2462 #endif // #ifndef SERVER