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"
47 #define PP(x) "("<<(x).X<<","<<(x).Y<<","<<(x).Z<<")"
49 Environment::Environment():
51 m_time_of_day_f(9000./24000),
52 m_time_of_day_speed(0),
57 Environment::~Environment()
60 for(core::list<Player*>::Iterator i = m_players.begin();
61 i != m_players.end(); i++)
67 void Environment::addPlayer(Player *player)
69 DSTACK(__FUNCTION_NAME);
71 Check that peer_ids are unique.
72 Also check that names are unique.
73 Exception: there can be multiple players with peer_id=0
75 // If peer id is non-zero, it has to be unique.
76 if(player->peer_id != 0)
77 assert(getPlayer(player->peer_id) == NULL);
78 // Name has to be unique.
79 assert(getPlayer(player->getName()) == NULL);
81 m_players.push_back(player);
84 void Environment::removePlayer(u16 peer_id)
86 DSTACK(__FUNCTION_NAME);
88 for(core::list<Player*>::Iterator i = m_players.begin();
89 i != m_players.end(); i++)
92 if(player->peer_id != peer_id)
97 // See if there is an another one
98 // (shouldn't be, but just to be sure)
103 Player * Environment::getPlayer(u16 peer_id)
105 for(core::list<Player*>::Iterator i = m_players.begin();
106 i != m_players.end(); i++)
109 if(player->peer_id == peer_id)
115 Player * Environment::getPlayer(const char *name)
117 for(core::list<Player*>::Iterator i = m_players.begin();
118 i != m_players.end(); i++)
121 if(strcmp(player->getName(), name) == 0)
127 Player * Environment::getRandomConnectedPlayer()
129 core::list<Player*> connected_players = getPlayers(true);
130 u32 chosen_one = myrand() % connected_players.size();
132 for(core::list<Player*>::Iterator
133 i = connected_players.begin();
134 i != connected_players.end(); i++)
146 Player * Environment::getNearestConnectedPlayer(v3f pos)
148 core::list<Player*> connected_players = getPlayers(true);
150 Player *nearest_player = NULL;
151 for(core::list<Player*>::Iterator
152 i = connected_players.begin();
153 i != connected_players.end(); i++)
156 f32 d = player->getPosition().getDistanceFrom(pos);
157 if(d < nearest_d || nearest_player == NULL)
160 nearest_player = player;
163 return nearest_player;
166 core::list<Player*> Environment::getPlayers()
171 core::list<Player*> Environment::getPlayers(bool ignore_disconnected)
173 core::list<Player*> newlist;
174 for(core::list<Player*>::Iterator
175 i = m_players.begin();
176 i != m_players.end(); i++)
180 if(ignore_disconnected)
182 // Ignore disconnected players
183 if(player->peer_id == 0)
187 newlist.push_back(player);
192 void Environment::printPlayers(std::ostream &o)
194 o<<"Players in environment:"<<std::endl;
195 for(core::list<Player*>::Iterator i = m_players.begin();
196 i != m_players.end(); i++)
199 o<<"Player peer_id="<<player->peer_id<<std::endl;
203 u32 Environment::getDayNightRatio()
205 return time_to_daynight_ratio(m_time_of_day);
208 void Environment::stepTimeOfDay(float dtime)
210 m_time_counter += dtime;
211 f32 speed = m_time_of_day_speed * 24000./(24.*3600);
212 u32 units = (u32)(m_time_counter*speed);
213 m_time_counter -= (f32)units / speed;
217 if(m_time_of_day + units >= 24000)
219 m_time_of_day = (m_time_of_day + units) % 24000;
221 m_time_of_day_f = (float)m_time_of_day / 24000.0;
224 m_time_of_day_f += m_time_of_day_speed/24/3600*dtime;
225 if(m_time_of_day_f > 1.0)
226 m_time_of_day_f -= 1.0;
227 if(m_time_of_day_f < 0.0)
228 m_time_of_day_f += 1.0;
236 ABMWithState::ABMWithState(ActiveBlockModifier *abm_):
240 // Initialize timer to random value to spread processing
241 float itv = abm->getTriggerInterval();
242 itv = MYMAX(0.001, itv); // No less than 1ms
243 int minval = MYMAX(-0.51*itv, -60); // Clamp to
244 int maxval = MYMIN(0.51*itv, 60); // +-60 seconds
245 timer = myrand_range(minval, maxval);
252 void fillRadiusBlock(v3s16 p0, s16 r, core::map<v3s16, bool> &list)
255 for(p.X=p0.X-r; p.X<=p0.X+r; p.X++)
256 for(p.Y=p0.Y-r; p.Y<=p0.Y+r; p.Y++)
257 for(p.Z=p0.Z-r; p.Z<=p0.Z+r; p.Z++)
264 void ActiveBlockList::update(core::list<v3s16> &active_positions,
266 core::map<v3s16, bool> &blocks_removed,
267 core::map<v3s16, bool> &blocks_added)
272 core::map<v3s16, bool> newlist;
273 for(core::list<v3s16>::Iterator i = active_positions.begin();
274 i != active_positions.end(); i++)
276 fillRadiusBlock(*i, radius, newlist);
280 Find out which blocks on the old list are not on the new list
282 // Go through old list
283 for(core::map<v3s16, bool>::Iterator i = m_list.getIterator();
284 i.atEnd()==false; i++)
286 v3s16 p = i.getNode()->getKey();
287 // If not on new list, it's been removed
288 if(newlist.find(p) == NULL)
289 blocks_removed.insert(p, true);
293 Find out which blocks on the new list are not on the old list
295 // Go through new list
296 for(core::map<v3s16, bool>::Iterator i = newlist.getIterator();
297 i.atEnd()==false; i++)
299 v3s16 p = i.getNode()->getKey();
300 // If not on old list, it's been added
301 if(m_list.find(p) == NULL)
302 blocks_added.insert(p, true);
309 for(core::map<v3s16, bool>::Iterator i = newlist.getIterator();
310 i.atEnd()==false; i++)
312 v3s16 p = i.getNode()->getKey();
313 m_list.insert(p, true);
321 ServerEnvironment::ServerEnvironment(ServerMap *map, lua_State *L,
322 IGameDef *gamedef, IBackgroundBlockEmerger *emerger):
327 m_random_spawn_timer(3),
328 m_send_recommended_timer(0),
329 m_active_block_interval_overload_skip(0),
331 m_game_time_fraction_counter(0)
335 ServerEnvironment::~ServerEnvironment()
337 // Clear active block list.
338 // This makes the next one delete all active objects.
339 m_active_blocks.clear();
341 // Convert all objects to static and delete the active objects
342 deactivateFarObjects(true);
347 // Delete ActiveBlockModifiers
348 for(core::list<ABMWithState>::Iterator
349 i = m_abms.begin(); i != m_abms.end(); i++){
354 Map & ServerEnvironment::getMap()
359 ServerMap & ServerEnvironment::getServerMap()
365 void ServerEnvironment::serializePlayers(const std::string &savedir)
367 std::string players_path = savedir + "/players";
368 fs::CreateDir(players_path);
370 core::map<Player*, bool> saved_players;
372 std::vector<fs::DirListNode> player_files = fs::GetDirListing(players_path);
373 for(u32 i=0; i<player_files.size(); i++)
375 if(player_files[i].dir)
378 // Full path to this file
379 std::string path = players_path + "/" + player_files[i].name;
381 //infostream<<"Checking player file "<<path<<std::endl;
383 // Load player to see what is its name
384 RemotePlayer testplayer(m_gamedef);
386 // Open file and deserialize
387 std::ifstream is(path.c_str(), std::ios_base::binary);
388 if(is.good() == false)
390 infostream<<"Failed to read "<<path<<std::endl;
393 testplayer.deSerialize(is);
396 //infostream<<"Loaded test player with name "<<testplayer.getName()<<std::endl;
398 // Search for the player
399 std::string playername = testplayer.getName();
400 Player *player = getPlayer(playername.c_str());
403 infostream<<"Didn't find matching player, ignoring file "<<path<<std::endl;
407 //infostream<<"Found matching player, overwriting."<<std::endl;
409 // OK, found. Save player there.
411 // Open file and serialize
412 std::ofstream os(path.c_str(), std::ios_base::binary);
413 if(os.good() == false)
415 infostream<<"Failed to overwrite "<<path<<std::endl;
418 player->serialize(os);
419 saved_players.insert(player, true);
423 for(core::list<Player*>::Iterator i = m_players.begin();
424 i != m_players.end(); i++)
427 if(saved_players.find(player) != NULL)
429 /*infostream<<"Player "<<player->getName()
430 <<" was already saved."<<std::endl;*/
433 std::string playername = player->getName();
434 // Don't save unnamed player
437 //infostream<<"Not saving unnamed player."<<std::endl;
443 if(string_allowed(playername, PLAYERNAME_ALLOWED_CHARS) == false)
444 playername = "player";
445 std::string path = players_path + "/" + playername;
447 for(u32 i=0; i<1000; i++)
449 if(fs::PathExists(path) == false)
454 path = players_path + "/" + playername + itos(i);
458 infostream<<"Didn't find free file for player"<<std::endl;
463 /*infostream<<"Saving player "<<player->getName()<<" to "
465 // Open file and serialize
466 std::ofstream os(path.c_str(), std::ios_base::binary);
467 if(os.good() == false)
469 infostream<<"Failed to overwrite "<<path<<std::endl;
472 player->serialize(os);
473 saved_players.insert(player, true);
477 //infostream<<"Saved "<<saved_players.size()<<" players."<<std::endl;
480 void ServerEnvironment::deSerializePlayers(const std::string &savedir)
482 std::string players_path = savedir + "/players";
484 core::map<Player*, bool> saved_players;
486 std::vector<fs::DirListNode> player_files = fs::GetDirListing(players_path);
487 for(u32 i=0; i<player_files.size(); i++)
489 if(player_files[i].dir)
492 // Full path to this file
493 std::string path = players_path + "/" + player_files[i].name;
495 //infostream<<"Checking player file "<<path<<std::endl;
497 // Load player to see what is its name
498 RemotePlayer testplayer(m_gamedef);
500 // Open file and deserialize
501 std::ifstream is(path.c_str(), std::ios_base::binary);
502 if(is.good() == false)
504 infostream<<"Failed to read "<<path<<std::endl;
507 testplayer.deSerialize(is);
510 if(!string_allowed(testplayer.getName(), PLAYERNAME_ALLOWED_CHARS))
512 infostream<<"Not loading player with invalid name: "
513 <<testplayer.getName()<<std::endl;
516 /*infostream<<"Loaded test player with name "<<testplayer.getName()
519 // Search for the player
520 std::string playername = testplayer.getName();
521 Player *player = getPlayer(playername.c_str());
522 bool newplayer = false;
525 //infostream<<"Is a new player"<<std::endl;
526 player = new RemotePlayer(m_gamedef);
532 verbosestream<<"Reading player "<<testplayer.getName()<<" from "
534 // Open file and deserialize
535 std::ifstream is(path.c_str(), std::ios_base::binary);
536 if(is.good() == false)
538 infostream<<"Failed to read "<<path<<std::endl;
541 player->deSerialize(is);
551 void ServerEnvironment::saveMeta(const std::string &savedir)
553 std::string path = savedir + "/env_meta.txt";
555 // Open file and serialize
556 std::ofstream os(path.c_str(), std::ios_base::binary);
557 if(os.good() == false)
559 infostream<<"ServerEnvironment::saveMeta(): Failed to open "
561 throw SerializationError("Couldn't save env meta");
565 args.setU64("game_time", m_game_time);
566 args.setU64("time_of_day", getTimeOfDay());
571 void ServerEnvironment::loadMeta(const std::string &savedir)
573 std::string path = savedir + "/env_meta.txt";
575 // Open file and deserialize
576 std::ifstream is(path.c_str(), std::ios_base::binary);
577 if(is.good() == false)
579 infostream<<"ServerEnvironment::loadMeta(): Failed to open "
581 throw SerializationError("Couldn't load env meta");
589 throw SerializationError
590 ("ServerEnvironment::loadMeta(): EnvArgsEnd not found");
592 std::getline(is, line);
593 std::string trimmedline = trim(line);
594 if(trimmedline == "EnvArgsEnd")
596 args.parseConfigLine(line);
600 m_game_time = args.getU64("game_time");
601 }catch(SettingNotFoundException &e){
602 // Getting this is crucial, otherwise timestamps are useless
603 throw SerializationError("Couldn't load env meta game_time");
607 m_time_of_day = args.getU64("time_of_day");
608 }catch(SettingNotFoundException &e){
609 // This is not as important
610 m_time_of_day = 9000;
616 ActiveBlockModifier *abm;
618 std::set<content_t> required_neighbors;
624 ServerEnvironment *m_env;
625 std::map<content_t, std::list<ActiveABM> > m_aabms;
627 ABMHandler(core::list<ABMWithState> &abms,
628 float dtime_s, ServerEnvironment *env,
634 INodeDefManager *ndef = env->getGameDef()->ndef();
635 for(core::list<ABMWithState>::Iterator
636 i = abms.begin(); i != abms.end(); i++){
637 ActiveBlockModifier *abm = i->abm;
638 float trigger_interval = abm->getTriggerInterval();
639 if(trigger_interval < 0.001)
640 trigger_interval = 0.001;
641 float actual_interval = dtime_s;
644 if(i->timer < trigger_interval)
646 i->timer -= trigger_interval;
647 actual_interval = trigger_interval;
649 float intervals = actual_interval / trigger_interval;
652 float chance = abm->getTriggerChance();
657 aabm.chance = chance / intervals;
661 std::set<std::string> required_neighbors_s
662 = abm->getRequiredNeighbors();
663 for(std::set<std::string>::iterator
664 i = required_neighbors_s.begin();
665 i != required_neighbors_s.end(); i++)
667 ndef->getIds(*i, aabm.required_neighbors);
670 std::set<std::string> contents_s = abm->getTriggerContents();
671 for(std::set<std::string>::iterator
672 i = contents_s.begin(); i != contents_s.end(); i++)
674 std::set<content_t> ids;
675 ndef->getIds(*i, ids);
676 for(std::set<content_t>::const_iterator k = ids.begin();
680 std::map<content_t, std::list<ActiveABM> >::iterator j;
682 if(j == m_aabms.end()){
683 std::list<ActiveABM> aabmlist;
684 m_aabms[c] = aabmlist;
687 j->second.push_back(aabm);
692 void apply(MapBlock *block)
697 ServerMap *map = &m_env->getServerMap();
700 for(p0.X=0; p0.X<MAP_BLOCKSIZE; p0.X++)
701 for(p0.Y=0; p0.Y<MAP_BLOCKSIZE; p0.Y++)
702 for(p0.Z=0; p0.Z<MAP_BLOCKSIZE; p0.Z++)
704 MapNode n = block->getNodeNoEx(p0);
705 content_t c = n.getContent();
706 v3s16 p = p0 + block->getPosRelative();
708 std::map<content_t, std::list<ActiveABM> >::iterator j;
710 if(j == m_aabms.end())
713 for(std::list<ActiveABM>::iterator
714 i = j->second.begin(); i != j->second.end(); i++)
716 if(myrand() % i->chance != 0)
720 if(!i->required_neighbors.empty())
723 for(p1.X = p.X-1; p1.X <= p.X+1; p1.X++)
724 for(p1.Y = p.Y-1; p1.Y <= p.Y+1; p1.Y++)
725 for(p1.Z = p.Z-1; p1.Z <= p.Z+1; p1.Z++)
729 MapNode n = map->getNodeNoEx(p1);
730 content_t c = n.getContent();
731 std::set<content_t>::const_iterator k;
732 k = i->required_neighbors.find(c);
733 if(k != i->required_neighbors.end()){
737 // No required neighbor found
742 // Find out how many objects the block contains
743 u32 active_object_count = block->m_static_objects.m_active.size();
744 // Find out how many objects this and all the neighbors contain
745 u32 active_object_count_wider = 0;
746 for(s16 x=-1; x<=1; x++)
747 for(s16 y=-1; y<=1; y++)
748 for(s16 z=-1; z<=1; z++)
750 MapBlock *block2 = map->getBlockNoCreateNoEx(
751 block->getPos() + v3s16(x,y,z));
754 active_object_count_wider +=
755 block2->m_static_objects.m_active.size()
756 + block2->m_static_objects.m_stored.size();
759 // Call all the trigger variations
760 i->abm->trigger(m_env, p, n);
761 i->abm->trigger(m_env, p, n,
762 active_object_count, active_object_count_wider);
768 void ServerEnvironment::activateBlock(MapBlock *block, u32 additional_dtime)
770 // Get time difference
772 u32 stamp = block->getTimestamp();
773 if(m_game_time > stamp && stamp != BLOCK_TIMESTAMP_UNDEFINED)
774 dtime_s = m_game_time - block->getTimestamp();
775 dtime_s += additional_dtime;
777 /*infostream<<"ServerEnvironment::activateBlock(): block timestamp: "
778 <<stamp<<", game time: "<<m_game_time<<std::endl;*/
780 // Set current time as timestamp
781 block->setTimestampNoChangedFlag(m_game_time);
783 /*infostream<<"ServerEnvironment::activateBlock(): block is "
784 <<dtime_s<<" seconds old."<<std::endl;*/
786 // Activate stored objects
787 activateObjects(block);
790 std::map<v3s16, NodeTimer> elapsed_timers =
791 block->m_node_timers.step((float)dtime_s);
792 if(!elapsed_timers.empty()){
794 for(std::map<v3s16, NodeTimer>::iterator
795 i = elapsed_timers.begin();
796 i != elapsed_timers.end(); i++){
797 n = block->getNodeNoEx(i->first);
798 if(scriptapi_node_on_timer(m_lua,i->first,n,i->second.elapsed))
799 block->setNodeTimer(i->first,NodeTimer(i->second.timeout,0));
803 /* Handle ActiveBlockModifiers */
804 ABMHandler abmhandler(m_abms, dtime_s, this, false);
805 abmhandler.apply(block);
808 void ServerEnvironment::addActiveBlockModifier(ActiveBlockModifier *abm)
810 m_abms.push_back(ABMWithState(abm));
813 std::set<u16> ServerEnvironment::getObjectsInsideRadius(v3f pos, float radius)
815 std::set<u16> objects;
816 for(core::map<u16, ServerActiveObject*>::Iterator
817 i = m_active_objects.getIterator();
818 i.atEnd()==false; i++)
820 ServerActiveObject* obj = i.getNode()->getValue();
821 u16 id = i.getNode()->getKey();
822 v3f objectpos = obj->getBasePosition();
823 if(objectpos.getDistanceFrom(pos) > radius)
830 void ServerEnvironment::clearAllObjects()
832 infostream<<"ServerEnvironment::clearAllObjects(): "
833 <<"Removing all active objects"<<std::endl;
834 core::list<u16> objects_to_remove;
835 for(core::map<u16, ServerActiveObject*>::Iterator
836 i = m_active_objects.getIterator();
837 i.atEnd()==false; i++)
839 ServerActiveObject* obj = i.getNode()->getValue();
840 if(obj->getType() == ACTIVEOBJECT_TYPE_PLAYER)
842 u16 id = i.getNode()->getKey();
843 v3f objectpos = obj->getBasePosition();
844 // Delete static object if block is loaded
845 if(obj->m_static_exists){
846 MapBlock *block = m_map->getBlockNoCreateNoEx(obj->m_static_block);
848 block->m_static_objects.remove(id);
849 block->raiseModified(MOD_STATE_WRITE_NEEDED,
851 obj->m_static_exists = false;
854 // If known by some client, don't delete immediately
855 if(obj->m_known_by_count > 0){
856 obj->m_pending_deactivation = true;
857 obj->m_removed = true;
861 // Tell the object about removal
862 obj->removingFromEnvironment();
863 // Deregister in scripting api
864 scriptapi_rm_object_reference(m_lua, obj);
866 // Delete active object
867 if(obj->environmentDeletes())
869 // Id to be removed from m_active_objects
870 objects_to_remove.push_back(id);
872 // Remove references from m_active_objects
873 for(core::list<u16>::Iterator i = objects_to_remove.begin();
874 i != objects_to_remove.end(); i++)
876 m_active_objects.remove(*i);
879 core::list<v3s16> loadable_blocks;
880 infostream<<"ServerEnvironment::clearAllObjects(): "
881 <<"Listing all loadable blocks"<<std::endl;
882 m_map->listAllLoadableBlocks(loadable_blocks);
883 infostream<<"ServerEnvironment::clearAllObjects(): "
884 <<"Done listing all loadable blocks: "
885 <<loadable_blocks.size()
886 <<", now clearing"<<std::endl;
887 u32 report_interval = loadable_blocks.size() / 10;
888 u32 num_blocks_checked = 0;
889 u32 num_blocks_cleared = 0;
890 u32 num_objs_cleared = 0;
891 for(core::list<v3s16>::Iterator i = loadable_blocks.begin();
892 i != loadable_blocks.end(); i++)
895 MapBlock *block = m_map->emergeBlock(p, false);
897 errorstream<<"ServerEnvironment::clearAllObjects(): "
898 <<"Failed to emerge block "<<PP(p)<<std::endl;
901 u32 num_stored = block->m_static_objects.m_stored.size();
902 u32 num_active = block->m_static_objects.m_active.size();
903 if(num_stored != 0 || num_active != 0){
904 block->m_static_objects.m_stored.clear();
905 block->m_static_objects.m_active.clear();
906 block->raiseModified(MOD_STATE_WRITE_NEEDED,
908 num_objs_cleared += num_stored + num_active;
909 num_blocks_cleared++;
911 num_blocks_checked++;
913 if(num_blocks_checked % report_interval == 0){
914 float percent = 100.0 * (float)num_blocks_checked /
915 loadable_blocks.size();
916 infostream<<"ServerEnvironment::clearAllObjects(): "
917 <<"Cleared "<<num_objs_cleared<<" objects"
918 <<" in "<<num_blocks_cleared<<" blocks ("
919 <<percent<<"%)"<<std::endl;
922 infostream<<"ServerEnvironment::clearAllObjects(): "
923 <<"Finished: Cleared "<<num_objs_cleared<<" objects"
924 <<" in "<<num_blocks_cleared<<" blocks"<<std::endl;
927 void ServerEnvironment::step(float dtime)
929 DSTACK(__FUNCTION_NAME);
931 //TimeTaker timer("ServerEnv step");
933 /* Step time of day */
934 stepTimeOfDay(dtime);
940 m_game_time_fraction_counter += dtime;
941 u32 inc_i = (u32)m_game_time_fraction_counter;
942 m_game_time += inc_i;
943 m_game_time_fraction_counter -= (float)inc_i;
950 ScopeProfiler sp(g_profiler, "SEnv: handle players avg", SPT_AVG);
951 for(core::list<Player*>::Iterator i = m_players.begin();
952 i != m_players.end(); i++)
956 // Ignore disconnected players
957 if(player->peer_id == 0)
960 v3f playerpos = player->getPosition();
963 player->move(dtime, *m_map, 100*BS);
968 Manage active block list
970 if(m_active_blocks_management_interval.step(dtime, 2.0))
972 ScopeProfiler sp(g_profiler, "SEnv: manage act. block list avg /2s", SPT_AVG);
974 Get player block positions
976 core::list<v3s16> players_blockpos;
977 for(core::list<Player*>::Iterator
978 i = m_players.begin();
979 i != m_players.end(); i++)
982 // Ignore disconnected players
983 if(player->peer_id == 0)
985 v3s16 blockpos = getNodeBlockPos(
986 floatToInt(player->getPosition(), BS));
987 players_blockpos.push_back(blockpos);
991 Update list of active blocks, collecting changes
993 const s16 active_block_range = g_settings->getS16("active_block_range");
994 core::map<v3s16, bool> blocks_removed;
995 core::map<v3s16, bool> blocks_added;
996 m_active_blocks.update(players_blockpos, active_block_range,
997 blocks_removed, blocks_added);
1000 Handle removed blocks
1003 // Convert active objects that are no more in active blocks to static
1004 deactivateFarObjects(false);
1006 for(core::map<v3s16, bool>::Iterator
1007 i = blocks_removed.getIterator();
1008 i.atEnd()==false; i++)
1010 v3s16 p = i.getNode()->getKey();
1012 /*infostream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
1013 <<") became inactive"<<std::endl;*/
1015 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1019 // Set current time as timestamp (and let it set ChangedFlag)
1020 block->setTimestamp(m_game_time);
1027 for(core::map<v3s16, bool>::Iterator
1028 i = blocks_added.getIterator();
1029 i.atEnd()==false; i++)
1031 v3s16 p = i.getNode()->getKey();
1033 /*infostream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
1034 <<") became active"<<std::endl;*/
1036 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1038 // Block needs to be fetched first
1039 m_emerger->queueBlockEmerge(p, false);
1040 m_active_blocks.m_list.remove(p);
1044 activateBlock(block);
1049 Mess around in active blocks
1051 if(m_active_blocks_nodemetadata_interval.step(dtime, 1.0))
1053 ScopeProfiler sp(g_profiler, "SEnv: mess in act. blocks avg /1s", SPT_AVG);
1057 for(core::map<v3s16, bool>::Iterator
1058 i = m_active_blocks.m_list.getIterator();
1059 i.atEnd()==false; i++)
1061 v3s16 p = i.getNode()->getKey();
1063 /*infostream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
1064 <<") being handled"<<std::endl;*/
1066 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1070 // Reset block usage timer
1071 block->resetUsageTimer();
1073 // Set current time as timestamp
1074 block->setTimestampNoChangedFlag(m_game_time);
1075 // If time has changed much from the one on disk,
1076 // set block to be saved when it is unloaded
1077 if(block->getTimestamp() > block->getDiskTimestamp() + 60)
1078 block->raiseModified(MOD_STATE_WRITE_AT_UNLOAD,
1079 "Timestamp older than 60s (step)");
1082 std::map<v3s16, NodeTimer> elapsed_timers =
1083 block->m_node_timers.step((float)dtime);
1084 if(!elapsed_timers.empty()){
1086 for(std::map<v3s16, NodeTimer>::iterator
1087 i = elapsed_timers.begin();
1088 i != elapsed_timers.end(); i++){
1089 n = block->getNodeNoEx(i->first);
1090 p = i->first + block->getPosRelative();
1091 if(scriptapi_node_on_timer(m_lua,p,n,i->second.elapsed))
1092 block->setNodeTimer(i->first,NodeTimer(i->second.timeout,0));
1098 const float abm_interval = 1.0;
1099 if(m_active_block_modifier_interval.step(dtime, abm_interval))
1101 if(m_active_block_interval_overload_skip > 0){
1102 ScopeProfiler sp(g_profiler, "SEnv: ABM overload skips");
1103 m_active_block_interval_overload_skip--;
1106 ScopeProfiler sp(g_profiler, "SEnv: modify in blocks avg /1s", SPT_AVG);
1107 TimeTaker timer("modify in active blocks");
1109 // Initialize handling of ActiveBlockModifiers
1110 ABMHandler abmhandler(m_abms, abm_interval, this, true);
1112 for(core::map<v3s16, bool>::Iterator
1113 i = m_active_blocks.m_list.getIterator();
1114 i.atEnd()==false; i++)
1116 v3s16 p = i.getNode()->getKey();
1118 /*infostream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
1119 <<") being handled"<<std::endl;*/
1121 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1125 // Set current time as timestamp
1126 block->setTimestampNoChangedFlag(m_game_time);
1128 /* Handle ActiveBlockModifiers */
1129 abmhandler.apply(block);
1132 u32 time_ms = timer.stop(true);
1133 u32 max_time_ms = 200;
1134 if(time_ms > max_time_ms){
1135 infostream<<"WARNING: active block modifiers took "
1136 <<time_ms<<"ms (longer than "
1137 <<max_time_ms<<"ms)"<<std::endl;
1138 m_active_block_interval_overload_skip = (time_ms / max_time_ms) + 1;
1143 Step script environment (run global on_step())
1145 scriptapi_environment_step(m_lua, dtime);
1151 ScopeProfiler sp(g_profiler, "SEnv: step act. objs avg", SPT_AVG);
1152 //TimeTaker timer("Step active objects");
1154 g_profiler->avg("SEnv: num of objects", m_active_objects.size());
1156 // This helps the objects to send data at the same time
1157 bool send_recommended = false;
1158 m_send_recommended_timer += dtime;
1159 if(m_send_recommended_timer > getSendRecommendedInterval())
1161 m_send_recommended_timer -= getSendRecommendedInterval();
1162 send_recommended = true;
1165 for(core::map<u16, ServerActiveObject*>::Iterator
1166 i = m_active_objects.getIterator();
1167 i.atEnd()==false; i++)
1169 ServerActiveObject* obj = i.getNode()->getValue();
1170 // Remove non-peaceful mobs on peaceful mode
1171 if(g_settings->getBool("only_peaceful_mobs")){
1172 if(!obj->isPeaceful())
1173 obj->m_removed = true;
1175 // Don't step if is to be removed or stored statically
1176 if(obj->m_removed || obj->m_pending_deactivation)
1179 obj->step(dtime, send_recommended);
1180 // Read messages from object
1181 while(obj->m_messages_out.size() > 0)
1183 m_active_object_messages.push_back(
1184 obj->m_messages_out.pop_front());
1190 Manage active objects
1192 if(m_object_management_interval.step(dtime, 0.5))
1194 ScopeProfiler sp(g_profiler, "SEnv: remove removed objs avg /.5s", SPT_AVG);
1196 Remove objects that satisfy (m_removed && m_known_by_count==0)
1198 removeRemovedObjects();
1202 ServerActiveObject* ServerEnvironment::getActiveObject(u16 id)
1204 core::map<u16, ServerActiveObject*>::Node *n;
1205 n = m_active_objects.find(id);
1208 return n->getValue();
1211 bool isFreeServerActiveObjectId(u16 id,
1212 core::map<u16, ServerActiveObject*> &objects)
1217 for(core::map<u16, ServerActiveObject*>::Iterator
1218 i = objects.getIterator();
1219 i.atEnd()==false; i++)
1221 if(i.getNode()->getKey() == id)
1227 u16 getFreeServerActiveObjectId(
1228 core::map<u16, ServerActiveObject*> &objects)
1233 if(isFreeServerActiveObjectId(new_id, objects))
1243 u16 ServerEnvironment::addActiveObject(ServerActiveObject *object)
1246 u16 id = addActiveObjectRaw(object, true);
1250 bool ServerEnvironment::addActiveObjectAsStatic(ServerActiveObject *obj)
1254 v3f objectpos = obj->getBasePosition();
1256 // The block in which the object resides in
1257 v3s16 blockpos_o = getNodeBlockPos(floatToInt(objectpos, BS));
1260 Update the static data
1263 // Create new static object
1264 std::string staticdata = obj->getStaticData();
1265 StaticObject s_obj(obj->getType(), objectpos, staticdata);
1266 // Add to the block where the object is located in
1267 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1268 // Get or generate the block
1269 MapBlock *block = m_map->emergeBlock(blockpos);
1271 bool succeeded = false;
1275 block->m_static_objects.insert(0, s_obj);
1276 block->raiseModified(MOD_STATE_WRITE_AT_UNLOAD,
1277 "addActiveObjectAsStatic");
1281 infostream<<"ServerEnvironment::addActiveObjectAsStatic: "
1282 <<"Could not find or generate "
1283 <<"a block for storing static object"<<std::endl;
1287 if(obj->environmentDeletes())
1294 Finds out what new objects have been added to
1295 inside a radius around a position
1297 void ServerEnvironment::getAddedActiveObjects(v3s16 pos, s16 radius,
1298 core::map<u16, bool> ¤t_objects,
1299 core::map<u16, bool> &added_objects)
1301 v3f pos_f = intToFloat(pos, BS);
1302 f32 radius_f = radius * BS;
1304 Go through the object list,
1305 - discard m_removed objects,
1306 - discard objects that are too far away,
1307 - discard objects that are found in current_objects.
1308 - add remaining objects to added_objects
1310 for(core::map<u16, ServerActiveObject*>::Iterator
1311 i = m_active_objects.getIterator();
1312 i.atEnd()==false; i++)
1314 u16 id = i.getNode()->getKey();
1316 ServerActiveObject *object = i.getNode()->getValue();
1319 // Discard if removed
1320 if(object->m_removed)
1322 if(object->unlimitedTransferDistance() == false){
1323 // Discard if too far
1324 f32 distance_f = object->getBasePosition().getDistanceFrom(pos_f);
1325 if(distance_f > radius_f)
1328 // Discard if already on current_objects
1329 core::map<u16, bool>::Node *n;
1330 n = current_objects.find(id);
1333 // Add to added_objects
1334 added_objects.insert(id, false);
1339 Finds out what objects have been removed from
1340 inside a radius around a position
1342 void ServerEnvironment::getRemovedActiveObjects(v3s16 pos, s16 radius,
1343 core::map<u16, bool> ¤t_objects,
1344 core::map<u16, bool> &removed_objects)
1346 v3f pos_f = intToFloat(pos, BS);
1347 f32 radius_f = radius * BS;
1349 Go through current_objects; object is removed if:
1350 - object is not found in m_active_objects (this is actually an
1351 error condition; objects should be set m_removed=true and removed
1352 only after all clients have been informed about removal), or
1353 - object has m_removed=true, or
1354 - object is too far away
1356 for(core::map<u16, bool>::Iterator
1357 i = current_objects.getIterator();
1358 i.atEnd()==false; i++)
1360 u16 id = i.getNode()->getKey();
1361 ServerActiveObject *object = getActiveObject(id);
1364 infostream<<"ServerEnvironment::getRemovedActiveObjects():"
1365 <<" object in current_objects is NULL"<<std::endl;
1366 removed_objects.insert(id, false);
1370 if(object->m_removed)
1372 removed_objects.insert(id, false);
1376 // If transfer distance is unlimited, don't remove
1377 if(object->unlimitedTransferDistance())
1380 f32 distance_f = object->getBasePosition().getDistanceFrom(pos_f);
1382 if(distance_f >= radius_f)
1384 removed_objects.insert(id, false);
1392 ActiveObjectMessage ServerEnvironment::getActiveObjectMessage()
1394 if(m_active_object_messages.size() == 0)
1395 return ActiveObjectMessage(0);
1397 return m_active_object_messages.pop_front();
1401 ************ Private methods *************
1404 u16 ServerEnvironment::addActiveObjectRaw(ServerActiveObject *object,
1408 if(object->getId() == 0){
1409 u16 new_id = getFreeServerActiveObjectId(m_active_objects);
1412 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1413 <<"no free ids available"<<std::endl;
1414 if(object->environmentDeletes())
1418 object->setId(new_id);
1421 verbosestream<<"ServerEnvironment::addActiveObjectRaw(): "
1422 <<"supplied with id "<<object->getId()<<std::endl;
1424 if(isFreeServerActiveObjectId(object->getId(), m_active_objects) == false)
1426 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1427 <<"id is not free ("<<object->getId()<<")"<<std::endl;
1428 if(object->environmentDeletes())
1432 /*infostream<<"ServerEnvironment::addActiveObjectRaw(): "
1433 <<"added (id="<<object->getId()<<")"<<std::endl;*/
1435 m_active_objects.insert(object->getId(), object);
1437 verbosestream<<"ServerEnvironment::addActiveObjectRaw(): "
1438 <<"Added id="<<object->getId()<<"; there are now "
1439 <<m_active_objects.size()<<" active objects."
1442 // Register reference in scripting api (must be done before post-init)
1443 scriptapi_add_object_reference(m_lua, object);
1444 // Post-initialize object
1445 object->addedToEnvironment();
1447 // Add static data to block
1448 if(object->isStaticAllowed())
1450 // Add static object to active static list of the block
1451 v3f objectpos = object->getBasePosition();
1452 std::string staticdata = object->getStaticData();
1453 StaticObject s_obj(object->getType(), objectpos, staticdata);
1454 // Add to the block where the object is located in
1455 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1456 MapBlock *block = m_map->getBlockNoCreateNoEx(blockpos);
1459 block->m_static_objects.m_active.insert(object->getId(), s_obj);
1460 object->m_static_exists = true;
1461 object->m_static_block = blockpos;
1464 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1465 "addActiveObjectRaw");
1468 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1469 <<"could not find block for storing id="<<object->getId()
1470 <<" statically"<<std::endl;
1474 return object->getId();
1478 Remove objects that satisfy (m_removed && m_known_by_count==0)
1480 void ServerEnvironment::removeRemovedObjects()
1482 core::list<u16> objects_to_remove;
1483 for(core::map<u16, ServerActiveObject*>::Iterator
1484 i = m_active_objects.getIterator();
1485 i.atEnd()==false; i++)
1487 u16 id = i.getNode()->getKey();
1488 ServerActiveObject* obj = i.getNode()->getValue();
1489 // This shouldn't happen but check it
1492 infostream<<"NULL object found in ServerEnvironment"
1493 <<" while finding removed objects. id="<<id<<std::endl;
1494 // Id to be removed from m_active_objects
1495 objects_to_remove.push_back(id);
1500 We will delete objects that are marked as removed or thatare
1501 waiting for deletion after deactivation
1503 if(obj->m_removed == false && obj->m_pending_deactivation == false)
1507 Delete static data from block if is marked as removed
1509 if(obj->m_static_exists && obj->m_removed)
1511 MapBlock *block = m_map->emergeBlock(obj->m_static_block);
1514 block->m_static_objects.remove(id);
1515 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1516 "removeRemovedObjects");
1517 obj->m_static_exists = false;
1521 // If m_known_by_count > 0, don't actually remove.
1522 if(obj->m_known_by_count > 0)
1525 // Tell the object about removal
1526 obj->removingFromEnvironment();
1527 // Deregister in scripting api
1528 scriptapi_rm_object_reference(m_lua, obj);
1531 if(obj->environmentDeletes())
1533 // Id to be removed from m_active_objects
1534 objects_to_remove.push_back(id);
1536 // Remove references from m_active_objects
1537 for(core::list<u16>::Iterator i = objects_to_remove.begin();
1538 i != objects_to_remove.end(); i++)
1540 m_active_objects.remove(*i);
1544 static void print_hexdump(std::ostream &o, const std::string &data)
1546 const int linelength = 16;
1547 for(int l=0; ; l++){
1548 int i0 = linelength * l;
1549 bool at_end = false;
1550 int thislinelength = linelength;
1551 if(i0 + thislinelength > (int)data.size()){
1552 thislinelength = data.size() - i0;
1555 for(int di=0; di<linelength; di++){
1558 if(di<thislinelength)
1559 snprintf(buf, 4, "%.2x ", data[i]);
1561 snprintf(buf, 4, " ");
1565 for(int di=0; di<thislinelength; di++){
1579 Convert stored objects from blocks near the players to active.
1581 void ServerEnvironment::activateObjects(MapBlock *block)
1585 // Ignore if no stored objects (to not set changed flag)
1586 if(block->m_static_objects.m_stored.size() == 0)
1588 verbosestream<<"ServerEnvironment::activateObjects(): "
1589 <<"activating objects of block "<<PP(block->getPos())
1590 <<" ("<<block->m_static_objects.m_stored.size()
1591 <<" objects)"<<std::endl;
1592 bool large_amount = (block->m_static_objects.m_stored.size() > 49);
1594 errorstream<<"suspiciously large amount of objects detected: "
1595 <<block->m_static_objects.m_stored.size()<<" in "
1596 <<PP(block->getPos())
1597 <<"; removing all of them."<<std::endl;
1598 // Clear stored list
1599 block->m_static_objects.m_stored.clear();
1600 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1601 "stored list cleared in activateObjects due to "
1602 "large amount of objects");
1605 // A list for objects that couldn't be converted to static for some
1606 // reason. They will be stored back.
1607 core::list<StaticObject> new_stored;
1608 // Loop through stored static objects
1609 for(core::list<StaticObject>::Iterator
1610 i = block->m_static_objects.m_stored.begin();
1611 i != block->m_static_objects.m_stored.end(); i++)
1613 /*infostream<<"Server: Creating an active object from "
1614 <<"static data"<<std::endl;*/
1615 StaticObject &s_obj = *i;
1616 // Create an active object from the data
1617 ServerActiveObject *obj = ServerActiveObject::create
1618 (s_obj.type, this, 0, s_obj.pos, s_obj.data);
1619 // If couldn't create object, store static data back.
1622 errorstream<<"ServerEnvironment::activateObjects(): "
1623 <<"failed to create active object from static object "
1624 <<"in block "<<PP(s_obj.pos/BS)
1625 <<" type="<<(int)s_obj.type<<" data:"<<std::endl;
1626 print_hexdump(verbosestream, s_obj.data);
1628 new_stored.push_back(s_obj);
1631 verbosestream<<"ServerEnvironment::activateObjects(): "
1632 <<"activated static object pos="<<PP(s_obj.pos/BS)
1633 <<" type="<<(int)s_obj.type<<std::endl;
1634 // This will also add the object to the active static list
1635 addActiveObjectRaw(obj, false);
1637 // Clear stored list
1638 block->m_static_objects.m_stored.clear();
1639 // Add leftover failed stuff to stored list
1640 for(core::list<StaticObject>::Iterator
1641 i = new_stored.begin();
1642 i != new_stored.end(); i++)
1644 StaticObject &s_obj = *i;
1645 block->m_static_objects.m_stored.push_back(s_obj);
1648 Note: Block hasn't really been modified here.
1649 The objects have just been activated and moved from the stored
1650 static list to the active static list.
1651 As such, the block is essentially the same.
1652 Thus, do not call block->raiseModified(MOD_STATE_WRITE_NEEDED).
1653 Otherwise there would be a huge amount of unnecessary I/O.
1658 Convert objects that are not standing inside active blocks to static.
1660 If m_known_by_count != 0, active object is not deleted, but static
1661 data is still updated.
1663 If force_delete is set, active object is deleted nevertheless. It
1664 shall only be set so in the destructor of the environment.
1666 void ServerEnvironment::deactivateFarObjects(bool force_delete)
1668 core::list<u16> objects_to_remove;
1669 for(core::map<u16, ServerActiveObject*>::Iterator
1670 i = m_active_objects.getIterator();
1671 i.atEnd()==false; i++)
1673 ServerActiveObject* obj = i.getNode()->getValue();
1676 // Do not deactivate if static data creation not allowed
1677 if(!force_delete && !obj->isStaticAllowed())
1680 // If pending deactivation, let removeRemovedObjects() do it
1681 if(!force_delete && obj->m_pending_deactivation)
1684 u16 id = i.getNode()->getKey();
1685 v3f objectpos = obj->getBasePosition();
1687 // The block in which the object resides in
1688 v3s16 blockpos_o = getNodeBlockPos(floatToInt(objectpos, BS));
1690 // If block is active, don't remove
1691 if(!force_delete && m_active_blocks.contains(blockpos_o))
1694 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
1695 <<"deactivating object id="<<id<<" on inactive block "
1696 <<PP(blockpos_o)<<std::endl;
1698 // If known by some client, don't immediately delete.
1699 bool pending_delete = (obj->m_known_by_count > 0 && !force_delete);
1702 Update the static data
1705 if(obj->isStaticAllowed())
1707 // Create new static object
1708 std::string staticdata_new = obj->getStaticData();
1709 StaticObject s_obj(obj->getType(), objectpos, staticdata_new);
1711 bool stays_in_same_block = false;
1712 bool data_changed = true;
1714 if(obj->m_static_exists){
1715 if(obj->m_static_block == blockpos_o)
1716 stays_in_same_block = true;
1718 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
1720 core::map<u16, StaticObject>::Node *n =
1721 block->m_static_objects.m_active.find(id);
1723 StaticObject static_old = n->getValue();
1725 float save_movem = obj->getMinimumSavedMovement();
1727 if(static_old.data == staticdata_new &&
1728 (static_old.pos - objectpos).getLength() < save_movem)
1729 data_changed = false;
1731 errorstream<<"ServerEnvironment::deactivateFarObjects(): "
1732 <<"id="<<id<<" m_static_exists=true but "
1733 <<"static data doesn't actually exist in "
1734 <<PP(obj->m_static_block)<<std::endl;
1738 bool shall_be_written = (!stays_in_same_block || data_changed);
1740 // Delete old static object
1741 if(obj->m_static_exists)
1743 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
1746 block->m_static_objects.remove(id);
1747 obj->m_static_exists = false;
1748 // Only mark block as modified if data changed considerably
1749 if(shall_be_written)
1750 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1751 "deactivateFarObjects: Static data "
1752 "changed considerably");
1756 // Add to the block where the object is located in
1757 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1758 // Get or generate the block
1759 MapBlock *block = m_map->emergeBlock(blockpos);
1763 if(block->m_static_objects.m_stored.size() >= 49){
1764 errorstream<<"ServerEnv: Trying to store id="<<obj->getId()
1765 <<" statically but block "<<PP(blockpos)
1766 <<" already contains "
1767 <<block->m_static_objects.m_stored.size()
1768 <<" (over 49) objects."
1769 <<" Forcing delete."<<std::endl;
1770 force_delete = true;
1772 u16 new_id = pending_delete ? id : 0;
1773 // If static counterpart already exists, remove it first.
1774 // This shouldn't happen, but happens rarely for some
1775 // unknown reason. Unsuccessful attempts have been made to
1776 // find said reason.
1777 if(new_id && block->m_static_objects.m_active.find(new_id)){
1778 infostream<<"ServerEnv: WARNING: Performing hack #83274"
1780 block->m_static_objects.remove(new_id);
1782 block->m_static_objects.insert(new_id, s_obj);
1784 // Only mark block as modified if data changed considerably
1785 if(shall_be_written)
1786 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1787 "deactivateFarObjects: Static data "
1788 "changed considerably");
1790 obj->m_static_exists = true;
1791 obj->m_static_block = block->getPos();
1796 errorstream<<"ServerEnv: Could not find or generate "
1797 <<"a block for storing id="<<obj->getId()
1798 <<" statically"<<std::endl;
1805 If known by some client, set pending deactivation.
1806 Otherwise delete it immediately.
1809 if(pending_delete && !force_delete)
1811 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
1812 <<"object id="<<id<<" is known by clients"
1813 <<"; not deleting yet"<<std::endl;
1815 obj->m_pending_deactivation = true;
1819 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
1820 <<"object id="<<id<<" is not known by clients"
1821 <<"; deleting"<<std::endl;
1823 // Tell the object about removal
1824 obj->removingFromEnvironment();
1825 // Deregister in scripting api
1826 scriptapi_rm_object_reference(m_lua, obj);
1828 // Delete active object
1829 if(obj->environmentDeletes())
1831 // Id to be removed from m_active_objects
1832 objects_to_remove.push_back(id);
1835 // Remove references from m_active_objects
1836 for(core::list<u16>::Iterator i = objects_to_remove.begin();
1837 i != objects_to_remove.end(); i++)
1839 m_active_objects.remove(*i);
1846 #include "clientsimpleobject.h"
1852 ClientEnvironment::ClientEnvironment(ClientMap *map, scene::ISceneManager *smgr,
1853 ITextureSource *texturesource, IGameDef *gamedef,
1854 IrrlichtDevice *irr):
1857 m_texturesource(texturesource),
1863 ClientEnvironment::~ClientEnvironment()
1865 // delete active objects
1866 for(core::map<u16, ClientActiveObject*>::Iterator
1867 i = m_active_objects.getIterator();
1868 i.atEnd()==false; i++)
1870 delete i.getNode()->getValue();
1873 for(core::list<ClientSimpleObject*>::Iterator
1874 i = m_simple_objects.begin(); i != m_simple_objects.end(); i++)
1883 Map & ClientEnvironment::getMap()
1888 ClientMap & ClientEnvironment::getClientMap()
1893 void ClientEnvironment::addPlayer(Player *player)
1895 DSTACK(__FUNCTION_NAME);
1897 It is a failure if player is local and there already is a local
1900 assert(!(player->isLocal() == true && getLocalPlayer() != NULL));
1902 Environment::addPlayer(player);
1905 LocalPlayer * ClientEnvironment::getLocalPlayer()
1907 for(core::list<Player*>::Iterator i = m_players.begin();
1908 i != m_players.end(); i++)
1910 Player *player = *i;
1911 if(player->isLocal())
1912 return (LocalPlayer*)player;
1917 void ClientEnvironment::step(float dtime)
1919 DSTACK(__FUNCTION_NAME);
1921 /* Step time of day */
1922 stepTimeOfDay(dtime);
1924 // Get some settings
1925 bool fly_allowed = m_gamedef->checkLocalPrivilege("fly");
1926 bool free_move = fly_allowed && g_settings->getBool("free_move");
1929 LocalPlayer *lplayer = getLocalPlayer();
1931 // collision info queue
1932 core::list<CollisionInfo> player_collisions;
1935 Get the speed the player is going
1937 bool is_climbing = lplayer->is_climbing;
1939 f32 player_speed = lplayer->getSpeed().getLength();
1942 Maximum position increment
1944 //f32 position_max_increment = 0.05*BS;
1945 f32 position_max_increment = 0.1*BS;
1947 // Maximum time increment (for collision detection etc)
1948 // time = distance / speed
1949 f32 dtime_max_increment = 1;
1950 if(player_speed > 0.001)
1951 dtime_max_increment = position_max_increment / player_speed;
1953 // Maximum time increment is 10ms or lower
1954 if(dtime_max_increment > 0.01)
1955 dtime_max_increment = 0.01;
1957 // Don't allow overly huge dtime
1961 f32 dtime_downcount = dtime;
1964 Stuff that has a maximum time increment
1973 if(dtime_downcount > dtime_max_increment)
1975 dtime_part = dtime_max_increment;
1976 dtime_downcount -= dtime_part;
1980 dtime_part = dtime_downcount;
1982 Setting this to 0 (no -=dtime_part) disables an infinite loop
1983 when dtime_part is so small that dtime_downcount -= dtime_part
1986 dtime_downcount = 0;
1994 v3f lplayerpos = lplayer->getPosition();
1997 if(free_move == false && is_climbing == false)
2000 v3f speed = lplayer->getSpeed();
2001 if(lplayer->swimming_up == false)
2002 speed.Y -= 9.81 * BS * dtime_part * 2;
2005 if(lplayer->in_water_stable || lplayer->in_water)
2007 f32 max_down = 2.0*BS;
2008 if(speed.Y < -max_down) speed.Y = -max_down;
2011 if(speed.getLength() > max)
2013 speed = speed / speed.getLength() * max;
2017 lplayer->setSpeed(speed);
2022 This also does collision detection.
2024 lplayer->move(dtime_part, *m_map, position_max_increment,
2025 &player_collisions);
2028 while(dtime_downcount > 0.001);
2030 //std::cout<<"Looped "<<loopcount<<" times."<<std::endl;
2032 for(core::list<CollisionInfo>::Iterator
2033 i = player_collisions.begin();
2034 i != player_collisions.end(); i++)
2036 CollisionInfo &info = *i;
2037 v3f speed_diff = info.new_speed - info.old_speed;;
2038 // Handle only fall damage
2039 // (because otherwise walking against something in fast_move kills you)
2040 if(speed_diff.Y < 0 || info.old_speed.Y >= 0)
2042 // Get rid of other components
2045 f32 pre_factor = 1; // 1 hp per node/s
2046 f32 tolerance = BS*14; // 5 without damage
2047 f32 post_factor = 1; // 1 hp per node/s
2048 if(info.type == COLLISION_NODE)
2050 const ContentFeatures &f = m_gamedef->ndef()->
2051 get(m_map->getNodeNoEx(info.node_p));
2052 // Determine fall damage multiplier
2053 int addp = itemgroup_get(f.groups, "fall_damage_add_percent");
2054 pre_factor = 1.0 + (float)addp/100.0;
2056 float speed = pre_factor * speed_diff.getLength();
2057 if(speed > tolerance)
2059 f32 damage_f = (speed - tolerance)/BS * post_factor;
2060 u16 damage = (u16)(damage_f+0.5);
2062 damageLocalPlayer(damage, true);
2067 A quick draft of lava damage
2069 if(m_lava_hurt_interval.step(dtime, 1.0))
2071 v3f pf = lplayer->getPosition();
2073 // Feet, middle and head
2074 v3s16 p1 = floatToInt(pf + v3f(0, BS*0.1, 0), BS);
2075 MapNode n1 = m_map->getNodeNoEx(p1);
2076 v3s16 p2 = floatToInt(pf + v3f(0, BS*0.8, 0), BS);
2077 MapNode n2 = m_map->getNodeNoEx(p2);
2078 v3s16 p3 = floatToInt(pf + v3f(0, BS*1.6, 0), BS);
2079 MapNode n3 = m_map->getNodeNoEx(p2);
2081 u32 damage_per_second = 0;
2082 damage_per_second = MYMAX(damage_per_second,
2083 m_gamedef->ndef()->get(n1).damage_per_second);
2084 damage_per_second = MYMAX(damage_per_second,
2085 m_gamedef->ndef()->get(n2).damage_per_second);
2086 damage_per_second = MYMAX(damage_per_second,
2087 m_gamedef->ndef()->get(n3).damage_per_second);
2089 if(damage_per_second != 0)
2091 damageLocalPlayer(damage_per_second, true);
2096 Stuff that can be done in an arbitarily large dtime
2098 for(core::list<Player*>::Iterator i = m_players.begin();
2099 i != m_players.end(); i++)
2101 Player *player = *i;
2102 v3f playerpos = player->getPosition();
2105 Handle non-local players
2107 if(player->isLocal() == false)
2110 player->move(dtime, *m_map, 100*BS);
2114 // Update lighting on all players on client
2115 u8 light = LIGHT_MAX;
2118 v3s16 p = player->getLightPosition();
2119 MapNode n = m_map->getNode(p);
2120 light = n.getLightBlend(getDayNightRatio(), m_gamedef->ndef());
2122 catch(InvalidPositionException &e){
2123 light = blend_light(getDayNightRatio(), LIGHT_SUN, 0);
2125 player->light = light;
2129 Step active objects and update lighting of them
2132 bool update_lighting = m_active_object_light_update_interval.step(dtime, 0.21);
2133 for(core::map<u16, ClientActiveObject*>::Iterator
2134 i = m_active_objects.getIterator();
2135 i.atEnd()==false; i++)
2137 ClientActiveObject* obj = i.getNode()->getValue();
2139 obj->step(dtime, this);
2147 v3s16 p = obj->getLightPosition();
2148 MapNode n = m_map->getNode(p);
2149 light = n.getLightBlend(getDayNightRatio(), m_gamedef->ndef());
2151 catch(InvalidPositionException &e){
2152 light = blend_light(getDayNightRatio(), LIGHT_SUN, 0);
2154 obj->updateLight(light);
2159 Step and handle simple objects
2161 for(core::list<ClientSimpleObject*>::Iterator
2162 i = m_simple_objects.begin(); i != m_simple_objects.end();)
2164 ClientSimpleObject *simple = *i;
2165 core::list<ClientSimpleObject*>::Iterator cur = i;
2167 simple->step(dtime);
2168 if(simple->m_to_be_removed){
2170 m_simple_objects.erase(cur);
2175 void ClientEnvironment::addSimpleObject(ClientSimpleObject *simple)
2177 m_simple_objects.push_back(simple);
2180 ClientActiveObject* ClientEnvironment::getActiveObject(u16 id)
2182 core::map<u16, ClientActiveObject*>::Node *n;
2183 n = m_active_objects.find(id);
2186 return n->getValue();
2189 bool isFreeClientActiveObjectId(u16 id,
2190 core::map<u16, ClientActiveObject*> &objects)
2195 for(core::map<u16, ClientActiveObject*>::Iterator
2196 i = objects.getIterator();
2197 i.atEnd()==false; i++)
2199 if(i.getNode()->getKey() == id)
2205 u16 getFreeClientActiveObjectId(
2206 core::map<u16, ClientActiveObject*> &objects)
2211 if(isFreeClientActiveObjectId(new_id, objects))
2221 u16 ClientEnvironment::addActiveObject(ClientActiveObject *object)
2224 if(object->getId() == 0)
2226 u16 new_id = getFreeClientActiveObjectId(m_active_objects);
2229 infostream<<"ClientEnvironment::addActiveObject(): "
2230 <<"no free ids available"<<std::endl;
2234 object->setId(new_id);
2236 if(isFreeClientActiveObjectId(object->getId(), m_active_objects) == false)
2238 infostream<<"ClientEnvironment::addActiveObject(): "
2239 <<"id is not free ("<<object->getId()<<")"<<std::endl;
2243 infostream<<"ClientEnvironment::addActiveObject(): "
2244 <<"added (id="<<object->getId()<<")"<<std::endl;
2245 m_active_objects.insert(object->getId(), object);
2246 object->addToScene(m_smgr, m_texturesource, m_irr);
2247 { // Update lighting immediately
2251 v3s16 p = object->getLightPosition();
2252 MapNode n = m_map->getNode(p);
2253 light = n.getLightBlend(getDayNightRatio(), m_gamedef->ndef());
2255 catch(InvalidPositionException &e){
2256 light = blend_light(getDayNightRatio(), LIGHT_SUN, 0);
2258 object->updateLight(light);
2260 return object->getId();
2263 void ClientEnvironment::addActiveObject(u16 id, u8 type,
2264 const std::string &init_data)
2266 ClientActiveObject* obj =
2267 ClientActiveObject::create(type, m_gamedef, this);
2270 infostream<<"ClientEnvironment::addActiveObject(): "
2271 <<"id="<<id<<" type="<<type<<": Couldn't create object"
2280 obj->initialize(init_data);
2282 catch(SerializationError &e)
2284 errorstream<<"ClientEnvironment::addActiveObject():"
2285 <<" id="<<id<<" type="<<type
2286 <<": SerializationError in initialize(),"
2287 <<" init_data="<<serializeJsonString(init_data)
2291 addActiveObject(obj);
2294 void ClientEnvironment::removeActiveObject(u16 id)
2296 verbosestream<<"ClientEnvironment::removeActiveObject(): "
2297 <<"id="<<id<<std::endl;
2298 ClientActiveObject* obj = getActiveObject(id);
2301 infostream<<"ClientEnvironment::removeActiveObject(): "
2302 <<"id="<<id<<" not found"<<std::endl;
2305 obj->removeFromScene();
2307 m_active_objects.remove(id);
2310 void ClientEnvironment::processActiveObjectMessage(u16 id,
2311 const std::string &data)
2313 ClientActiveObject* obj = getActiveObject(id);
2316 infostream<<"ClientEnvironment::processActiveObjectMessage():"
2317 <<" got message for id="<<id<<", which doesn't exist."
2323 obj->processMessage(data);
2325 catch(SerializationError &e)
2327 errorstream<<"ClientEnvironment::processActiveObjectMessage():"
2328 <<" id="<<id<<" type="<<obj->getType()
2329 <<" SerializationError in processMessage(),"
2330 <<" message="<<serializeJsonString(data)
2336 Callbacks for activeobjects
2339 void ClientEnvironment::damageLocalPlayer(u8 damage, bool handle_hp)
2341 LocalPlayer *lplayer = getLocalPlayer();
2345 if(lplayer->hp > damage)
2346 lplayer->hp -= damage;
2351 ClientEnvEvent event;
2352 event.type = CEE_PLAYER_DAMAGE;
2353 event.player_damage.amount = damage;
2354 event.player_damage.send_to_server = handle_hp;
2355 m_client_event_queue.push_back(event);
2359 Client likes to call these
2362 void ClientEnvironment::getActiveObjects(v3f origin, f32 max_d,
2363 core::array<DistanceSortedActiveObject> &dest)
2365 for(core::map<u16, ClientActiveObject*>::Iterator
2366 i = m_active_objects.getIterator();
2367 i.atEnd()==false; i++)
2369 ClientActiveObject* obj = i.getNode()->getValue();
2371 f32 d = (obj->getPosition() - origin).getLength();
2376 DistanceSortedActiveObject dso(obj, d);
2378 dest.push_back(dso);
2382 ClientEnvEvent ClientEnvironment::getClientEvent()
2384 if(m_client_event_queue.size() == 0)
2386 ClientEnvEvent event;
2387 event.type = CEE_NONE;
2390 return m_client_event_queue.pop_front();
2393 #endif // #ifndef SERVER