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 if(scriptapi_node_on_timer(m_lua,i->first,n,i->second.elapsed))
1091 block->setNodeTimer(i->first,NodeTimer(i->second.timeout,0));
1097 const float abm_interval = 1.0;
1098 if(m_active_block_modifier_interval.step(dtime, abm_interval))
1100 if(m_active_block_interval_overload_skip > 0){
1101 ScopeProfiler sp(g_profiler, "SEnv: ABM overload skips");
1102 m_active_block_interval_overload_skip--;
1105 ScopeProfiler sp(g_profiler, "SEnv: modify in blocks avg /1s", SPT_AVG);
1106 TimeTaker timer("modify in active blocks");
1108 // Initialize handling of ActiveBlockModifiers
1109 ABMHandler abmhandler(m_abms, abm_interval, this, true);
1111 for(core::map<v3s16, bool>::Iterator
1112 i = m_active_blocks.m_list.getIterator();
1113 i.atEnd()==false; i++)
1115 v3s16 p = i.getNode()->getKey();
1117 /*infostream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
1118 <<") being handled"<<std::endl;*/
1120 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1124 // Set current time as timestamp
1125 block->setTimestampNoChangedFlag(m_game_time);
1127 /* Handle ActiveBlockModifiers */
1128 abmhandler.apply(block);
1131 u32 time_ms = timer.stop(true);
1132 u32 max_time_ms = 200;
1133 if(time_ms > max_time_ms){
1134 infostream<<"WARNING: active block modifiers took "
1135 <<time_ms<<"ms (longer than "
1136 <<max_time_ms<<"ms)"<<std::endl;
1137 m_active_block_interval_overload_skip = (time_ms / max_time_ms) + 1;
1142 Step script environment (run global on_step())
1144 scriptapi_environment_step(m_lua, dtime);
1150 ScopeProfiler sp(g_profiler, "SEnv: step act. objs avg", SPT_AVG);
1151 //TimeTaker timer("Step active objects");
1153 g_profiler->avg("SEnv: num of objects", m_active_objects.size());
1155 // This helps the objects to send data at the same time
1156 bool send_recommended = false;
1157 m_send_recommended_timer += dtime;
1158 if(m_send_recommended_timer > getSendRecommendedInterval())
1160 m_send_recommended_timer -= getSendRecommendedInterval();
1161 send_recommended = true;
1164 for(core::map<u16, ServerActiveObject*>::Iterator
1165 i = m_active_objects.getIterator();
1166 i.atEnd()==false; i++)
1168 ServerActiveObject* obj = i.getNode()->getValue();
1169 // Remove non-peaceful mobs on peaceful mode
1170 if(g_settings->getBool("only_peaceful_mobs")){
1171 if(!obj->isPeaceful())
1172 obj->m_removed = true;
1174 // Don't step if is to be removed or stored statically
1175 if(obj->m_removed || obj->m_pending_deactivation)
1178 obj->step(dtime, send_recommended);
1179 // Read messages from object
1180 while(obj->m_messages_out.size() > 0)
1182 m_active_object_messages.push_back(
1183 obj->m_messages_out.pop_front());
1189 Manage active objects
1191 if(m_object_management_interval.step(dtime, 0.5))
1193 ScopeProfiler sp(g_profiler, "SEnv: remove removed objs avg /.5s", SPT_AVG);
1195 Remove objects that satisfy (m_removed && m_known_by_count==0)
1197 removeRemovedObjects();
1201 ServerActiveObject* ServerEnvironment::getActiveObject(u16 id)
1203 core::map<u16, ServerActiveObject*>::Node *n;
1204 n = m_active_objects.find(id);
1207 return n->getValue();
1210 bool isFreeServerActiveObjectId(u16 id,
1211 core::map<u16, ServerActiveObject*> &objects)
1216 for(core::map<u16, ServerActiveObject*>::Iterator
1217 i = objects.getIterator();
1218 i.atEnd()==false; i++)
1220 if(i.getNode()->getKey() == id)
1226 u16 getFreeServerActiveObjectId(
1227 core::map<u16, ServerActiveObject*> &objects)
1232 if(isFreeServerActiveObjectId(new_id, objects))
1242 u16 ServerEnvironment::addActiveObject(ServerActiveObject *object)
1245 u16 id = addActiveObjectRaw(object, true);
1249 bool ServerEnvironment::addActiveObjectAsStatic(ServerActiveObject *obj)
1253 v3f objectpos = obj->getBasePosition();
1255 // The block in which the object resides in
1256 v3s16 blockpos_o = getNodeBlockPos(floatToInt(objectpos, BS));
1259 Update the static data
1262 // Create new static object
1263 std::string staticdata = obj->getStaticData();
1264 StaticObject s_obj(obj->getType(), objectpos, staticdata);
1265 // Add to the block where the object is located in
1266 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1267 // Get or generate the block
1268 MapBlock *block = m_map->emergeBlock(blockpos);
1270 bool succeeded = false;
1274 block->m_static_objects.insert(0, s_obj);
1275 block->raiseModified(MOD_STATE_WRITE_AT_UNLOAD,
1276 "addActiveObjectAsStatic");
1280 infostream<<"ServerEnvironment::addActiveObjectAsStatic: "
1281 <<"Could not find or generate "
1282 <<"a block for storing static object"<<std::endl;
1286 if(obj->environmentDeletes())
1293 Finds out what new objects have been added to
1294 inside a radius around a position
1296 void ServerEnvironment::getAddedActiveObjects(v3s16 pos, s16 radius,
1297 core::map<u16, bool> ¤t_objects,
1298 core::map<u16, bool> &added_objects)
1300 v3f pos_f = intToFloat(pos, BS);
1301 f32 radius_f = radius * BS;
1303 Go through the object list,
1304 - discard m_removed objects,
1305 - discard objects that are too far away,
1306 - discard objects that are found in current_objects.
1307 - add remaining objects to added_objects
1309 for(core::map<u16, ServerActiveObject*>::Iterator
1310 i = m_active_objects.getIterator();
1311 i.atEnd()==false; i++)
1313 u16 id = i.getNode()->getKey();
1315 ServerActiveObject *object = i.getNode()->getValue();
1318 // Discard if removed
1319 if(object->m_removed)
1321 if(object->unlimitedTransferDistance() == false){
1322 // Discard if too far
1323 f32 distance_f = object->getBasePosition().getDistanceFrom(pos_f);
1324 if(distance_f > radius_f)
1327 // Discard if already on current_objects
1328 core::map<u16, bool>::Node *n;
1329 n = current_objects.find(id);
1332 // Add to added_objects
1333 added_objects.insert(id, false);
1338 Finds out what objects have been removed from
1339 inside a radius around a position
1341 void ServerEnvironment::getRemovedActiveObjects(v3s16 pos, s16 radius,
1342 core::map<u16, bool> ¤t_objects,
1343 core::map<u16, bool> &removed_objects)
1345 v3f pos_f = intToFloat(pos, BS);
1346 f32 radius_f = radius * BS;
1348 Go through current_objects; object is removed if:
1349 - object is not found in m_active_objects (this is actually an
1350 error condition; objects should be set m_removed=true and removed
1351 only after all clients have been informed about removal), or
1352 - object has m_removed=true, or
1353 - object is too far away
1355 for(core::map<u16, bool>::Iterator
1356 i = current_objects.getIterator();
1357 i.atEnd()==false; i++)
1359 u16 id = i.getNode()->getKey();
1360 ServerActiveObject *object = getActiveObject(id);
1363 infostream<<"ServerEnvironment::getRemovedActiveObjects():"
1364 <<" object in current_objects is NULL"<<std::endl;
1365 removed_objects.insert(id, false);
1369 if(object->m_removed)
1371 removed_objects.insert(id, false);
1375 // If transfer distance is unlimited, don't remove
1376 if(object->unlimitedTransferDistance())
1379 f32 distance_f = object->getBasePosition().getDistanceFrom(pos_f);
1381 if(distance_f >= radius_f)
1383 removed_objects.insert(id, false);
1391 ActiveObjectMessage ServerEnvironment::getActiveObjectMessage()
1393 if(m_active_object_messages.size() == 0)
1394 return ActiveObjectMessage(0);
1396 return m_active_object_messages.pop_front();
1400 ************ Private methods *************
1403 u16 ServerEnvironment::addActiveObjectRaw(ServerActiveObject *object,
1407 if(object->getId() == 0){
1408 u16 new_id = getFreeServerActiveObjectId(m_active_objects);
1411 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1412 <<"no free ids available"<<std::endl;
1413 if(object->environmentDeletes())
1417 object->setId(new_id);
1420 verbosestream<<"ServerEnvironment::addActiveObjectRaw(): "
1421 <<"supplied with id "<<object->getId()<<std::endl;
1423 if(isFreeServerActiveObjectId(object->getId(), m_active_objects) == false)
1425 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1426 <<"id is not free ("<<object->getId()<<")"<<std::endl;
1427 if(object->environmentDeletes())
1431 /*infostream<<"ServerEnvironment::addActiveObjectRaw(): "
1432 <<"added (id="<<object->getId()<<")"<<std::endl;*/
1434 m_active_objects.insert(object->getId(), object);
1436 verbosestream<<"ServerEnvironment::addActiveObjectRaw(): "
1437 <<"Added id="<<object->getId()<<"; there are now "
1438 <<m_active_objects.size()<<" active objects."
1441 // Register reference in scripting api (must be done before post-init)
1442 scriptapi_add_object_reference(m_lua, object);
1443 // Post-initialize object
1444 object->addedToEnvironment();
1446 // Add static data to block
1447 if(object->isStaticAllowed())
1449 // Add static object to active static list of the block
1450 v3f objectpos = object->getBasePosition();
1451 std::string staticdata = object->getStaticData();
1452 StaticObject s_obj(object->getType(), objectpos, staticdata);
1453 // Add to the block where the object is located in
1454 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1455 MapBlock *block = m_map->getBlockNoCreateNoEx(blockpos);
1458 block->m_static_objects.m_active.insert(object->getId(), s_obj);
1459 object->m_static_exists = true;
1460 object->m_static_block = blockpos;
1463 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1464 "addActiveObjectRaw");
1467 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1468 <<"could not find block for storing id="<<object->getId()
1469 <<" statically"<<std::endl;
1473 return object->getId();
1477 Remove objects that satisfy (m_removed && m_known_by_count==0)
1479 void ServerEnvironment::removeRemovedObjects()
1481 core::list<u16> objects_to_remove;
1482 for(core::map<u16, ServerActiveObject*>::Iterator
1483 i = m_active_objects.getIterator();
1484 i.atEnd()==false; i++)
1486 u16 id = i.getNode()->getKey();
1487 ServerActiveObject* obj = i.getNode()->getValue();
1488 // This shouldn't happen but check it
1491 infostream<<"NULL object found in ServerEnvironment"
1492 <<" while finding removed objects. id="<<id<<std::endl;
1493 // Id to be removed from m_active_objects
1494 objects_to_remove.push_back(id);
1499 We will delete objects that are marked as removed or thatare
1500 waiting for deletion after deactivation
1502 if(obj->m_removed == false && obj->m_pending_deactivation == false)
1506 Delete static data from block if is marked as removed
1508 if(obj->m_static_exists && obj->m_removed)
1510 MapBlock *block = m_map->emergeBlock(obj->m_static_block);
1513 block->m_static_objects.remove(id);
1514 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1515 "removeRemovedObjects");
1516 obj->m_static_exists = false;
1520 // If m_known_by_count > 0, don't actually remove.
1521 if(obj->m_known_by_count > 0)
1524 // Tell the object about removal
1525 obj->removingFromEnvironment();
1526 // Deregister in scripting api
1527 scriptapi_rm_object_reference(m_lua, obj);
1530 if(obj->environmentDeletes())
1532 // Id to be removed from m_active_objects
1533 objects_to_remove.push_back(id);
1535 // Remove references from m_active_objects
1536 for(core::list<u16>::Iterator i = objects_to_remove.begin();
1537 i != objects_to_remove.end(); i++)
1539 m_active_objects.remove(*i);
1543 static void print_hexdump(std::ostream &o, const std::string &data)
1545 const int linelength = 16;
1546 for(int l=0; ; l++){
1547 int i0 = linelength * l;
1548 bool at_end = false;
1549 int thislinelength = linelength;
1550 if(i0 + thislinelength > (int)data.size()){
1551 thislinelength = data.size() - i0;
1554 for(int di=0; di<linelength; di++){
1557 if(di<thislinelength)
1558 snprintf(buf, 4, "%.2x ", data[i]);
1560 snprintf(buf, 4, " ");
1564 for(int di=0; di<thislinelength; di++){
1578 Convert stored objects from blocks near the players to active.
1580 void ServerEnvironment::activateObjects(MapBlock *block)
1584 // Ignore if no stored objects (to not set changed flag)
1585 if(block->m_static_objects.m_stored.size() == 0)
1587 verbosestream<<"ServerEnvironment::activateObjects(): "
1588 <<"activating objects of block "<<PP(block->getPos())
1589 <<" ("<<block->m_static_objects.m_stored.size()
1590 <<" objects)"<<std::endl;
1591 bool large_amount = (block->m_static_objects.m_stored.size() > 49);
1593 errorstream<<"suspiciously large amount of objects detected: "
1594 <<block->m_static_objects.m_stored.size()<<" in "
1595 <<PP(block->getPos())
1596 <<"; removing all of them."<<std::endl;
1597 // Clear stored list
1598 block->m_static_objects.m_stored.clear();
1599 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1600 "stored list cleared in activateObjects due to "
1601 "large amount of objects");
1604 // A list for objects that couldn't be converted to static for some
1605 // reason. They will be stored back.
1606 core::list<StaticObject> new_stored;
1607 // Loop through stored static objects
1608 for(core::list<StaticObject>::Iterator
1609 i = block->m_static_objects.m_stored.begin();
1610 i != block->m_static_objects.m_stored.end(); i++)
1612 /*infostream<<"Server: Creating an active object from "
1613 <<"static data"<<std::endl;*/
1614 StaticObject &s_obj = *i;
1615 // Create an active object from the data
1616 ServerActiveObject *obj = ServerActiveObject::create
1617 (s_obj.type, this, 0, s_obj.pos, s_obj.data);
1618 // If couldn't create object, store static data back.
1621 errorstream<<"ServerEnvironment::activateObjects(): "
1622 <<"failed to create active object from static object "
1623 <<"in block "<<PP(s_obj.pos/BS)
1624 <<" type="<<(int)s_obj.type<<" data:"<<std::endl;
1625 print_hexdump(verbosestream, s_obj.data);
1627 new_stored.push_back(s_obj);
1630 verbosestream<<"ServerEnvironment::activateObjects(): "
1631 <<"activated static object pos="<<PP(s_obj.pos/BS)
1632 <<" type="<<(int)s_obj.type<<std::endl;
1633 // This will also add the object to the active static list
1634 addActiveObjectRaw(obj, false);
1636 // Clear stored list
1637 block->m_static_objects.m_stored.clear();
1638 // Add leftover failed stuff to stored list
1639 for(core::list<StaticObject>::Iterator
1640 i = new_stored.begin();
1641 i != new_stored.end(); i++)
1643 StaticObject &s_obj = *i;
1644 block->m_static_objects.m_stored.push_back(s_obj);
1647 Note: Block hasn't really been modified here.
1648 The objects have just been activated and moved from the stored
1649 static list to the active static list.
1650 As such, the block is essentially the same.
1651 Thus, do not call block->raiseModified(MOD_STATE_WRITE_NEEDED).
1652 Otherwise there would be a huge amount of unnecessary I/O.
1657 Convert objects that are not standing inside active blocks to static.
1659 If m_known_by_count != 0, active object is not deleted, but static
1660 data is still updated.
1662 If force_delete is set, active object is deleted nevertheless. It
1663 shall only be set so in the destructor of the environment.
1665 void ServerEnvironment::deactivateFarObjects(bool force_delete)
1667 core::list<u16> objects_to_remove;
1668 for(core::map<u16, ServerActiveObject*>::Iterator
1669 i = m_active_objects.getIterator();
1670 i.atEnd()==false; i++)
1672 ServerActiveObject* obj = i.getNode()->getValue();
1675 // Do not deactivate if static data creation not allowed
1676 if(!force_delete && !obj->isStaticAllowed())
1679 // If pending deactivation, let removeRemovedObjects() do it
1680 if(!force_delete && obj->m_pending_deactivation)
1683 u16 id = i.getNode()->getKey();
1684 v3f objectpos = obj->getBasePosition();
1686 // The block in which the object resides in
1687 v3s16 blockpos_o = getNodeBlockPos(floatToInt(objectpos, BS));
1689 // If block is active, don't remove
1690 if(!force_delete && m_active_blocks.contains(blockpos_o))
1693 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
1694 <<"deactivating object id="<<id<<" on inactive block "
1695 <<PP(blockpos_o)<<std::endl;
1697 // If known by some client, don't immediately delete.
1698 bool pending_delete = (obj->m_known_by_count > 0 && !force_delete);
1701 Update the static data
1704 if(obj->isStaticAllowed())
1706 // Create new static object
1707 std::string staticdata_new = obj->getStaticData();
1708 StaticObject s_obj(obj->getType(), objectpos, staticdata_new);
1710 bool stays_in_same_block = false;
1711 bool data_changed = true;
1713 if(obj->m_static_exists){
1714 if(obj->m_static_block == blockpos_o)
1715 stays_in_same_block = true;
1717 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
1719 core::map<u16, StaticObject>::Node *n =
1720 block->m_static_objects.m_active.find(id);
1722 StaticObject static_old = n->getValue();
1724 float save_movem = obj->getMinimumSavedMovement();
1726 if(static_old.data == staticdata_new &&
1727 (static_old.pos - objectpos).getLength() < save_movem)
1728 data_changed = false;
1730 errorstream<<"ServerEnvironment::deactivateFarObjects(): "
1731 <<"id="<<id<<" m_static_exists=true but "
1732 <<"static data doesn't actually exist in "
1733 <<PP(obj->m_static_block)<<std::endl;
1737 bool shall_be_written = (!stays_in_same_block || data_changed);
1739 // Delete old static object
1740 if(obj->m_static_exists)
1742 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
1745 block->m_static_objects.remove(id);
1746 obj->m_static_exists = false;
1747 // Only mark block as modified if data changed considerably
1748 if(shall_be_written)
1749 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1750 "deactivateFarObjects: Static data "
1751 "changed considerably");
1755 // Add to the block where the object is located in
1756 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1757 // Get or generate the block
1758 MapBlock *block = m_map->emergeBlock(blockpos);
1762 if(block->m_static_objects.m_stored.size() >= 49){
1763 errorstream<<"ServerEnv: Trying to store id="<<obj->getId()
1764 <<" statically but block "<<PP(blockpos)
1765 <<" already contains "
1766 <<block->m_static_objects.m_stored.size()
1767 <<" (over 49) objects."
1768 <<" Forcing delete."<<std::endl;
1769 force_delete = true;
1771 u16 new_id = pending_delete ? id : 0;
1772 // If static counterpart already exists, remove it first.
1773 // This shouldn't happen, but happens rarely for some
1774 // unknown reason. Unsuccessful attempts have been made to
1775 // find said reason.
1776 if(new_id && block->m_static_objects.m_active.find(new_id)){
1777 infostream<<"ServerEnv: WARNING: Performing hack #83274"
1779 block->m_static_objects.remove(new_id);
1781 block->m_static_objects.insert(new_id, s_obj);
1783 // Only mark block as modified if data changed considerably
1784 if(shall_be_written)
1785 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1786 "deactivateFarObjects: Static data "
1787 "changed considerably");
1789 obj->m_static_exists = true;
1790 obj->m_static_block = block->getPos();
1795 errorstream<<"ServerEnv: Could not find or generate "
1796 <<"a block for storing id="<<obj->getId()
1797 <<" statically"<<std::endl;
1804 If known by some client, set pending deactivation.
1805 Otherwise delete it immediately.
1808 if(pending_delete && !force_delete)
1810 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
1811 <<"object id="<<id<<" is known by clients"
1812 <<"; not deleting yet"<<std::endl;
1814 obj->m_pending_deactivation = true;
1818 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
1819 <<"object id="<<id<<" is not known by clients"
1820 <<"; deleting"<<std::endl;
1822 // Tell the object about removal
1823 obj->removingFromEnvironment();
1824 // Deregister in scripting api
1825 scriptapi_rm_object_reference(m_lua, obj);
1827 // Delete active object
1828 if(obj->environmentDeletes())
1830 // Id to be removed from m_active_objects
1831 objects_to_remove.push_back(id);
1834 // Remove references from m_active_objects
1835 for(core::list<u16>::Iterator i = objects_to_remove.begin();
1836 i != objects_to_remove.end(); i++)
1838 m_active_objects.remove(*i);
1845 #include "clientsimpleobject.h"
1851 ClientEnvironment::ClientEnvironment(ClientMap *map, scene::ISceneManager *smgr,
1852 ITextureSource *texturesource, IGameDef *gamedef,
1853 IrrlichtDevice *irr):
1856 m_texturesource(texturesource),
1862 ClientEnvironment::~ClientEnvironment()
1864 // delete active objects
1865 for(core::map<u16, ClientActiveObject*>::Iterator
1866 i = m_active_objects.getIterator();
1867 i.atEnd()==false; i++)
1869 delete i.getNode()->getValue();
1872 for(core::list<ClientSimpleObject*>::Iterator
1873 i = m_simple_objects.begin(); i != m_simple_objects.end(); i++)
1882 Map & ClientEnvironment::getMap()
1887 ClientMap & ClientEnvironment::getClientMap()
1892 void ClientEnvironment::addPlayer(Player *player)
1894 DSTACK(__FUNCTION_NAME);
1896 It is a failure if player is local and there already is a local
1899 assert(!(player->isLocal() == true && getLocalPlayer() != NULL));
1901 Environment::addPlayer(player);
1904 LocalPlayer * ClientEnvironment::getLocalPlayer()
1906 for(core::list<Player*>::Iterator i = m_players.begin();
1907 i != m_players.end(); i++)
1909 Player *player = *i;
1910 if(player->isLocal())
1911 return (LocalPlayer*)player;
1916 void ClientEnvironment::step(float dtime)
1918 DSTACK(__FUNCTION_NAME);
1920 /* Step time of day */
1921 stepTimeOfDay(dtime);
1923 // Get some settings
1924 bool fly_allowed = m_gamedef->checkLocalPrivilege("fly");
1925 bool free_move = fly_allowed && g_settings->getBool("free_move");
1928 LocalPlayer *lplayer = getLocalPlayer();
1930 // collision info queue
1931 core::list<CollisionInfo> player_collisions;
1934 Get the speed the player is going
1936 bool is_climbing = lplayer->is_climbing;
1938 f32 player_speed = lplayer->getSpeed().getLength();
1941 Maximum position increment
1943 //f32 position_max_increment = 0.05*BS;
1944 f32 position_max_increment = 0.1*BS;
1946 // Maximum time increment (for collision detection etc)
1947 // time = distance / speed
1948 f32 dtime_max_increment = 1;
1949 if(player_speed > 0.001)
1950 dtime_max_increment = position_max_increment / player_speed;
1952 // Maximum time increment is 10ms or lower
1953 if(dtime_max_increment > 0.01)
1954 dtime_max_increment = 0.01;
1956 // Don't allow overly huge dtime
1960 f32 dtime_downcount = dtime;
1963 Stuff that has a maximum time increment
1972 if(dtime_downcount > dtime_max_increment)
1974 dtime_part = dtime_max_increment;
1975 dtime_downcount -= dtime_part;
1979 dtime_part = dtime_downcount;
1981 Setting this to 0 (no -=dtime_part) disables an infinite loop
1982 when dtime_part is so small that dtime_downcount -= dtime_part
1985 dtime_downcount = 0;
1993 v3f lplayerpos = lplayer->getPosition();
1996 if(free_move == false && is_climbing == false)
1999 v3f speed = lplayer->getSpeed();
2000 if(lplayer->swimming_up == false)
2001 speed.Y -= 9.81 * BS * dtime_part * 2;
2004 if(lplayer->in_water_stable || lplayer->in_water)
2006 f32 max_down = 2.0*BS;
2007 if(speed.Y < -max_down) speed.Y = -max_down;
2010 if(speed.getLength() > max)
2012 speed = speed / speed.getLength() * max;
2016 lplayer->setSpeed(speed);
2021 This also does collision detection.
2023 lplayer->move(dtime_part, *m_map, position_max_increment,
2024 &player_collisions);
2027 while(dtime_downcount > 0.001);
2029 //std::cout<<"Looped "<<loopcount<<" times."<<std::endl;
2031 for(core::list<CollisionInfo>::Iterator
2032 i = player_collisions.begin();
2033 i != player_collisions.end(); i++)
2035 CollisionInfo &info = *i;
2036 if(info.t == COLLISION_FALL)
2038 //f32 tolerance = BS*10; // 2 without damage
2039 //f32 tolerance = BS*12; // 3 without damage
2040 f32 tolerance = BS*14; // 5 without damage
2042 if(info.speed > tolerance)
2044 f32 damage_f = (info.speed - tolerance)/BS*factor;
2045 u16 damage = (u16)(damage_f+0.5);
2047 damageLocalPlayer(damage, true);
2053 A quick draft of lava damage
2055 if(m_lava_hurt_interval.step(dtime, 1.0))
2057 v3f pf = lplayer->getPosition();
2059 // Feet, middle and head
2060 v3s16 p1 = floatToInt(pf + v3f(0, BS*0.1, 0), BS);
2061 MapNode n1 = m_map->getNodeNoEx(p1);
2062 v3s16 p2 = floatToInt(pf + v3f(0, BS*0.8, 0), BS);
2063 MapNode n2 = m_map->getNodeNoEx(p2);
2064 v3s16 p3 = floatToInt(pf + v3f(0, BS*1.6, 0), BS);
2065 MapNode n3 = m_map->getNodeNoEx(p2);
2067 u32 damage_per_second = 0;
2068 damage_per_second = MYMAX(damage_per_second,
2069 m_gamedef->ndef()->get(n1).damage_per_second);
2070 damage_per_second = MYMAX(damage_per_second,
2071 m_gamedef->ndef()->get(n2).damage_per_second);
2072 damage_per_second = MYMAX(damage_per_second,
2073 m_gamedef->ndef()->get(n3).damage_per_second);
2075 if(damage_per_second != 0)
2077 damageLocalPlayer(damage_per_second, true);
2082 Stuff that can be done in an arbitarily large dtime
2084 for(core::list<Player*>::Iterator i = m_players.begin();
2085 i != m_players.end(); i++)
2087 Player *player = *i;
2088 v3f playerpos = player->getPosition();
2091 Handle non-local players
2093 if(player->isLocal() == false)
2096 player->move(dtime, *m_map, 100*BS);
2100 // Update lighting on all players on client
2101 u8 light = LIGHT_MAX;
2104 v3s16 p = player->getLightPosition();
2105 MapNode n = m_map->getNode(p);
2106 light = n.getLightBlend(getDayNightRatio(), m_gamedef->ndef());
2108 catch(InvalidPositionException &e){
2109 light = blend_light(getDayNightRatio(), LIGHT_SUN, 0);
2111 player->light = light;
2115 Step active objects and update lighting of them
2118 for(core::map<u16, ClientActiveObject*>::Iterator
2119 i = m_active_objects.getIterator();
2120 i.atEnd()==false; i++)
2122 ClientActiveObject* obj = i.getNode()->getValue();
2124 obj->step(dtime, this);
2126 if(m_active_object_light_update_interval.step(dtime, 0.21))
2132 v3s16 p = obj->getLightPosition();
2133 MapNode n = m_map->getNode(p);
2134 light = n.getLightBlend(getDayNightRatio(), m_gamedef->ndef());
2136 catch(InvalidPositionException &e){
2137 light = blend_light(getDayNightRatio(), LIGHT_SUN, 0);
2139 obj->updateLight(light);
2144 Step and handle simple objects
2146 for(core::list<ClientSimpleObject*>::Iterator
2147 i = m_simple_objects.begin(); i != m_simple_objects.end();)
2149 ClientSimpleObject *simple = *i;
2150 core::list<ClientSimpleObject*>::Iterator cur = i;
2152 simple->step(dtime);
2153 if(simple->m_to_be_removed){
2155 m_simple_objects.erase(cur);
2160 void ClientEnvironment::addSimpleObject(ClientSimpleObject *simple)
2162 m_simple_objects.push_back(simple);
2165 ClientActiveObject* ClientEnvironment::getActiveObject(u16 id)
2167 core::map<u16, ClientActiveObject*>::Node *n;
2168 n = m_active_objects.find(id);
2171 return n->getValue();
2174 bool isFreeClientActiveObjectId(u16 id,
2175 core::map<u16, ClientActiveObject*> &objects)
2180 for(core::map<u16, ClientActiveObject*>::Iterator
2181 i = objects.getIterator();
2182 i.atEnd()==false; i++)
2184 if(i.getNode()->getKey() == id)
2190 u16 getFreeClientActiveObjectId(
2191 core::map<u16, ClientActiveObject*> &objects)
2196 if(isFreeClientActiveObjectId(new_id, objects))
2206 u16 ClientEnvironment::addActiveObject(ClientActiveObject *object)
2209 if(object->getId() == 0)
2211 u16 new_id = getFreeClientActiveObjectId(m_active_objects);
2214 infostream<<"ClientEnvironment::addActiveObject(): "
2215 <<"no free ids available"<<std::endl;
2219 object->setId(new_id);
2221 if(isFreeClientActiveObjectId(object->getId(), m_active_objects) == false)
2223 infostream<<"ClientEnvironment::addActiveObject(): "
2224 <<"id is not free ("<<object->getId()<<")"<<std::endl;
2228 infostream<<"ClientEnvironment::addActiveObject(): "
2229 <<"added (id="<<object->getId()<<")"<<std::endl;
2230 m_active_objects.insert(object->getId(), object);
2231 object->addToScene(m_smgr, m_texturesource, m_irr);
2232 { // Update lighting immediately
2236 v3s16 p = object->getLightPosition();
2237 MapNode n = m_map->getNode(p);
2238 light = n.getLightBlend(getDayNightRatio(), m_gamedef->ndef());
2240 catch(InvalidPositionException &e){
2241 light = blend_light(getDayNightRatio(), LIGHT_SUN, 0);
2243 object->updateLight(light);
2245 return object->getId();
2248 void ClientEnvironment::addActiveObject(u16 id, u8 type,
2249 const std::string &init_data)
2251 ClientActiveObject* obj =
2252 ClientActiveObject::create(type, m_gamedef, this);
2255 infostream<<"ClientEnvironment::addActiveObject(): "
2256 <<"id="<<id<<" type="<<type<<": Couldn't create object"
2265 obj->initialize(init_data);
2267 catch(SerializationError &e)
2269 errorstream<<"ClientEnvironment::addActiveObject():"
2270 <<" id="<<id<<" type="<<type
2271 <<": SerializationError in initialize(),"
2272 <<" init_data="<<serializeJsonString(init_data)
2276 addActiveObject(obj);
2279 void ClientEnvironment::removeActiveObject(u16 id)
2281 verbosestream<<"ClientEnvironment::removeActiveObject(): "
2282 <<"id="<<id<<std::endl;
2283 ClientActiveObject* obj = getActiveObject(id);
2286 infostream<<"ClientEnvironment::removeActiveObject(): "
2287 <<"id="<<id<<" not found"<<std::endl;
2290 obj->removeFromScene();
2292 m_active_objects.remove(id);
2295 void ClientEnvironment::processActiveObjectMessage(u16 id,
2296 const std::string &data)
2298 ClientActiveObject* obj = getActiveObject(id);
2301 infostream<<"ClientEnvironment::processActiveObjectMessage():"
2302 <<" got message for id="<<id<<", which doesn't exist."
2308 obj->processMessage(data);
2310 catch(SerializationError &e)
2312 errorstream<<"ClientEnvironment::processActiveObjectMessage():"
2313 <<" id="<<id<<" type="<<obj->getType()
2314 <<" SerializationError in processMessage(),"
2315 <<" message="<<serializeJsonString(data)
2321 Callbacks for activeobjects
2324 void ClientEnvironment::damageLocalPlayer(u8 damage, bool handle_hp)
2326 LocalPlayer *lplayer = getLocalPlayer();
2330 if(lplayer->hp > damage)
2331 lplayer->hp -= damage;
2336 ClientEnvEvent event;
2337 event.type = CEE_PLAYER_DAMAGE;
2338 event.player_damage.amount = damage;
2339 event.player_damage.send_to_server = handle_hp;
2340 m_client_event_queue.push_back(event);
2344 Client likes to call these
2347 void ClientEnvironment::getActiveObjects(v3f origin, f32 max_d,
2348 core::array<DistanceSortedActiveObject> &dest)
2350 for(core::map<u16, ClientActiveObject*>::Iterator
2351 i = m_active_objects.getIterator();
2352 i.atEnd()==false; i++)
2354 ClientActiveObject* obj = i.getNode()->getValue();
2356 f32 d = (obj->getPosition() - origin).getLength();
2361 DistanceSortedActiveObject dso(obj, d);
2363 dest.push_back(dso);
2367 ClientEnvEvent ClientEnvironment::getClientEvent()
2369 if(m_client_event_queue.size() == 0)
2371 ClientEnvEvent event;
2372 event.type = CEE_NONE;
2375 return m_client_event_queue.pop_front();
2378 #endif // #ifndef SERVER