3 Copyright (C) 2010-2011 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(core::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(core::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(core::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(core::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 core::list<Player*> connected_players = getPlayers(true);
131 u32 chosen_one = myrand() % connected_players.size();
133 for(core::list<Player*>::Iterator
134 i = connected_players.begin();
135 i != connected_players.end(); i++)
147 Player * Environment::getNearestConnectedPlayer(v3f pos)
149 core::list<Player*> connected_players = getPlayers(true);
151 Player *nearest_player = NULL;
152 for(core::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 core::list<Player*> Environment::getPlayers()
172 core::list<Player*> Environment::getPlayers(bool ignore_disconnected)
174 core::list<Player*> newlist;
175 for(core::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(core::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, core::map<v3s16, bool> &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(core::list<v3s16> &active_positions,
268 core::map<v3s16, bool> &blocks_removed,
269 core::map<v3s16, bool> &blocks_added)
274 core::map<v3s16, bool> newlist;
275 for(core::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(core::map<v3s16, bool>::Iterator i = m_list.getIterator();
286 i.atEnd()==false; i++)
288 v3s16 p = i.getNode()->getKey();
289 // If not on new list, it's been removed
290 if(newlist.find(p) == NULL)
291 blocks_removed.insert(p, true);
295 Find out which blocks on the new list are not on the old list
297 // Go through new list
298 for(core::map<v3s16, bool>::Iterator i = newlist.getIterator();
299 i.atEnd()==false; i++)
301 v3s16 p = i.getNode()->getKey();
302 // If not on old list, it's been added
303 if(m_list.find(p) == NULL)
304 blocks_added.insert(p, true);
311 for(core::map<v3s16, bool>::Iterator i = newlist.getIterator();
312 i.atEnd()==false; i++)
314 v3s16 p = i.getNode()->getKey();
315 m_list.insert(p, true);
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(core::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 core::map<Player*, bool> 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, true);
426 for(core::list<Player*>::Iterator i = m_players.begin();
427 i != m_players.end(); i++)
430 if(saved_players.find(player) != NULL)
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, true);
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 core::map<Player*, bool> saved_players;
489 std::vector<fs::DirListNode> player_files = fs::GetDirListing(players_path);
490 for(u32 i=0; i<player_files.size(); i++)
492 if(player_files[i].dir)
495 // Full path to this file
496 std::string path = players_path + "/" + player_files[i].name;
498 //infostream<<"Checking player file "<<path<<std::endl;
500 // Load player to see what is its name
501 RemotePlayer testplayer(m_gamedef);
503 // Open file and deserialize
504 std::ifstream is(path.c_str(), std::ios_base::binary);
505 if(is.good() == false)
507 infostream<<"Failed to read "<<path<<std::endl;
510 testplayer.deSerialize(is);
513 if(!string_allowed(testplayer.getName(), PLAYERNAME_ALLOWED_CHARS))
515 infostream<<"Not loading player with invalid name: "
516 <<testplayer.getName()<<std::endl;
519 /*infostream<<"Loaded test player with name "<<testplayer.getName()
522 // Search for the player
523 std::string playername = testplayer.getName();
524 Player *player = getPlayer(playername.c_str());
525 bool newplayer = false;
528 //infostream<<"Is a new player"<<std::endl;
529 player = new RemotePlayer(m_gamedef);
535 verbosestream<<"Reading player "<<testplayer.getName()<<" from "
537 // Open file and deserialize
538 std::ifstream is(path.c_str(), std::ios_base::binary);
539 if(is.good() == false)
541 infostream<<"Failed to read "<<path<<std::endl;
544 player->deSerialize(is);
554 void ServerEnvironment::saveMeta(const std::string &savedir)
556 std::string path = savedir + "/env_meta.txt";
558 // Open file and serialize
559 std::ofstream os(path.c_str(), std::ios_base::binary);
560 if(os.good() == false)
562 infostream<<"ServerEnvironment::saveMeta(): Failed to open "
564 throw SerializationError("Couldn't save env meta");
568 args.setU64("game_time", m_game_time);
569 args.setU64("time_of_day", getTimeOfDay());
574 void ServerEnvironment::loadMeta(const std::string &savedir)
576 std::string path = savedir + "/env_meta.txt";
578 // Open file and deserialize
579 std::ifstream is(path.c_str(), std::ios_base::binary);
580 if(is.good() == false)
582 infostream<<"ServerEnvironment::loadMeta(): Failed to open "
584 throw SerializationError("Couldn't load env meta");
592 throw SerializationError
593 ("ServerEnvironment::loadMeta(): EnvArgsEnd not found");
595 std::getline(is, line);
596 std::string trimmedline = trim(line);
597 if(trimmedline == "EnvArgsEnd")
599 args.parseConfigLine(line);
603 m_game_time = args.getU64("game_time");
604 }catch(SettingNotFoundException &e){
605 // Getting this is crucial, otherwise timestamps are useless
606 throw SerializationError("Couldn't load env meta game_time");
610 m_time_of_day = args.getU64("time_of_day");
611 }catch(SettingNotFoundException &e){
612 // This is not as important
613 m_time_of_day = 9000;
619 ActiveBlockModifier *abm;
621 std::set<content_t> required_neighbors;
627 ServerEnvironment *m_env;
628 std::map<content_t, std::list<ActiveABM> > m_aabms;
630 ABMHandler(core::list<ABMWithState> &abms,
631 float dtime_s, ServerEnvironment *env,
637 INodeDefManager *ndef = env->getGameDef()->ndef();
638 for(core::list<ABMWithState>::Iterator
639 i = abms.begin(); i != abms.end(); i++){
640 ActiveBlockModifier *abm = i->abm;
641 float trigger_interval = abm->getTriggerInterval();
642 if(trigger_interval < 0.001)
643 trigger_interval = 0.001;
644 float actual_interval = dtime_s;
647 if(i->timer < trigger_interval)
649 i->timer -= trigger_interval;
650 actual_interval = trigger_interval;
652 float intervals = actual_interval / trigger_interval;
655 float chance = abm->getTriggerChance();
660 aabm.chance = chance / intervals;
664 std::set<std::string> required_neighbors_s
665 = abm->getRequiredNeighbors();
666 for(std::set<std::string>::iterator
667 i = required_neighbors_s.begin();
668 i != required_neighbors_s.end(); i++)
670 ndef->getIds(*i, aabm.required_neighbors);
673 std::set<std::string> contents_s = abm->getTriggerContents();
674 for(std::set<std::string>::iterator
675 i = contents_s.begin(); i != contents_s.end(); i++)
677 std::set<content_t> ids;
678 ndef->getIds(*i, ids);
679 for(std::set<content_t>::const_iterator k = ids.begin();
683 std::map<content_t, std::list<ActiveABM> >::iterator j;
685 if(j == m_aabms.end()){
686 std::list<ActiveABM> aabmlist;
687 m_aabms[c] = aabmlist;
690 j->second.push_back(aabm);
695 void apply(MapBlock *block)
700 ServerMap *map = &m_env->getServerMap();
703 for(p0.X=0; p0.X<MAP_BLOCKSIZE; p0.X++)
704 for(p0.Y=0; p0.Y<MAP_BLOCKSIZE; p0.Y++)
705 for(p0.Z=0; p0.Z<MAP_BLOCKSIZE; p0.Z++)
707 MapNode n = block->getNodeNoEx(p0);
708 content_t c = n.getContent();
709 v3s16 p = p0 + block->getPosRelative();
711 std::map<content_t, std::list<ActiveABM> >::iterator j;
713 if(j == m_aabms.end())
716 for(std::list<ActiveABM>::iterator
717 i = j->second.begin(); i != j->second.end(); i++)
719 if(myrand() % i->chance != 0)
723 if(!i->required_neighbors.empty())
726 for(p1.X = p.X-1; p1.X <= p.X+1; p1.X++)
727 for(p1.Y = p.Y-1; p1.Y <= p.Y+1; p1.Y++)
728 for(p1.Z = p.Z-1; p1.Z <= p.Z+1; p1.Z++)
732 MapNode n = map->getNodeNoEx(p1);
733 content_t c = n.getContent();
734 std::set<content_t>::const_iterator k;
735 k = i->required_neighbors.find(c);
736 if(k != i->required_neighbors.end()){
740 // No required neighbor found
745 // Find out how many objects the block contains
746 u32 active_object_count = block->m_static_objects.m_active.size();
747 // Find out how many objects this and all the neighbors contain
748 u32 active_object_count_wider = 0;
749 u32 wider_unknown_count = 0;
750 for(s16 x=-1; x<=1; x++)
751 for(s16 y=-1; y<=1; y++)
752 for(s16 z=-1; z<=1; z++)
754 MapBlock *block2 = map->getBlockNoCreateNoEx(
755 block->getPos() + v3s16(x,y,z));
757 wider_unknown_count = 0;
760 active_object_count_wider +=
761 block2->m_static_objects.m_active.size()
762 + block2->m_static_objects.m_stored.size();
765 u32 wider_known_count = 3*3*3 - wider_unknown_count;
766 active_object_count_wider += wider_unknown_count * active_object_count_wider / wider_known_count;
768 // Call all the trigger variations
769 i->abm->trigger(m_env, p, n);
770 i->abm->trigger(m_env, p, n,
771 active_object_count, active_object_count_wider);
777 void ServerEnvironment::activateBlock(MapBlock *block, u32 additional_dtime)
779 // Get time difference
781 u32 stamp = block->getTimestamp();
782 if(m_game_time > stamp && stamp != BLOCK_TIMESTAMP_UNDEFINED)
783 dtime_s = m_game_time - block->getTimestamp();
784 dtime_s += additional_dtime;
786 /*infostream<<"ServerEnvironment::activateBlock(): block timestamp: "
787 <<stamp<<", game time: "<<m_game_time<<std::endl;*/
789 // Set current time as timestamp
790 block->setTimestampNoChangedFlag(m_game_time);
792 /*infostream<<"ServerEnvironment::activateBlock(): block is "
793 <<dtime_s<<" seconds old."<<std::endl;*/
795 // Activate stored objects
796 activateObjects(block, dtime_s);
799 std::map<v3s16, NodeTimer> elapsed_timers =
800 block->m_node_timers.step((float)dtime_s);
801 if(!elapsed_timers.empty()){
803 for(std::map<v3s16, NodeTimer>::iterator
804 i = elapsed_timers.begin();
805 i != elapsed_timers.end(); i++){
806 n = block->getNodeNoEx(i->first);
807 if(scriptapi_node_on_timer(m_lua,i->first,n,i->second.elapsed))
808 block->setNodeTimer(i->first,NodeTimer(i->second.timeout,0));
812 /* Handle ActiveBlockModifiers */
813 ABMHandler abmhandler(m_abms, dtime_s, this, false);
814 abmhandler.apply(block);
817 void ServerEnvironment::addActiveBlockModifier(ActiveBlockModifier *abm)
819 m_abms.push_back(ABMWithState(abm));
822 std::set<u16> ServerEnvironment::getObjectsInsideRadius(v3f pos, float radius)
824 std::set<u16> objects;
825 for(core::map<u16, ServerActiveObject*>::Iterator
826 i = m_active_objects.getIterator();
827 i.atEnd()==false; i++)
829 ServerActiveObject* obj = i.getNode()->getValue();
830 u16 id = i.getNode()->getKey();
831 v3f objectpos = obj->getBasePosition();
832 if(objectpos.getDistanceFrom(pos) > radius)
839 void ServerEnvironment::clearAllObjects()
841 infostream<<"ServerEnvironment::clearAllObjects(): "
842 <<"Removing all active objects"<<std::endl;
843 core::list<u16> objects_to_remove;
844 for(core::map<u16, ServerActiveObject*>::Iterator
845 i = m_active_objects.getIterator();
846 i.atEnd()==false; i++)
848 ServerActiveObject* obj = i.getNode()->getValue();
849 if(obj->getType() == ACTIVEOBJECT_TYPE_PLAYER)
851 u16 id = i.getNode()->getKey();
852 v3f objectpos = obj->getBasePosition();
853 // Delete static object if block is loaded
854 if(obj->m_static_exists){
855 MapBlock *block = m_map->getBlockNoCreateNoEx(obj->m_static_block);
857 block->m_static_objects.remove(id);
858 block->raiseModified(MOD_STATE_WRITE_NEEDED,
860 obj->m_static_exists = false;
863 // If known by some client, don't delete immediately
864 if(obj->m_known_by_count > 0){
865 obj->m_pending_deactivation = true;
866 obj->m_removed = true;
870 // Tell the object about removal
871 obj->removingFromEnvironment();
872 // Deregister in scripting api
873 scriptapi_rm_object_reference(m_lua, obj);
875 // Delete active object
876 if(obj->environmentDeletes())
878 // Id to be removed from m_active_objects
879 objects_to_remove.push_back(id);
881 // Remove references from m_active_objects
882 for(core::list<u16>::Iterator i = objects_to_remove.begin();
883 i != objects_to_remove.end(); i++)
885 m_active_objects.remove(*i);
888 core::list<v3s16> loadable_blocks;
889 infostream<<"ServerEnvironment::clearAllObjects(): "
890 <<"Listing all loadable blocks"<<std::endl;
891 m_map->listAllLoadableBlocks(loadable_blocks);
892 infostream<<"ServerEnvironment::clearAllObjects(): "
893 <<"Done listing all loadable blocks: "
894 <<loadable_blocks.size()
895 <<", now clearing"<<std::endl;
896 u32 report_interval = loadable_blocks.size() / 10;
897 u32 num_blocks_checked = 0;
898 u32 num_blocks_cleared = 0;
899 u32 num_objs_cleared = 0;
900 for(core::list<v3s16>::Iterator i = loadable_blocks.begin();
901 i != loadable_blocks.end(); i++)
904 MapBlock *block = m_map->emergeBlock(p, false);
906 errorstream<<"ServerEnvironment::clearAllObjects(): "
907 <<"Failed to emerge block "<<PP(p)<<std::endl;
910 u32 num_stored = block->m_static_objects.m_stored.size();
911 u32 num_active = block->m_static_objects.m_active.size();
912 if(num_stored != 0 || num_active != 0){
913 block->m_static_objects.m_stored.clear();
914 block->m_static_objects.m_active.clear();
915 block->raiseModified(MOD_STATE_WRITE_NEEDED,
917 num_objs_cleared += num_stored + num_active;
918 num_blocks_cleared++;
920 num_blocks_checked++;
922 if(num_blocks_checked % report_interval == 0){
923 float percent = 100.0 * (float)num_blocks_checked /
924 loadable_blocks.size();
925 infostream<<"ServerEnvironment::clearAllObjects(): "
926 <<"Cleared "<<num_objs_cleared<<" objects"
927 <<" in "<<num_blocks_cleared<<" blocks ("
928 <<percent<<"%)"<<std::endl;
931 infostream<<"ServerEnvironment::clearAllObjects(): "
932 <<"Finished: Cleared "<<num_objs_cleared<<" objects"
933 <<" in "<<num_blocks_cleared<<" blocks"<<std::endl;
936 void ServerEnvironment::step(float dtime)
938 DSTACK(__FUNCTION_NAME);
940 //TimeTaker timer("ServerEnv step");
942 /* Step time of day */
943 stepTimeOfDay(dtime);
946 // NOTE: This is kind of funny on a singleplayer game, but doesn't
947 // really matter that much.
948 m_recommended_send_interval = g_settings->getFloat("dedicated_server_step");
954 m_game_time_fraction_counter += dtime;
955 u32 inc_i = (u32)m_game_time_fraction_counter;
956 m_game_time += inc_i;
957 m_game_time_fraction_counter -= (float)inc_i;
964 ScopeProfiler sp(g_profiler, "SEnv: handle players avg", SPT_AVG);
965 for(core::list<Player*>::Iterator i = m_players.begin();
966 i != m_players.end(); i++)
970 // Ignore disconnected players
971 if(player->peer_id == 0)
974 v3f playerpos = player->getPosition();
977 player->move(dtime, *m_map, 100*BS);
982 Manage active block list
984 if(m_active_blocks_management_interval.step(dtime, 2.0))
986 ScopeProfiler sp(g_profiler, "SEnv: manage act. block list avg /2s", SPT_AVG);
988 Get player block positions
990 core::list<v3s16> players_blockpos;
991 for(core::list<Player*>::Iterator
992 i = m_players.begin();
993 i != m_players.end(); i++)
996 // Ignore disconnected players
997 if(player->peer_id == 0)
999 v3s16 blockpos = getNodeBlockPos(
1000 floatToInt(player->getPosition(), BS));
1001 players_blockpos.push_back(blockpos);
1005 Update list of active blocks, collecting changes
1007 const s16 active_block_range = g_settings->getS16("active_block_range");
1008 core::map<v3s16, bool> blocks_removed;
1009 core::map<v3s16, bool> blocks_added;
1010 m_active_blocks.update(players_blockpos, active_block_range,
1011 blocks_removed, blocks_added);
1014 Handle removed blocks
1017 // Convert active objects that are no more in active blocks to static
1018 deactivateFarObjects(false);
1020 for(core::map<v3s16, bool>::Iterator
1021 i = blocks_removed.getIterator();
1022 i.atEnd()==false; i++)
1024 v3s16 p = i.getNode()->getKey();
1026 /*infostream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
1027 <<") became inactive"<<std::endl;*/
1029 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1033 // Set current time as timestamp (and let it set ChangedFlag)
1034 block->setTimestamp(m_game_time);
1041 for(core::map<v3s16, bool>::Iterator
1042 i = blocks_added.getIterator();
1043 i.atEnd()==false; i++)
1045 v3s16 p = i.getNode()->getKey();
1047 /*infostream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
1048 <<") became active"<<std::endl;*/
1050 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1052 // Block needs to be fetched first
1053 m_emerger->queueBlockEmerge(p, false);
1054 m_active_blocks.m_list.remove(p);
1058 activateBlock(block);
1063 Mess around in active blocks
1065 if(m_active_blocks_nodemetadata_interval.step(dtime, 1.0))
1067 ScopeProfiler sp(g_profiler, "SEnv: mess in act. blocks avg /1s", SPT_AVG);
1071 for(core::map<v3s16, bool>::Iterator
1072 i = m_active_blocks.m_list.getIterator();
1073 i.atEnd()==false; i++)
1075 v3s16 p = i.getNode()->getKey();
1077 /*infostream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
1078 <<") being handled"<<std::endl;*/
1080 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1084 // Reset block usage timer
1085 block->resetUsageTimer();
1087 // Set current time as timestamp
1088 block->setTimestampNoChangedFlag(m_game_time);
1089 // If time has changed much from the one on disk,
1090 // set block to be saved when it is unloaded
1091 if(block->getTimestamp() > block->getDiskTimestamp() + 60)
1092 block->raiseModified(MOD_STATE_WRITE_AT_UNLOAD,
1093 "Timestamp older than 60s (step)");
1096 std::map<v3s16, NodeTimer> elapsed_timers =
1097 block->m_node_timers.step((float)dtime);
1098 if(!elapsed_timers.empty()){
1100 for(std::map<v3s16, NodeTimer>::iterator
1101 i = elapsed_timers.begin();
1102 i != elapsed_timers.end(); i++){
1103 n = block->getNodeNoEx(i->first);
1104 p = i->first + block->getPosRelative();
1105 if(scriptapi_node_on_timer(m_lua,p,n,i->second.elapsed))
1106 block->setNodeTimer(i->first,NodeTimer(i->second.timeout,0));
1112 const float abm_interval = 1.0;
1113 if(m_active_block_modifier_interval.step(dtime, abm_interval))
1115 if(m_active_block_interval_overload_skip > 0){
1116 ScopeProfiler sp(g_profiler, "SEnv: ABM overload skips");
1117 m_active_block_interval_overload_skip--;
1120 ScopeProfiler sp(g_profiler, "SEnv: modify in blocks avg /1s", SPT_AVG);
1121 TimeTaker timer("modify in active blocks");
1123 // Initialize handling of ActiveBlockModifiers
1124 ABMHandler abmhandler(m_abms, abm_interval, this, true);
1126 for(core::map<v3s16, bool>::Iterator
1127 i = m_active_blocks.m_list.getIterator();
1128 i.atEnd()==false; i++)
1130 v3s16 p = i.getNode()->getKey();
1132 /*infostream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
1133 <<") being handled"<<std::endl;*/
1135 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1139 // Set current time as timestamp
1140 block->setTimestampNoChangedFlag(m_game_time);
1142 /* Handle ActiveBlockModifiers */
1143 abmhandler.apply(block);
1146 u32 time_ms = timer.stop(true);
1147 u32 max_time_ms = 200;
1148 if(time_ms > max_time_ms){
1149 infostream<<"WARNING: active block modifiers took "
1150 <<time_ms<<"ms (longer than "
1151 <<max_time_ms<<"ms)"<<std::endl;
1152 m_active_block_interval_overload_skip = (time_ms / max_time_ms) + 1;
1157 Step script environment (run global on_step())
1159 scriptapi_environment_step(m_lua, dtime);
1165 ScopeProfiler sp(g_profiler, "SEnv: step act. objs avg", SPT_AVG);
1166 //TimeTaker timer("Step active objects");
1168 g_profiler->avg("SEnv: num of objects", m_active_objects.size());
1170 // This helps the objects to send data at the same time
1171 bool send_recommended = false;
1172 m_send_recommended_timer += dtime;
1173 if(m_send_recommended_timer > getSendRecommendedInterval())
1175 m_send_recommended_timer -= getSendRecommendedInterval();
1176 send_recommended = true;
1179 for(core::map<u16, ServerActiveObject*>::Iterator
1180 i = m_active_objects.getIterator();
1181 i.atEnd()==false; i++)
1183 ServerActiveObject* obj = i.getNode()->getValue();
1184 // Remove non-peaceful mobs on peaceful mode
1185 if(g_settings->getBool("only_peaceful_mobs")){
1186 if(!obj->isPeaceful())
1187 obj->m_removed = true;
1189 // Don't step if is to be removed or stored statically
1190 if(obj->m_removed || obj->m_pending_deactivation)
1193 obj->step(dtime, send_recommended);
1194 // Read messages from object
1195 while(obj->m_messages_out.size() > 0)
1197 m_active_object_messages.push_back(
1198 obj->m_messages_out.pop_front());
1204 Manage active objects
1206 if(m_object_management_interval.step(dtime, 0.5))
1208 ScopeProfiler sp(g_profiler, "SEnv: remove removed objs avg /.5s", SPT_AVG);
1210 Remove objects that satisfy (m_removed && m_known_by_count==0)
1212 removeRemovedObjects();
1216 ServerActiveObject* ServerEnvironment::getActiveObject(u16 id)
1218 core::map<u16, ServerActiveObject*>::Node *n;
1219 n = m_active_objects.find(id);
1222 return n->getValue();
1225 bool isFreeServerActiveObjectId(u16 id,
1226 core::map<u16, ServerActiveObject*> &objects)
1231 for(core::map<u16, ServerActiveObject*>::Iterator
1232 i = objects.getIterator();
1233 i.atEnd()==false; i++)
1235 if(i.getNode()->getKey() == id)
1241 u16 getFreeServerActiveObjectId(
1242 core::map<u16, ServerActiveObject*> &objects)
1247 if(isFreeServerActiveObjectId(new_id, objects))
1257 u16 ServerEnvironment::addActiveObject(ServerActiveObject *object)
1260 u16 id = addActiveObjectRaw(object, true, 0);
1264 bool ServerEnvironment::addActiveObjectAsStatic(ServerActiveObject *obj)
1268 v3f objectpos = obj->getBasePosition();
1270 // The block in which the object resides in
1271 v3s16 blockpos_o = getNodeBlockPos(floatToInt(objectpos, BS));
1274 Update the static data
1277 // Create new static object
1278 std::string staticdata = obj->getStaticData();
1279 StaticObject s_obj(obj->getType(), objectpos, staticdata);
1280 // Add to the block where the object is located in
1281 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1282 // Get or generate the block
1283 MapBlock *block = m_map->emergeBlock(blockpos);
1285 bool succeeded = false;
1289 block->m_static_objects.insert(0, s_obj);
1290 block->raiseModified(MOD_STATE_WRITE_AT_UNLOAD,
1291 "addActiveObjectAsStatic");
1295 infostream<<"ServerEnvironment::addActiveObjectAsStatic: "
1296 <<"Could not find or generate "
1297 <<"a block for storing static object"<<std::endl;
1301 if(obj->environmentDeletes())
1308 Finds out what new objects have been added to
1309 inside a radius around a position
1311 void ServerEnvironment::getAddedActiveObjects(v3s16 pos, s16 radius,
1312 core::map<u16, bool> ¤t_objects,
1313 core::map<u16, bool> &added_objects)
1315 v3f pos_f = intToFloat(pos, BS);
1316 f32 radius_f = radius * BS;
1318 Go through the object list,
1319 - discard m_removed objects,
1320 - discard objects that are too far away,
1321 - discard objects that are found in current_objects.
1322 - add remaining objects to added_objects
1324 for(core::map<u16, ServerActiveObject*>::Iterator
1325 i = m_active_objects.getIterator();
1326 i.atEnd()==false; i++)
1328 u16 id = i.getNode()->getKey();
1330 ServerActiveObject *object = i.getNode()->getValue();
1333 // Discard if removed
1334 if(object->m_removed)
1336 if(object->unlimitedTransferDistance() == false){
1337 // Discard if too far
1338 f32 distance_f = object->getBasePosition().getDistanceFrom(pos_f);
1339 if(distance_f > radius_f)
1342 // Discard if already on current_objects
1343 core::map<u16, bool>::Node *n;
1344 n = current_objects.find(id);
1347 // Add to added_objects
1348 added_objects.insert(id, false);
1353 Finds out what objects have been removed from
1354 inside a radius around a position
1356 void ServerEnvironment::getRemovedActiveObjects(v3s16 pos, s16 radius,
1357 core::map<u16, bool> ¤t_objects,
1358 core::map<u16, bool> &removed_objects)
1360 v3f pos_f = intToFloat(pos, BS);
1361 f32 radius_f = radius * BS;
1363 Go through current_objects; object is removed if:
1364 - object is not found in m_active_objects (this is actually an
1365 error condition; objects should be set m_removed=true and removed
1366 only after all clients have been informed about removal), or
1367 - object has m_removed=true, or
1368 - object is too far away
1370 for(core::map<u16, bool>::Iterator
1371 i = current_objects.getIterator();
1372 i.atEnd()==false; i++)
1374 u16 id = i.getNode()->getKey();
1375 ServerActiveObject *object = getActiveObject(id);
1378 infostream<<"ServerEnvironment::getRemovedActiveObjects():"
1379 <<" object in current_objects is NULL"<<std::endl;
1380 removed_objects.insert(id, false);
1384 if(object->m_removed)
1386 removed_objects.insert(id, false);
1390 // If transfer distance is unlimited, don't remove
1391 if(object->unlimitedTransferDistance())
1394 f32 distance_f = object->getBasePosition().getDistanceFrom(pos_f);
1396 if(distance_f >= radius_f)
1398 removed_objects.insert(id, false);
1406 ActiveObjectMessage ServerEnvironment::getActiveObjectMessage()
1408 if(m_active_object_messages.size() == 0)
1409 return ActiveObjectMessage(0);
1411 return m_active_object_messages.pop_front();
1415 ************ Private methods *************
1418 u16 ServerEnvironment::addActiveObjectRaw(ServerActiveObject *object,
1419 bool set_changed, u32 dtime_s)
1422 if(object->getId() == 0){
1423 u16 new_id = getFreeServerActiveObjectId(m_active_objects);
1426 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1427 <<"no free ids available"<<std::endl;
1428 if(object->environmentDeletes())
1432 object->setId(new_id);
1435 verbosestream<<"ServerEnvironment::addActiveObjectRaw(): "
1436 <<"supplied with id "<<object->getId()<<std::endl;
1438 if(isFreeServerActiveObjectId(object->getId(), m_active_objects) == false)
1440 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1441 <<"id is not free ("<<object->getId()<<")"<<std::endl;
1442 if(object->environmentDeletes())
1446 /*infostream<<"ServerEnvironment::addActiveObjectRaw(): "
1447 <<"added (id="<<object->getId()<<")"<<std::endl;*/
1449 m_active_objects.insert(object->getId(), object);
1451 verbosestream<<"ServerEnvironment::addActiveObjectRaw(): "
1452 <<"Added id="<<object->getId()<<"; there are now "
1453 <<m_active_objects.size()<<" active objects."
1456 // Register reference in scripting api (must be done before post-init)
1457 scriptapi_add_object_reference(m_lua, object);
1458 // Post-initialize object
1459 object->addedToEnvironment(dtime_s);
1461 // Add static data to block
1462 if(object->isStaticAllowed())
1464 // Add static object to active static list of the block
1465 v3f objectpos = object->getBasePosition();
1466 std::string staticdata = object->getStaticData();
1467 StaticObject s_obj(object->getType(), objectpos, staticdata);
1468 // Add to the block where the object is located in
1469 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1470 MapBlock *block = m_map->getBlockNoCreateNoEx(blockpos);
1473 block->m_static_objects.m_active.insert(object->getId(), s_obj);
1474 object->m_static_exists = true;
1475 object->m_static_block = blockpos;
1478 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1479 "addActiveObjectRaw");
1482 v3s16 p = floatToInt(objectpos, BS);
1483 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1484 <<"could not find block for storing id="<<object->getId()
1485 <<" statically (pos="<<PP(p)<<")"<<std::endl;
1489 return object->getId();
1493 Remove objects that satisfy (m_removed && m_known_by_count==0)
1495 void ServerEnvironment::removeRemovedObjects()
1497 core::list<u16> objects_to_remove;
1498 for(core::map<u16, ServerActiveObject*>::Iterator
1499 i = m_active_objects.getIterator();
1500 i.atEnd()==false; i++)
1502 u16 id = i.getNode()->getKey();
1503 ServerActiveObject* obj = i.getNode()->getValue();
1504 // This shouldn't happen but check it
1507 infostream<<"NULL object found in ServerEnvironment"
1508 <<" while finding removed objects. id="<<id<<std::endl;
1509 // Id to be removed from m_active_objects
1510 objects_to_remove.push_back(id);
1515 We will delete objects that are marked as removed or thatare
1516 waiting for deletion after deactivation
1518 if(obj->m_removed == false && obj->m_pending_deactivation == false)
1522 Delete static data from block if is marked as removed
1524 if(obj->m_static_exists && obj->m_removed)
1526 MapBlock *block = m_map->emergeBlock(obj->m_static_block);
1529 block->m_static_objects.remove(id);
1530 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1531 "removeRemovedObjects");
1532 obj->m_static_exists = false;
1536 // If m_known_by_count > 0, don't actually remove.
1537 if(obj->m_known_by_count > 0)
1540 // Tell the object about removal
1541 obj->removingFromEnvironment();
1542 // Deregister in scripting api
1543 scriptapi_rm_object_reference(m_lua, obj);
1546 if(obj->environmentDeletes())
1548 // Id to be removed from m_active_objects
1549 objects_to_remove.push_back(id);
1551 // Remove references from m_active_objects
1552 for(core::list<u16>::Iterator i = objects_to_remove.begin();
1553 i != objects_to_remove.end(); i++)
1555 m_active_objects.remove(*i);
1559 static void print_hexdump(std::ostream &o, const std::string &data)
1561 const int linelength = 16;
1562 for(int l=0; ; l++){
1563 int i0 = linelength * l;
1564 bool at_end = false;
1565 int thislinelength = linelength;
1566 if(i0 + thislinelength > (int)data.size()){
1567 thislinelength = data.size() - i0;
1570 for(int di=0; di<linelength; di++){
1573 if(di<thislinelength)
1574 snprintf(buf, 4, "%.2x ", data[i]);
1576 snprintf(buf, 4, " ");
1580 for(int di=0; di<thislinelength; di++){
1594 Convert stored objects from blocks near the players to active.
1596 void ServerEnvironment::activateObjects(MapBlock *block, u32 dtime_s)
1600 // Ignore if no stored objects (to not set changed flag)
1601 if(block->m_static_objects.m_stored.size() == 0)
1603 verbosestream<<"ServerEnvironment::activateObjects(): "
1604 <<"activating objects of block "<<PP(block->getPos())
1605 <<" ("<<block->m_static_objects.m_stored.size()
1606 <<" objects)"<<std::endl;
1607 bool large_amount = (block->m_static_objects.m_stored.size() > 49);
1609 errorstream<<"suspiciously large amount of objects detected: "
1610 <<block->m_static_objects.m_stored.size()<<" in "
1611 <<PP(block->getPos())
1612 <<"; removing all of them."<<std::endl;
1613 // Clear stored list
1614 block->m_static_objects.m_stored.clear();
1615 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1616 "stored list cleared in activateObjects due to "
1617 "large amount of objects");
1620 // A list for objects that couldn't be converted to active for some
1621 // reason. They will be stored back.
1622 core::list<StaticObject> new_stored;
1623 // Loop through stored static objects
1624 for(core::list<StaticObject>::Iterator
1625 i = block->m_static_objects.m_stored.begin();
1626 i != block->m_static_objects.m_stored.end(); i++)
1628 /*infostream<<"Server: Creating an active object from "
1629 <<"static data"<<std::endl;*/
1630 StaticObject &s_obj = *i;
1631 // Create an active object from the data
1632 ServerActiveObject *obj = ServerActiveObject::create
1633 (s_obj.type, this, 0, s_obj.pos, s_obj.data);
1634 // If couldn't create object, store static data back.
1637 errorstream<<"ServerEnvironment::activateObjects(): "
1638 <<"failed to create active object from static object "
1639 <<"in block "<<PP(s_obj.pos/BS)
1640 <<" type="<<(int)s_obj.type<<" data:"<<std::endl;
1641 print_hexdump(verbosestream, s_obj.data);
1643 new_stored.push_back(s_obj);
1646 verbosestream<<"ServerEnvironment::activateObjects(): "
1647 <<"activated static object pos="<<PP(s_obj.pos/BS)
1648 <<" type="<<(int)s_obj.type<<std::endl;
1649 // This will also add the object to the active static list
1650 addActiveObjectRaw(obj, false, dtime_s);
1652 // Clear stored list
1653 block->m_static_objects.m_stored.clear();
1654 // Add leftover failed stuff to stored list
1655 for(core::list<StaticObject>::Iterator
1656 i = new_stored.begin();
1657 i != new_stored.end(); i++)
1659 StaticObject &s_obj = *i;
1660 block->m_static_objects.m_stored.push_back(s_obj);
1663 Note: Block hasn't really been modified here.
1664 The objects have just been activated and moved from the stored
1665 static list to the active static list.
1666 As such, the block is essentially the same.
1667 Thus, do not call block->raiseModified(MOD_STATE_WRITE_NEEDED).
1668 Otherwise there would be a huge amount of unnecessary I/O.
1673 Convert objects that are not standing inside active blocks to static.
1675 If m_known_by_count != 0, active object is not deleted, but static
1676 data is still updated.
1678 If force_delete is set, active object is deleted nevertheless. It
1679 shall only be set so in the destructor of the environment.
1681 void ServerEnvironment::deactivateFarObjects(bool force_delete)
1683 core::list<u16> objects_to_remove;
1684 for(core::map<u16, ServerActiveObject*>::Iterator
1685 i = m_active_objects.getIterator();
1686 i.atEnd()==false; i++)
1688 ServerActiveObject* obj = i.getNode()->getValue();
1691 // Do not deactivate if static data creation not allowed
1692 if(!force_delete && !obj->isStaticAllowed())
1695 // If pending deactivation, let removeRemovedObjects() do it
1696 if(!force_delete && obj->m_pending_deactivation)
1699 u16 id = i.getNode()->getKey();
1700 v3f objectpos = obj->getBasePosition();
1702 // The block in which the object resides in
1703 v3s16 blockpos_o = getNodeBlockPos(floatToInt(objectpos, BS));
1705 // If block is active, don't remove
1706 if(!force_delete && m_active_blocks.contains(blockpos_o))
1709 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
1710 <<"deactivating object id="<<id<<" on inactive block "
1711 <<PP(blockpos_o)<<std::endl;
1713 // If known by some client, don't immediately delete.
1714 bool pending_delete = (obj->m_known_by_count > 0 && !force_delete);
1717 Update the static data
1720 if(obj->isStaticAllowed())
1722 // Create new static object
1723 std::string staticdata_new = obj->getStaticData();
1724 StaticObject s_obj(obj->getType(), objectpos, staticdata_new);
1726 bool stays_in_same_block = false;
1727 bool data_changed = true;
1729 if(obj->m_static_exists){
1730 if(obj->m_static_block == blockpos_o)
1731 stays_in_same_block = true;
1733 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
1735 core::map<u16, StaticObject>::Node *n =
1736 block->m_static_objects.m_active.find(id);
1738 StaticObject static_old = n->getValue();
1740 float save_movem = obj->getMinimumSavedMovement();
1742 if(static_old.data == staticdata_new &&
1743 (static_old.pos - objectpos).getLength() < save_movem)
1744 data_changed = false;
1746 errorstream<<"ServerEnvironment::deactivateFarObjects(): "
1747 <<"id="<<id<<" m_static_exists=true but "
1748 <<"static data doesn't actually exist in "
1749 <<PP(obj->m_static_block)<<std::endl;
1753 bool shall_be_written = (!stays_in_same_block || data_changed);
1755 // Delete old static object
1756 if(obj->m_static_exists)
1758 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
1761 block->m_static_objects.remove(id);
1762 obj->m_static_exists = false;
1763 // Only mark block as modified if data changed considerably
1764 if(shall_be_written)
1765 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1766 "deactivateFarObjects: Static data "
1767 "changed considerably");
1771 // Add to the block where the object is located in
1772 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1773 // Get or generate the block
1774 MapBlock *block = NULL;
1776 block = m_map->emergeBlock(blockpos);
1777 } catch(InvalidPositionException &e){
1778 // Handled via NULL pointer
1783 if(block->m_static_objects.m_stored.size() >= 49){
1784 errorstream<<"ServerEnv: Trying to store id="<<obj->getId()
1785 <<" statically but block "<<PP(blockpos)
1786 <<" already contains "
1787 <<block->m_static_objects.m_stored.size()
1788 <<" (over 49) objects."
1789 <<" Forcing delete."<<std::endl;
1790 force_delete = true;
1792 u16 new_id = pending_delete ? id : 0;
1793 // If static counterpart already exists, remove it first.
1794 // This shouldn't happen, but happens rarely for some
1795 // unknown reason. Unsuccessful attempts have been made to
1796 // find said reason.
1797 if(new_id && block->m_static_objects.m_active.find(new_id)){
1798 infostream<<"ServerEnv: WARNING: Performing hack #83274"
1800 block->m_static_objects.remove(new_id);
1802 block->m_static_objects.insert(new_id, s_obj);
1804 // Only mark block as modified if data changed considerably
1805 if(shall_be_written)
1806 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1807 "deactivateFarObjects: Static data "
1808 "changed considerably");
1810 obj->m_static_exists = true;
1811 obj->m_static_block = block->getPos();
1816 v3s16 p = floatToInt(objectpos, BS);
1817 errorstream<<"ServerEnv: Could not find or generate "
1818 <<"a block for storing id="<<obj->getId()
1819 <<" statically (pos="<<PP(p)<<")"<<std::endl;
1826 If known by some client, set pending deactivation.
1827 Otherwise delete it immediately.
1830 if(pending_delete && !force_delete)
1832 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
1833 <<"object id="<<id<<" is known by clients"
1834 <<"; not deleting yet"<<std::endl;
1836 obj->m_pending_deactivation = true;
1840 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
1841 <<"object id="<<id<<" is not known by clients"
1842 <<"; deleting"<<std::endl;
1844 // Tell the object about removal
1845 obj->removingFromEnvironment();
1846 // Deregister in scripting api
1847 scriptapi_rm_object_reference(m_lua, obj);
1849 // Delete active object
1850 if(obj->environmentDeletes())
1852 // Id to be removed from m_active_objects
1853 objects_to_remove.push_back(id);
1856 // Remove references from m_active_objects
1857 for(core::list<u16>::Iterator i = objects_to_remove.begin();
1858 i != objects_to_remove.end(); i++)
1860 m_active_objects.remove(*i);
1867 #include "clientsimpleobject.h"
1873 ClientEnvironment::ClientEnvironment(ClientMap *map, scene::ISceneManager *smgr,
1874 ITextureSource *texturesource, IGameDef *gamedef,
1875 IrrlichtDevice *irr):
1878 m_texturesource(texturesource),
1884 ClientEnvironment::~ClientEnvironment()
1886 // delete active objects
1887 for(core::map<u16, ClientActiveObject*>::Iterator
1888 i = m_active_objects.getIterator();
1889 i.atEnd()==false; i++)
1891 delete i.getNode()->getValue();
1894 for(core::list<ClientSimpleObject*>::Iterator
1895 i = m_simple_objects.begin(); i != m_simple_objects.end(); i++)
1904 Map & ClientEnvironment::getMap()
1909 ClientMap & ClientEnvironment::getClientMap()
1914 void ClientEnvironment::addPlayer(Player *player)
1916 DSTACK(__FUNCTION_NAME);
1918 It is a failure if player is local and there already is a local
1921 assert(!(player->isLocal() == true && getLocalPlayer() != NULL));
1923 Environment::addPlayer(player);
1926 LocalPlayer * ClientEnvironment::getLocalPlayer()
1928 for(core::list<Player*>::Iterator i = m_players.begin();
1929 i != m_players.end(); i++)
1931 Player *player = *i;
1932 if(player->isLocal())
1933 return (LocalPlayer*)player;
1938 void ClientEnvironment::step(float dtime)
1940 DSTACK(__FUNCTION_NAME);
1942 /* Step time of day */
1943 stepTimeOfDay(dtime);
1945 // Get some settings
1946 bool fly_allowed = m_gamedef->checkLocalPrivilege("fly");
1947 bool free_move = fly_allowed && g_settings->getBool("free_move");
1950 LocalPlayer *lplayer = getLocalPlayer();
1952 // collision info queue
1953 core::list<CollisionInfo> player_collisions;
1956 Get the speed the player is going
1958 bool is_climbing = lplayer->is_climbing;
1960 f32 player_speed = lplayer->getSpeed().getLength();
1963 Maximum position increment
1965 //f32 position_max_increment = 0.05*BS;
1966 f32 position_max_increment = 0.1*BS;
1968 // Maximum time increment (for collision detection etc)
1969 // time = distance / speed
1970 f32 dtime_max_increment = 1;
1971 if(player_speed > 0.001)
1972 dtime_max_increment = position_max_increment / player_speed;
1974 // Maximum time increment is 10ms or lower
1975 if(dtime_max_increment > 0.01)
1976 dtime_max_increment = 0.01;
1978 // Don't allow overly huge dtime
1982 f32 dtime_downcount = dtime;
1985 Stuff that has a maximum time increment
1994 if(dtime_downcount > dtime_max_increment)
1996 dtime_part = dtime_max_increment;
1997 dtime_downcount -= dtime_part;
2001 dtime_part = dtime_downcount;
2003 Setting this to 0 (no -=dtime_part) disables an infinite loop
2004 when dtime_part is so small that dtime_downcount -= dtime_part
2007 dtime_downcount = 0;
2015 v3f lplayerpos = lplayer->getPosition();
2018 if(free_move == false && is_climbing == false)
2021 v3f speed = lplayer->getSpeed();
2022 if(lplayer->swimming_up == false)
2023 speed.Y -= 9.81 * BS * dtime_part * 2;
2026 if(lplayer->in_water_stable || lplayer->in_water)
2028 f32 max_down = 2.0*BS;
2029 if(speed.Y < -max_down) speed.Y = -max_down;
2032 if(speed.getLength() > max)
2034 speed = speed / speed.getLength() * max;
2038 lplayer->setSpeed(speed);
2043 This also does collision detection.
2045 lplayer->move(dtime_part, *m_map, position_max_increment,
2046 &player_collisions);
2049 while(dtime_downcount > 0.001);
2051 //std::cout<<"Looped "<<loopcount<<" times."<<std::endl;
2053 for(core::list<CollisionInfo>::Iterator
2054 i = player_collisions.begin();
2055 i != player_collisions.end(); i++)
2057 CollisionInfo &info = *i;
2058 v3f speed_diff = info.new_speed - info.old_speed;;
2059 // Handle only fall damage
2060 // (because otherwise walking against something in fast_move kills you)
2061 if(speed_diff.Y < 0 || info.old_speed.Y >= 0)
2063 // Get rid of other components
2066 f32 pre_factor = 1; // 1 hp per node/s
2067 f32 tolerance = BS*14; // 5 without damage
2068 f32 post_factor = 1; // 1 hp per node/s
2069 if(info.type == COLLISION_NODE)
2071 const ContentFeatures &f = m_gamedef->ndef()->
2072 get(m_map->getNodeNoEx(info.node_p));
2073 // Determine fall damage multiplier
2074 int addp = itemgroup_get(f.groups, "fall_damage_add_percent");
2075 pre_factor = 1.0 + (float)addp/100.0;
2077 float speed = pre_factor * speed_diff.getLength();
2078 if(speed > tolerance)
2080 f32 damage_f = (speed - tolerance)/BS * post_factor;
2081 u16 damage = (u16)(damage_f+0.5);
2083 damageLocalPlayer(damage, true);
2088 A quick draft of lava damage
2090 if(m_lava_hurt_interval.step(dtime, 1.0))
2092 v3f pf = lplayer->getPosition();
2094 // Feet, middle and head
2095 v3s16 p1 = floatToInt(pf + v3f(0, BS*0.1, 0), BS);
2096 MapNode n1 = m_map->getNodeNoEx(p1);
2097 v3s16 p2 = floatToInt(pf + v3f(0, BS*0.8, 0), BS);
2098 MapNode n2 = m_map->getNodeNoEx(p2);
2099 v3s16 p3 = floatToInt(pf + v3f(0, BS*1.6, 0), BS);
2100 MapNode n3 = m_map->getNodeNoEx(p2);
2102 u32 damage_per_second = 0;
2103 damage_per_second = MYMAX(damage_per_second,
2104 m_gamedef->ndef()->get(n1).damage_per_second);
2105 damage_per_second = MYMAX(damage_per_second,
2106 m_gamedef->ndef()->get(n2).damage_per_second);
2107 damage_per_second = MYMAX(damage_per_second,
2108 m_gamedef->ndef()->get(n3).damage_per_second);
2110 if(damage_per_second != 0)
2112 damageLocalPlayer(damage_per_second, true);
2117 Stuff that can be done in an arbitarily large dtime
2119 for(core::list<Player*>::Iterator i = m_players.begin();
2120 i != m_players.end(); i++)
2122 Player *player = *i;
2123 v3f playerpos = player->getPosition();
2126 Handle non-local players
2128 if(player->isLocal() == false)
2131 player->move(dtime, *m_map, 100*BS);
2135 // Update lighting on all players on client
2139 v3s16 p = player->getLightPosition();
2140 MapNode n = m_map->getNode(p);
2141 light = n.getLightBlendF1((float)getDayNightRatio()/1000, m_gamedef->ndef());
2143 catch(InvalidPositionException &e){
2144 light = blend_light_f1((float)getDayNightRatio()/1000, LIGHT_SUN, 0);
2146 player->light = light;
2150 Step active objects and update lighting of them
2153 bool update_lighting = m_active_object_light_update_interval.step(dtime, 0.21);
2154 for(core::map<u16, ClientActiveObject*>::Iterator
2155 i = m_active_objects.getIterator();
2156 i.atEnd()==false; i++)
2158 ClientActiveObject* obj = i.getNode()->getValue();
2160 obj->step(dtime, this);
2168 v3s16 p = obj->getLightPosition();
2169 MapNode n = m_map->getNode(p);
2170 light = n.getLightBlend(getDayNightRatio(), m_gamedef->ndef());
2172 catch(InvalidPositionException &e){
2173 light = blend_light(getDayNightRatio(), LIGHT_SUN, 0);
2175 obj->updateLight(light);
2180 Step and handle simple objects
2182 for(core::list<ClientSimpleObject*>::Iterator
2183 i = m_simple_objects.begin(); i != m_simple_objects.end();)
2185 ClientSimpleObject *simple = *i;
2186 core::list<ClientSimpleObject*>::Iterator cur = i;
2188 simple->step(dtime);
2189 if(simple->m_to_be_removed){
2191 m_simple_objects.erase(cur);
2196 void ClientEnvironment::addSimpleObject(ClientSimpleObject *simple)
2198 m_simple_objects.push_back(simple);
2201 ClientActiveObject* ClientEnvironment::getActiveObject(u16 id)
2203 core::map<u16, ClientActiveObject*>::Node *n;
2204 n = m_active_objects.find(id);
2207 return n->getValue();
2210 bool isFreeClientActiveObjectId(u16 id,
2211 core::map<u16, ClientActiveObject*> &objects)
2216 for(core::map<u16, ClientActiveObject*>::Iterator
2217 i = objects.getIterator();
2218 i.atEnd()==false; i++)
2220 if(i.getNode()->getKey() == id)
2226 u16 getFreeClientActiveObjectId(
2227 core::map<u16, ClientActiveObject*> &objects)
2232 if(isFreeClientActiveObjectId(new_id, objects))
2242 u16 ClientEnvironment::addActiveObject(ClientActiveObject *object)
2245 if(object->getId() == 0)
2247 u16 new_id = getFreeClientActiveObjectId(m_active_objects);
2250 infostream<<"ClientEnvironment::addActiveObject(): "
2251 <<"no free ids available"<<std::endl;
2255 object->setId(new_id);
2257 if(isFreeClientActiveObjectId(object->getId(), m_active_objects) == false)
2259 infostream<<"ClientEnvironment::addActiveObject(): "
2260 <<"id is not free ("<<object->getId()<<")"<<std::endl;
2264 infostream<<"ClientEnvironment::addActiveObject(): "
2265 <<"added (id="<<object->getId()<<")"<<std::endl;
2266 m_active_objects.insert(object->getId(), object);
2267 object->addToScene(m_smgr, m_texturesource, m_irr);
2268 { // Update lighting immediately
2272 v3s16 p = object->getLightPosition();
2273 MapNode n = m_map->getNode(p);
2274 light = n.getLightBlend(getDayNightRatio(), m_gamedef->ndef());
2276 catch(InvalidPositionException &e){
2277 light = blend_light(getDayNightRatio(), LIGHT_SUN, 0);
2279 object->updateLight(light);
2281 return object->getId();
2284 void ClientEnvironment::addActiveObject(u16 id, u8 type,
2285 const std::string &init_data)
2287 ClientActiveObject* obj =
2288 ClientActiveObject::create(type, m_gamedef, this);
2291 infostream<<"ClientEnvironment::addActiveObject(): "
2292 <<"id="<<id<<" type="<<type<<": Couldn't create object"
2301 obj->initialize(init_data);
2303 catch(SerializationError &e)
2305 errorstream<<"ClientEnvironment::addActiveObject():"
2306 <<" id="<<id<<" type="<<type
2307 <<": SerializationError in initialize(): "
2309 <<": init_data="<<serializeJsonString(init_data)
2313 addActiveObject(obj);
2316 void ClientEnvironment::removeActiveObject(u16 id)
2318 verbosestream<<"ClientEnvironment::removeActiveObject(): "
2319 <<"id="<<id<<std::endl;
2320 ClientActiveObject* obj = getActiveObject(id);
2323 infostream<<"ClientEnvironment::removeActiveObject(): "
2324 <<"id="<<id<<" not found"<<std::endl;
2327 obj->removeFromScene(true);
2329 m_active_objects.remove(id);
2332 void ClientEnvironment::processActiveObjectMessage(u16 id,
2333 const std::string &data)
2335 ClientActiveObject* obj = getActiveObject(id);
2338 infostream<<"ClientEnvironment::processActiveObjectMessage():"
2339 <<" got message for id="<<id<<", which doesn't exist."
2345 obj->processMessage(data);
2347 catch(SerializationError &e)
2349 errorstream<<"ClientEnvironment::processActiveObjectMessage():"
2350 <<" id="<<id<<" type="<<obj->getType()
2351 <<" SerializationError in processMessage(),"
2352 <<" message="<<serializeJsonString(data)
2358 Callbacks for activeobjects
2361 void ClientEnvironment::damageLocalPlayer(u8 damage, bool handle_hp)
2363 LocalPlayer *lplayer = getLocalPlayer();
2367 if(lplayer->hp > damage)
2368 lplayer->hp -= damage;
2373 ClientEnvEvent event;
2374 event.type = CEE_PLAYER_DAMAGE;
2375 event.player_damage.amount = damage;
2376 event.player_damage.send_to_server = handle_hp;
2377 m_client_event_queue.push_back(event);
2381 Client likes to call these
2384 void ClientEnvironment::getActiveObjects(v3f origin, f32 max_d,
2385 core::array<DistanceSortedActiveObject> &dest)
2387 for(core::map<u16, ClientActiveObject*>::Iterator
2388 i = m_active_objects.getIterator();
2389 i.atEnd()==false; i++)
2391 ClientActiveObject* obj = i.getNode()->getValue();
2393 f32 d = (obj->getPosition() - origin).getLength();
2398 DistanceSortedActiveObject dso(obj, d);
2400 dest.push_back(dso);
2404 ClientEnvEvent ClientEnvironment::getClientEvent()
2406 if(m_client_event_queue.size() == 0)
2408 ClientEnvEvent event;
2409 event.type = CEE_NONE;
2412 return m_client_event_queue.pop_front();
2415 #endif // #ifndef SERVER