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 General Public License as published by
7 the Free Software Foundation; either version 2 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 General Public License for more details.
15 You should have received a copy of the GNU 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"
43 #include "daynightratio.h"
45 #define PP(x) "("<<(x).X<<","<<(x).Y<<","<<(x).Z<<")"
47 Environment::Environment():
49 m_time_of_day_f(9000./24000),
50 m_time_of_day_speed(0),
55 Environment::~Environment()
58 for(core::list<Player*>::Iterator i = m_players.begin();
59 i != m_players.end(); i++)
65 void Environment::addPlayer(Player *player)
67 DSTACK(__FUNCTION_NAME);
69 Check that peer_ids are unique.
70 Also check that names are unique.
71 Exception: there can be multiple players with peer_id=0
73 // If peer id is non-zero, it has to be unique.
74 if(player->peer_id != 0)
75 assert(getPlayer(player->peer_id) == NULL);
76 // Name has to be unique.
77 assert(getPlayer(player->getName()) == NULL);
79 m_players.push_back(player);
82 void Environment::removePlayer(u16 peer_id)
84 DSTACK(__FUNCTION_NAME);
86 for(core::list<Player*>::Iterator i = m_players.begin();
87 i != m_players.end(); i++)
90 if(player->peer_id != peer_id)
95 // See if there is an another one
96 // (shouldn't be, but just to be sure)
101 Player * Environment::getPlayer(u16 peer_id)
103 for(core::list<Player*>::Iterator i = m_players.begin();
104 i != m_players.end(); i++)
107 if(player->peer_id == peer_id)
113 Player * Environment::getPlayer(const char *name)
115 for(core::list<Player*>::Iterator i = m_players.begin();
116 i != m_players.end(); i++)
119 if(strcmp(player->getName(), name) == 0)
125 Player * Environment::getRandomConnectedPlayer()
127 core::list<Player*> connected_players = getPlayers(true);
128 u32 chosen_one = myrand() % connected_players.size();
130 for(core::list<Player*>::Iterator
131 i = connected_players.begin();
132 i != connected_players.end(); i++)
144 Player * Environment::getNearestConnectedPlayer(v3f pos)
146 core::list<Player*> connected_players = getPlayers(true);
148 Player *nearest_player = NULL;
149 for(core::list<Player*>::Iterator
150 i = connected_players.begin();
151 i != connected_players.end(); i++)
154 f32 d = player->getPosition().getDistanceFrom(pos);
155 if(d < nearest_d || nearest_player == NULL)
158 nearest_player = player;
161 return nearest_player;
164 core::list<Player*> Environment::getPlayers()
169 core::list<Player*> Environment::getPlayers(bool ignore_disconnected)
171 core::list<Player*> newlist;
172 for(core::list<Player*>::Iterator
173 i = m_players.begin();
174 i != m_players.end(); i++)
178 if(ignore_disconnected)
180 // Ignore disconnected players
181 if(player->peer_id == 0)
185 newlist.push_back(player);
190 void Environment::printPlayers(std::ostream &o)
192 o<<"Players in environment:"<<std::endl;
193 for(core::list<Player*>::Iterator i = m_players.begin();
194 i != m_players.end(); i++)
197 o<<"Player peer_id="<<player->peer_id<<std::endl;
201 u32 Environment::getDayNightRatio()
203 return time_to_daynight_ratio(m_time_of_day);
206 void Environment::stepTimeOfDay(float dtime)
208 m_time_counter += dtime;
209 f32 speed = m_time_of_day_speed * 24000./(24.*3600);
210 u32 units = (u32)(m_time_counter*speed);
211 m_time_counter -= (f32)units / speed;
215 if(m_time_of_day + units >= 24000)
217 m_time_of_day = (m_time_of_day + units) % 24000;
219 m_time_of_day_f = (float)m_time_of_day / 24000.0;
222 m_time_of_day_f += m_time_of_day_speed/24/3600*dtime;
223 if(m_time_of_day_f > 1.0)
224 m_time_of_day_f -= 1.0;
225 if(m_time_of_day_f < 0.0)
226 m_time_of_day_f += 1.0;
234 ABMWithState::ABMWithState(ActiveBlockModifier *abm_):
238 // Initialize timer to random value to spread processing
239 float itv = abm->getTriggerInterval();
240 itv = MYMAX(0.001, itv); // No less than 1ms
241 int minval = MYMAX(-0.51*itv, -60); // Clamp to
242 int maxval = MYMIN(0.51*itv, 60); // +-60 seconds
243 timer = myrand_range(minval, maxval);
250 void fillRadiusBlock(v3s16 p0, s16 r, core::map<v3s16, bool> &list)
253 for(p.X=p0.X-r; p.X<=p0.X+r; p.X++)
254 for(p.Y=p0.Y-r; p.Y<=p0.Y+r; p.Y++)
255 for(p.Z=p0.Z-r; p.Z<=p0.Z+r; p.Z++)
262 void ActiveBlockList::update(core::list<v3s16> &active_positions,
264 core::map<v3s16, bool> &blocks_removed,
265 core::map<v3s16, bool> &blocks_added)
270 core::map<v3s16, bool> newlist;
271 for(core::list<v3s16>::Iterator i = active_positions.begin();
272 i != active_positions.end(); i++)
274 fillRadiusBlock(*i, radius, newlist);
278 Find out which blocks on the old list are not on the new list
280 // Go through old list
281 for(core::map<v3s16, bool>::Iterator i = m_list.getIterator();
282 i.atEnd()==false; i++)
284 v3s16 p = i.getNode()->getKey();
285 // If not on new list, it's been removed
286 if(newlist.find(p) == NULL)
287 blocks_removed.insert(p, true);
291 Find out which blocks on the new list are not on the old list
293 // Go through new list
294 for(core::map<v3s16, bool>::Iterator i = newlist.getIterator();
295 i.atEnd()==false; i++)
297 v3s16 p = i.getNode()->getKey();
298 // If not on old list, it's been added
299 if(m_list.find(p) == NULL)
300 blocks_added.insert(p, true);
307 for(core::map<v3s16, bool>::Iterator i = newlist.getIterator();
308 i.atEnd()==false; i++)
310 v3s16 p = i.getNode()->getKey();
311 m_list.insert(p, true);
319 ServerEnvironment::ServerEnvironment(ServerMap *map, lua_State *L,
320 IGameDef *gamedef, IBackgroundBlockEmerger *emerger):
325 m_random_spawn_timer(3),
326 m_send_recommended_timer(0),
328 m_game_time_fraction_counter(0)
332 ServerEnvironment::~ServerEnvironment()
334 // Clear active block list.
335 // This makes the next one delete all active objects.
336 m_active_blocks.clear();
338 // Convert all objects to static and delete the active objects
339 deactivateFarObjects(true);
344 // Delete ActiveBlockModifiers
345 for(core::list<ABMWithState>::Iterator
346 i = m_abms.begin(); i != m_abms.end(); i++){
351 void ServerEnvironment::serializePlayers(const std::string &savedir)
353 std::string players_path = savedir + "/players";
354 fs::CreateDir(players_path);
356 core::map<Player*, bool> saved_players;
358 std::vector<fs::DirListNode> player_files = fs::GetDirListing(players_path);
359 for(u32 i=0; i<player_files.size(); i++)
361 if(player_files[i].dir)
364 // Full path to this file
365 std::string path = players_path + "/" + player_files[i].name;
367 //infostream<<"Checking player file "<<path<<std::endl;
369 // Load player to see what is its name
370 RemotePlayer testplayer(m_gamedef);
372 // Open file and deserialize
373 std::ifstream is(path.c_str(), std::ios_base::binary);
374 if(is.good() == false)
376 infostream<<"Failed to read "<<path<<std::endl;
379 testplayer.deSerialize(is);
382 //infostream<<"Loaded test player with name "<<testplayer.getName()<<std::endl;
384 // Search for the player
385 std::string playername = testplayer.getName();
386 Player *player = getPlayer(playername.c_str());
389 infostream<<"Didn't find matching player, ignoring file "<<path<<std::endl;
393 //infostream<<"Found matching player, overwriting."<<std::endl;
395 // OK, found. Save player there.
397 // Open file and serialize
398 std::ofstream os(path.c_str(), std::ios_base::binary);
399 if(os.good() == false)
401 infostream<<"Failed to overwrite "<<path<<std::endl;
404 player->serialize(os);
405 saved_players.insert(player, true);
409 for(core::list<Player*>::Iterator i = m_players.begin();
410 i != m_players.end(); i++)
413 if(saved_players.find(player) != NULL)
415 /*infostream<<"Player "<<player->getName()
416 <<" was already saved."<<std::endl;*/
419 std::string playername = player->getName();
420 // Don't save unnamed player
423 //infostream<<"Not saving unnamed player."<<std::endl;
429 if(string_allowed(playername, PLAYERNAME_ALLOWED_CHARS) == false)
430 playername = "player";
431 std::string path = players_path + "/" + playername;
433 for(u32 i=0; i<1000; i++)
435 if(fs::PathExists(path) == false)
440 path = players_path + "/" + playername + itos(i);
444 infostream<<"Didn't find free file for player"<<std::endl;
449 /*infostream<<"Saving player "<<player->getName()<<" to "
451 // Open file and serialize
452 std::ofstream os(path.c_str(), std::ios_base::binary);
453 if(os.good() == false)
455 infostream<<"Failed to overwrite "<<path<<std::endl;
458 player->serialize(os);
459 saved_players.insert(player, true);
463 //infostream<<"Saved "<<saved_players.size()<<" players."<<std::endl;
466 void ServerEnvironment::deSerializePlayers(const std::string &savedir)
468 std::string players_path = savedir + "/players";
470 core::map<Player*, bool> saved_players;
472 std::vector<fs::DirListNode> player_files = fs::GetDirListing(players_path);
473 for(u32 i=0; i<player_files.size(); i++)
475 if(player_files[i].dir)
478 // Full path to this file
479 std::string path = players_path + "/" + player_files[i].name;
481 //infostream<<"Checking player file "<<path<<std::endl;
483 // Load player to see what is its name
484 RemotePlayer testplayer(m_gamedef);
486 // Open file and deserialize
487 std::ifstream is(path.c_str(), std::ios_base::binary);
488 if(is.good() == false)
490 infostream<<"Failed to read "<<path<<std::endl;
493 testplayer.deSerialize(is);
496 if(!string_allowed(testplayer.getName(), PLAYERNAME_ALLOWED_CHARS))
498 infostream<<"Not loading player with invalid name: "
499 <<testplayer.getName()<<std::endl;
502 /*infostream<<"Loaded test player with name "<<testplayer.getName()
505 // Search for the player
506 std::string playername = testplayer.getName();
507 Player *player = getPlayer(playername.c_str());
508 bool newplayer = false;
511 //infostream<<"Is a new player"<<std::endl;
512 player = new RemotePlayer(m_gamedef);
518 verbosestream<<"Reading player "<<testplayer.getName()<<" from "
520 // Open file and deserialize
521 std::ifstream is(path.c_str(), std::ios_base::binary);
522 if(is.good() == false)
524 infostream<<"Failed to read "<<path<<std::endl;
527 player->deSerialize(is);
537 void ServerEnvironment::saveMeta(const std::string &savedir)
539 std::string path = savedir + "/env_meta.txt";
541 // Open file and serialize
542 std::ofstream os(path.c_str(), std::ios_base::binary);
543 if(os.good() == false)
545 infostream<<"ServerEnvironment::saveMeta(): Failed to open "
547 throw SerializationError("Couldn't save env meta");
551 args.setU64("game_time", m_game_time);
552 args.setU64("time_of_day", getTimeOfDay());
557 void ServerEnvironment::loadMeta(const std::string &savedir)
559 std::string path = savedir + "/env_meta.txt";
561 // Open file and deserialize
562 std::ifstream is(path.c_str(), std::ios_base::binary);
563 if(is.good() == false)
565 infostream<<"ServerEnvironment::loadMeta(): Failed to open "
567 throw SerializationError("Couldn't load env meta");
575 throw SerializationError
576 ("ServerEnvironment::loadMeta(): EnvArgsEnd not found");
578 std::getline(is, line);
579 std::string trimmedline = trim(line);
580 if(trimmedline == "EnvArgsEnd")
582 args.parseConfigLine(line);
586 m_game_time = args.getU64("game_time");
587 }catch(SettingNotFoundException &e){
588 // Getting this is crucial, otherwise timestamps are useless
589 throw SerializationError("Couldn't load env meta game_time");
593 m_time_of_day = args.getU64("time_of_day");
594 }catch(SettingNotFoundException &e){
595 // This is not as important
596 m_time_of_day = 9000;
602 ActiveBlockModifier *abm;
604 std::set<content_t> required_neighbors;
610 ServerEnvironment *m_env;
611 std::map<content_t, std::list<ActiveABM> > m_aabms;
613 ABMHandler(core::list<ABMWithState> &abms,
614 float dtime_s, ServerEnvironment *env,
620 INodeDefManager *ndef = env->getGameDef()->ndef();
621 for(core::list<ABMWithState>::Iterator
622 i = abms.begin(); i != abms.end(); i++){
623 ActiveBlockModifier *abm = i->abm;
624 float trigger_interval = abm->getTriggerInterval();
625 if(trigger_interval < 0.001)
626 trigger_interval = 0.001;
627 float actual_interval = dtime_s;
630 if(i->timer < trigger_interval)
632 i->timer -= trigger_interval;
633 actual_interval = trigger_interval;
637 float intervals = actual_interval / trigger_interval;
638 float chance = abm->getTriggerChance();
641 aabm.chance = 1.0 / pow((float)1.0/chance, (float)intervals);
645 std::set<std::string> required_neighbors_s
646 = abm->getRequiredNeighbors();
647 for(std::set<std::string>::iterator
648 i = required_neighbors_s.begin();
649 i != required_neighbors_s.end(); i++){
650 content_t c = ndef->getId(*i);
651 if(c == CONTENT_IGNORE)
653 aabm.required_neighbors.insert(c);
656 std::set<std::string> contents_s = abm->getTriggerContents();
657 for(std::set<std::string>::iterator
658 i = contents_s.begin(); i != contents_s.end(); i++){
659 content_t c = ndef->getId(*i);
660 if(c == CONTENT_IGNORE)
662 std::map<content_t, std::list<ActiveABM> >::iterator j;
664 if(j == m_aabms.end()){
665 std::list<ActiveABM> aabmlist;
666 m_aabms[c] = aabmlist;
669 j->second.push_back(aabm);
673 void apply(MapBlock *block)
678 ServerMap *map = &m_env->getServerMap();
681 for(p0.X=0; p0.X<MAP_BLOCKSIZE; p0.X++)
682 for(p0.Y=0; p0.Y<MAP_BLOCKSIZE; p0.Y++)
683 for(p0.Z=0; p0.Z<MAP_BLOCKSIZE; p0.Z++)
685 MapNode n = block->getNodeNoEx(p0);
686 content_t c = n.getContent();
687 v3s16 p = p0 + block->getPosRelative();
689 std::map<content_t, std::list<ActiveABM> >::iterator j;
691 if(j == m_aabms.end())
694 for(std::list<ActiveABM>::iterator
695 i = j->second.begin(); i != j->second.end(); i++)
697 if(myrand() % i->chance != 0)
701 if(!i->required_neighbors.empty())
704 for(p1.X = p.X-1; p1.X <= p.X+1; p1.X++)
705 for(p1.Y = p.Y-1; p1.Y <= p.Y+1; p1.Y++)
706 for(p1.Z = p.Z-1; p1.Z <= p.Z+1; p1.Z++)
710 MapNode n = map->getNodeNoEx(p1);
711 content_t c = n.getContent();
712 std::set<content_t>::const_iterator k;
713 k = i->required_neighbors.find(c);
714 if(k != i->required_neighbors.end()){
718 // No required neighbor found
723 // Find out how many objects the block contains
724 u32 active_object_count = block->m_static_objects.m_active.size();
725 // Find out how many objects this and all the neighbors contain
726 u32 active_object_count_wider = 0;
727 for(s16 x=-1; x<=1; x++)
728 for(s16 y=-1; y<=1; y++)
729 for(s16 z=-1; z<=1; z++)
731 MapBlock *block2 = map->getBlockNoCreateNoEx(
732 block->getPos() + v3s16(x,y,z));
735 active_object_count_wider +=
736 block2->m_static_objects.m_active.size()
737 + block2->m_static_objects.m_stored.size();
740 // Call all the trigger variations
741 i->abm->trigger(m_env, p, n);
742 i->abm->trigger(m_env, p, n,
743 active_object_count, active_object_count_wider);
749 void ServerEnvironment::activateBlock(MapBlock *block, u32 additional_dtime)
751 // Get time difference
753 u32 stamp = block->getTimestamp();
754 if(m_game_time > stamp && stamp != BLOCK_TIMESTAMP_UNDEFINED)
755 dtime_s = m_game_time - block->getTimestamp();
756 dtime_s += additional_dtime;
758 /*infostream<<"ServerEnvironment::activateBlock(): block timestamp: "
759 <<stamp<<", game time: "<<m_game_time<<std::endl;*/
761 // Set current time as timestamp
762 block->setTimestampNoChangedFlag(m_game_time);
764 /*infostream<<"ServerEnvironment::activateBlock(): block is "
765 <<dtime_s<<" seconds old."<<std::endl;*/
767 // Activate stored objects
768 activateObjects(block);
771 bool changed = block->m_node_metadata->step((float)dtime_s);
775 event.type = MEET_BLOCK_NODE_METADATA_CHANGED;
776 event.p = block->getPos();
777 m_map->dispatchEvent(&event);
779 block->raiseModified(MOD_STATE_WRITE_NEEDED,
780 "node metadata modified in activateBlock");
783 /* Handle ActiveBlockModifiers */
784 ABMHandler abmhandler(m_abms, dtime_s, this, false);
785 abmhandler.apply(block);
788 void ServerEnvironment::addActiveBlockModifier(ActiveBlockModifier *abm)
790 m_abms.push_back(ABMWithState(abm));
793 std::set<u16> ServerEnvironment::getObjectsInsideRadius(v3f pos, float radius)
795 std::set<u16> objects;
796 for(core::map<u16, ServerActiveObject*>::Iterator
797 i = m_active_objects.getIterator();
798 i.atEnd()==false; i++)
800 ServerActiveObject* obj = i.getNode()->getValue();
801 u16 id = i.getNode()->getKey();
802 v3f objectpos = obj->getBasePosition();
803 if(objectpos.getDistanceFrom(pos) > radius)
810 void ServerEnvironment::clearAllObjects()
812 infostream<<"ServerEnvironment::clearAllObjects(): "
813 <<"Removing all active objects"<<std::endl;
814 core::list<u16> objects_to_remove;
815 for(core::map<u16, ServerActiveObject*>::Iterator
816 i = m_active_objects.getIterator();
817 i.atEnd()==false; i++)
819 ServerActiveObject* obj = i.getNode()->getValue();
820 if(obj->getType() == ACTIVEOBJECT_TYPE_PLAYER)
822 u16 id = i.getNode()->getKey();
823 v3f objectpos = obj->getBasePosition();
824 // Delete static object if block is loaded
825 if(obj->m_static_exists){
826 MapBlock *block = m_map->getBlockNoCreateNoEx(obj->m_static_block);
828 block->m_static_objects.remove(id);
829 block->raiseModified(MOD_STATE_WRITE_NEEDED,
831 obj->m_static_exists = false;
834 // If known by some client, don't delete immediately
835 if(obj->m_known_by_count > 0){
836 obj->m_pending_deactivation = true;
837 obj->m_removed = true;
841 // Tell the object about removal
842 obj->removingFromEnvironment();
843 // Deregister in scripting api
844 scriptapi_rm_object_reference(m_lua, obj);
846 // Delete active object
847 if(obj->environmentDeletes())
849 // Id to be removed from m_active_objects
850 objects_to_remove.push_back(id);
852 // Remove references from m_active_objects
853 for(core::list<u16>::Iterator i = objects_to_remove.begin();
854 i != objects_to_remove.end(); i++)
856 m_active_objects.remove(*i);
859 core::list<v3s16> loadable_blocks;
860 infostream<<"ServerEnvironment::clearAllObjects(): "
861 <<"Listing all loadable blocks"<<std::endl;
862 m_map->listAllLoadableBlocks(loadable_blocks);
863 infostream<<"ServerEnvironment::clearAllObjects(): "
864 <<"Done listing all loadable blocks: "
865 <<loadable_blocks.size()
866 <<", now clearing"<<std::endl;
867 u32 report_interval = loadable_blocks.size() / 10;
868 u32 num_blocks_checked = 0;
869 u32 num_blocks_cleared = 0;
870 u32 num_objs_cleared = 0;
871 for(core::list<v3s16>::Iterator i = loadable_blocks.begin();
872 i != loadable_blocks.end(); i++)
875 MapBlock *block = m_map->emergeBlock(p, false);
877 errorstream<<"ServerEnvironment::clearAllObjects(): "
878 <<"Failed to emerge block "<<PP(p)<<std::endl;
881 u32 num_stored = block->m_static_objects.m_stored.size();
882 u32 num_active = block->m_static_objects.m_active.size();
883 if(num_stored != 0 || num_active != 0){
884 block->m_static_objects.m_stored.clear();
885 block->m_static_objects.m_active.clear();
886 block->raiseModified(MOD_STATE_WRITE_NEEDED,
888 num_objs_cleared += num_stored + num_active;
889 num_blocks_cleared++;
891 num_blocks_checked++;
893 if(num_blocks_checked % report_interval == 0){
894 float percent = 100.0 * (float)num_blocks_checked /
895 loadable_blocks.size();
896 infostream<<"ServerEnvironment::clearAllObjects(): "
897 <<"Cleared "<<num_objs_cleared<<" objects"
898 <<" in "<<num_blocks_cleared<<" blocks ("
899 <<percent<<"%)"<<std::endl;
902 infostream<<"ServerEnvironment::clearAllObjects(): "
903 <<"Finished: Cleared "<<num_objs_cleared<<" objects"
904 <<" in "<<num_blocks_cleared<<" blocks"<<std::endl;
907 void ServerEnvironment::step(float dtime)
909 DSTACK(__FUNCTION_NAME);
911 //TimeTaker timer("ServerEnv step");
913 /* Step time of day */
914 stepTimeOfDay(dtime);
920 m_game_time_fraction_counter += dtime;
921 u32 inc_i = (u32)m_game_time_fraction_counter;
922 m_game_time += inc_i;
923 m_game_time_fraction_counter -= (float)inc_i;
930 ScopeProfiler sp(g_profiler, "SEnv: handle players avg", SPT_AVG);
931 for(core::list<Player*>::Iterator i = m_players.begin();
932 i != m_players.end(); i++)
936 // Ignore disconnected players
937 if(player->peer_id == 0)
940 v3f playerpos = player->getPosition();
943 player->move(dtime, *m_map, 100*BS);
948 Manage active block list
950 if(m_active_blocks_management_interval.step(dtime, 2.0))
952 ScopeProfiler sp(g_profiler, "SEnv: manage act. block list avg /2s", SPT_AVG);
954 Get player block positions
956 core::list<v3s16> players_blockpos;
957 for(core::list<Player*>::Iterator
958 i = m_players.begin();
959 i != m_players.end(); i++)
962 // Ignore disconnected players
963 if(player->peer_id == 0)
965 v3s16 blockpos = getNodeBlockPos(
966 floatToInt(player->getPosition(), BS));
967 players_blockpos.push_back(blockpos);
971 Update list of active blocks, collecting changes
973 const s16 active_block_range = g_settings->getS16("active_block_range");
974 core::map<v3s16, bool> blocks_removed;
975 core::map<v3s16, bool> blocks_added;
976 m_active_blocks.update(players_blockpos, active_block_range,
977 blocks_removed, blocks_added);
980 Handle removed blocks
983 // Convert active objects that are no more in active blocks to static
984 deactivateFarObjects(false);
986 for(core::map<v3s16, bool>::Iterator
987 i = blocks_removed.getIterator();
988 i.atEnd()==false; i++)
990 v3s16 p = i.getNode()->getKey();
992 /*infostream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
993 <<") became inactive"<<std::endl;*/
995 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
999 // Set current time as timestamp (and let it set ChangedFlag)
1000 block->setTimestamp(m_game_time);
1007 for(core::map<v3s16, bool>::Iterator
1008 i = blocks_added.getIterator();
1009 i.atEnd()==false; i++)
1011 v3s16 p = i.getNode()->getKey();
1013 /*infostream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
1014 <<") became active"<<std::endl;*/
1016 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1018 // Block needs to be fetched first
1019 m_emerger->queueBlockEmerge(p, false);
1020 m_active_blocks.m_list.remove(p);
1024 activateBlock(block);
1029 Mess around in active blocks
1031 if(m_active_blocks_nodemetadata_interval.step(dtime, 1.0))
1033 ScopeProfiler sp(g_profiler, "SEnv: mess in act. blocks avg /1s", SPT_AVG);
1037 for(core::map<v3s16, bool>::Iterator
1038 i = m_active_blocks.m_list.getIterator();
1039 i.atEnd()==false; i++)
1041 v3s16 p = i.getNode()->getKey();
1043 /*infostream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
1044 <<") being handled"<<std::endl;*/
1046 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1050 // Reset block usage timer
1051 block->resetUsageTimer();
1053 // Set current time as timestamp
1054 block->setTimestampNoChangedFlag(m_game_time);
1055 // If time has changed much from the one on disk,
1056 // set block to be saved when it is unloaded
1057 if(block->getTimestamp() > block->getDiskTimestamp() + 60)
1058 block->raiseModified(MOD_STATE_WRITE_AT_UNLOAD,
1059 "Timestamp older than 60s (step)");
1061 // Run node metadata
1062 bool changed = block->m_node_metadata->step(dtime);
1066 event.type = MEET_BLOCK_NODE_METADATA_CHANGED;
1068 m_map->dispatchEvent(&event);
1070 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1071 "node metadata modified in step");
1076 const float abm_interval = 1.0;
1077 if(m_active_block_modifier_interval.step(dtime, abm_interval))
1079 ScopeProfiler sp(g_profiler, "SEnv: modify in blocks avg /1s", SPT_AVG);
1080 TimeTaker timer("modify in active blocks");
1082 // Initialize handling of ActiveBlockModifiers
1083 ABMHandler abmhandler(m_abms, abm_interval, this, true);
1085 for(core::map<v3s16, bool>::Iterator
1086 i = m_active_blocks.m_list.getIterator();
1087 i.atEnd()==false; i++)
1089 v3s16 p = i.getNode()->getKey();
1091 /*infostream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
1092 <<") being handled"<<std::endl;*/
1094 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1098 // Set current time as timestamp
1099 block->setTimestampNoChangedFlag(m_game_time);
1101 /* Handle ActiveBlockModifiers */
1102 abmhandler.apply(block);
1105 u32 time_ms = timer.stop(true);
1106 u32 max_time_ms = 200;
1107 if(time_ms > max_time_ms){
1108 infostream<<"WARNING: active block modifiers took "
1109 <<time_ms<<"ms (longer than "
1110 <<max_time_ms<<"ms)"<<std::endl;
1115 Step script environment (run global on_step())
1117 scriptapi_environment_step(m_lua, dtime);
1123 ScopeProfiler sp(g_profiler, "SEnv: step act. objs avg", SPT_AVG);
1124 //TimeTaker timer("Step active objects");
1126 g_profiler->avg("SEnv: num of objects", m_active_objects.size());
1128 // This helps the objects to send data at the same time
1129 bool send_recommended = false;
1130 m_send_recommended_timer += dtime;
1131 if(m_send_recommended_timer > getSendRecommendedInterval())
1133 m_send_recommended_timer -= getSendRecommendedInterval();
1134 send_recommended = true;
1137 for(core::map<u16, ServerActiveObject*>::Iterator
1138 i = m_active_objects.getIterator();
1139 i.atEnd()==false; i++)
1141 ServerActiveObject* obj = i.getNode()->getValue();
1142 // Remove non-peaceful mobs on peaceful mode
1143 if(g_settings->getBool("only_peaceful_mobs")){
1144 if(!obj->isPeaceful())
1145 obj->m_removed = true;
1147 // Don't step if is to be removed or stored statically
1148 if(obj->m_removed || obj->m_pending_deactivation)
1151 obj->step(dtime, send_recommended);
1152 // Read messages from object
1153 while(obj->m_messages_out.size() > 0)
1155 m_active_object_messages.push_back(
1156 obj->m_messages_out.pop_front());
1162 Manage active objects
1164 if(m_object_management_interval.step(dtime, 0.5))
1166 ScopeProfiler sp(g_profiler, "SEnv: remove removed objs avg /.5s", SPT_AVG);
1168 Remove objects that satisfy (m_removed && m_known_by_count==0)
1170 removeRemovedObjects();
1174 ServerActiveObject* ServerEnvironment::getActiveObject(u16 id)
1176 core::map<u16, ServerActiveObject*>::Node *n;
1177 n = m_active_objects.find(id);
1180 return n->getValue();
1183 bool isFreeServerActiveObjectId(u16 id,
1184 core::map<u16, ServerActiveObject*> &objects)
1189 for(core::map<u16, ServerActiveObject*>::Iterator
1190 i = objects.getIterator();
1191 i.atEnd()==false; i++)
1193 if(i.getNode()->getKey() == id)
1199 u16 getFreeServerActiveObjectId(
1200 core::map<u16, ServerActiveObject*> &objects)
1205 if(isFreeServerActiveObjectId(new_id, objects))
1215 u16 ServerEnvironment::addActiveObject(ServerActiveObject *object)
1218 u16 id = addActiveObjectRaw(object, true);
1222 bool ServerEnvironment::addActiveObjectAsStatic(ServerActiveObject *obj)
1226 v3f objectpos = obj->getBasePosition();
1228 // The block in which the object resides in
1229 v3s16 blockpos_o = getNodeBlockPos(floatToInt(objectpos, BS));
1232 Update the static data
1235 // Create new static object
1236 std::string staticdata = obj->getStaticData();
1237 StaticObject s_obj(obj->getType(), objectpos, staticdata);
1238 // Add to the block where the object is located in
1239 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1240 // Get or generate the block
1241 MapBlock *block = m_map->emergeBlock(blockpos);
1243 bool succeeded = false;
1247 block->m_static_objects.insert(0, s_obj);
1248 block->raiseModified(MOD_STATE_WRITE_AT_UNLOAD,
1249 "addActiveObjectAsStatic");
1253 infostream<<"ServerEnvironment::addActiveObjectAsStatic: "
1254 <<"Could not find or generate "
1255 <<"a block for storing static object"<<std::endl;
1259 if(obj->environmentDeletes())
1266 Finds out what new objects have been added to
1267 inside a radius around a position
1269 void ServerEnvironment::getAddedActiveObjects(v3s16 pos, s16 radius,
1270 core::map<u16, bool> ¤t_objects,
1271 core::map<u16, bool> &added_objects)
1273 v3f pos_f = intToFloat(pos, BS);
1274 f32 radius_f = radius * BS;
1276 Go through the object list,
1277 - discard m_removed objects,
1278 - discard objects that are too far away,
1279 - discard objects that are found in current_objects.
1280 - add remaining objects to added_objects
1282 for(core::map<u16, ServerActiveObject*>::Iterator
1283 i = m_active_objects.getIterator();
1284 i.atEnd()==false; i++)
1286 u16 id = i.getNode()->getKey();
1288 ServerActiveObject *object = i.getNode()->getValue();
1291 // Discard if removed
1292 if(object->m_removed)
1294 if(object->unlimitedTransferDistance() == false){
1295 // Discard if too far
1296 f32 distance_f = object->getBasePosition().getDistanceFrom(pos_f);
1297 if(distance_f > radius_f)
1300 // Discard if already on current_objects
1301 core::map<u16, bool>::Node *n;
1302 n = current_objects.find(id);
1305 // Add to added_objects
1306 added_objects.insert(id, false);
1311 Finds out what objects have been removed from
1312 inside a radius around a position
1314 void ServerEnvironment::getRemovedActiveObjects(v3s16 pos, s16 radius,
1315 core::map<u16, bool> ¤t_objects,
1316 core::map<u16, bool> &removed_objects)
1318 v3f pos_f = intToFloat(pos, BS);
1319 f32 radius_f = radius * BS;
1321 Go through current_objects; object is removed if:
1322 - object is not found in m_active_objects (this is actually an
1323 error condition; objects should be set m_removed=true and removed
1324 only after all clients have been informed about removal), or
1325 - object has m_removed=true, or
1326 - object is too far away
1328 for(core::map<u16, bool>::Iterator
1329 i = current_objects.getIterator();
1330 i.atEnd()==false; i++)
1332 u16 id = i.getNode()->getKey();
1333 ServerActiveObject *object = getActiveObject(id);
1336 infostream<<"ServerEnvironment::getRemovedActiveObjects():"
1337 <<" object in current_objects is NULL"<<std::endl;
1338 removed_objects.insert(id, false);
1342 if(object->m_removed)
1344 removed_objects.insert(id, false);
1348 // If transfer distance is unlimited, don't remove
1349 if(object->unlimitedTransferDistance())
1352 f32 distance_f = object->getBasePosition().getDistanceFrom(pos_f);
1354 if(distance_f >= radius_f)
1356 removed_objects.insert(id, false);
1364 ActiveObjectMessage ServerEnvironment::getActiveObjectMessage()
1366 if(m_active_object_messages.size() == 0)
1367 return ActiveObjectMessage(0);
1369 return m_active_object_messages.pop_front();
1373 ************ Private methods *************
1376 u16 ServerEnvironment::addActiveObjectRaw(ServerActiveObject *object,
1380 if(object->getId() == 0){
1381 u16 new_id = getFreeServerActiveObjectId(m_active_objects);
1384 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1385 <<"no free ids available"<<std::endl;
1386 if(object->environmentDeletes())
1390 object->setId(new_id);
1393 verbosestream<<"ServerEnvironment::addActiveObjectRaw(): "
1394 <<"supplied with id "<<object->getId()<<std::endl;
1396 if(isFreeServerActiveObjectId(object->getId(), m_active_objects) == false)
1398 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1399 <<"id is not free ("<<object->getId()<<")"<<std::endl;
1400 if(object->environmentDeletes())
1404 /*infostream<<"ServerEnvironment::addActiveObjectRaw(): "
1405 <<"added (id="<<object->getId()<<")"<<std::endl;*/
1407 m_active_objects.insert(object->getId(), object);
1409 verbosestream<<"ServerEnvironment::addActiveObjectRaw(): "
1410 <<"Added id="<<object->getId()<<"; there are now "
1411 <<m_active_objects.size()<<" active objects."
1414 // Register reference in scripting api (must be done before post-init)
1415 scriptapi_add_object_reference(m_lua, object);
1416 // Post-initialize object
1417 object->addedToEnvironment();
1419 // Add static data to block
1420 if(object->isStaticAllowed())
1422 // Add static object to active static list of the block
1423 v3f objectpos = object->getBasePosition();
1424 std::string staticdata = object->getStaticData();
1425 StaticObject s_obj(object->getType(), objectpos, staticdata);
1426 // Add to the block where the object is located in
1427 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1428 MapBlock *block = m_map->getBlockNoCreateNoEx(blockpos);
1431 block->m_static_objects.m_active.insert(object->getId(), s_obj);
1432 object->m_static_exists = true;
1433 object->m_static_block = blockpos;
1436 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1437 "addActiveObjectRaw");
1440 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1441 <<"could not find block for storing id="<<object->getId()
1442 <<" statically"<<std::endl;
1446 return object->getId();
1450 Remove objects that satisfy (m_removed && m_known_by_count==0)
1452 void ServerEnvironment::removeRemovedObjects()
1454 core::list<u16> objects_to_remove;
1455 for(core::map<u16, ServerActiveObject*>::Iterator
1456 i = m_active_objects.getIterator();
1457 i.atEnd()==false; i++)
1459 u16 id = i.getNode()->getKey();
1460 ServerActiveObject* obj = i.getNode()->getValue();
1461 // This shouldn't happen but check it
1464 infostream<<"NULL object found in ServerEnvironment"
1465 <<" while finding removed objects. id="<<id<<std::endl;
1466 // Id to be removed from m_active_objects
1467 objects_to_remove.push_back(id);
1472 We will delete objects that are marked as removed or thatare
1473 waiting for deletion after deactivation
1475 if(obj->m_removed == false && obj->m_pending_deactivation == false)
1479 Delete static data from block if is marked as removed
1481 if(obj->m_static_exists && obj->m_removed)
1483 MapBlock *block = m_map->emergeBlock(obj->m_static_block);
1486 block->m_static_objects.remove(id);
1487 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1488 "removeRemovedObjects");
1489 obj->m_static_exists = false;
1493 // If m_known_by_count > 0, don't actually remove.
1494 if(obj->m_known_by_count > 0)
1497 // Tell the object about removal
1498 obj->removingFromEnvironment();
1499 // Deregister in scripting api
1500 scriptapi_rm_object_reference(m_lua, obj);
1503 if(obj->environmentDeletes())
1505 // Id to be removed from m_active_objects
1506 objects_to_remove.push_back(id);
1508 // Remove references from m_active_objects
1509 for(core::list<u16>::Iterator i = objects_to_remove.begin();
1510 i != objects_to_remove.end(); i++)
1512 m_active_objects.remove(*i);
1516 static void print_hexdump(std::ostream &o, const std::string &data)
1518 const int linelength = 16;
1519 for(int l=0; ; l++){
1520 int i0 = linelength * l;
1521 bool at_end = false;
1522 int thislinelength = linelength;
1523 if(i0 + thislinelength > (int)data.size()){
1524 thislinelength = data.size() - i0;
1527 for(int di=0; di<linelength; di++){
1530 if(di<thislinelength)
1531 snprintf(buf, 4, "%.2x ", data[i]);
1533 snprintf(buf, 4, " ");
1537 for(int di=0; di<thislinelength; di++){
1551 Convert stored objects from blocks near the players to active.
1553 void ServerEnvironment::activateObjects(MapBlock *block)
1557 // Ignore if no stored objects (to not set changed flag)
1558 if(block->m_static_objects.m_stored.size() == 0)
1560 verbosestream<<"ServerEnvironment::activateObjects(): "
1561 <<"activating objects of block "<<PP(block->getPos())
1562 <<" ("<<block->m_static_objects.m_stored.size()
1563 <<" objects)"<<std::endl;
1564 bool large_amount = (block->m_static_objects.m_stored.size() > 49);
1566 errorstream<<"suspiciously large amount of objects detected: "
1567 <<block->m_static_objects.m_stored.size()<<" in "
1568 <<PP(block->getPos())
1569 <<"; removing all of them."<<std::endl;
1570 // Clear stored list
1571 block->m_static_objects.m_stored.clear();
1572 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1573 "stored list cleared in activateObjects due to "
1574 "large amount of objects");
1577 // A list for objects that couldn't be converted to static for some
1578 // reason. They will be stored back.
1579 core::list<StaticObject> new_stored;
1580 // Loop through stored static objects
1581 for(core::list<StaticObject>::Iterator
1582 i = block->m_static_objects.m_stored.begin();
1583 i != block->m_static_objects.m_stored.end(); i++)
1585 /*infostream<<"Server: Creating an active object from "
1586 <<"static data"<<std::endl;*/
1587 StaticObject &s_obj = *i;
1588 // Create an active object from the data
1589 ServerActiveObject *obj = ServerActiveObject::create
1590 (s_obj.type, this, 0, s_obj.pos, s_obj.data);
1591 // If couldn't create object, store static data back.
1594 errorstream<<"ServerEnvironment::activateObjects(): "
1595 <<"failed to create active object from static object "
1596 <<"in block "<<PP(s_obj.pos/BS)
1597 <<" type="<<(int)s_obj.type<<" data:"<<std::endl;
1598 print_hexdump(verbosestream, s_obj.data);
1600 new_stored.push_back(s_obj);
1603 verbosestream<<"ServerEnvironment::activateObjects(): "
1604 <<"activated static object pos="<<PP(s_obj.pos/BS)
1605 <<" type="<<(int)s_obj.type<<std::endl;
1606 // This will also add the object to the active static list
1607 addActiveObjectRaw(obj, false);
1609 // Clear stored list
1610 block->m_static_objects.m_stored.clear();
1611 // Add leftover failed stuff to stored list
1612 for(core::list<StaticObject>::Iterator
1613 i = new_stored.begin();
1614 i != new_stored.end(); i++)
1616 StaticObject &s_obj = *i;
1617 block->m_static_objects.m_stored.push_back(s_obj);
1620 Note: Block hasn't really been modified here.
1621 The objects have just been activated and moved from the stored
1622 static list to the active static list.
1623 As such, the block is essentially the same.
1624 Thus, do not call block->raiseModified(MOD_STATE_WRITE_NEEDED).
1625 Otherwise there would be a huge amount of unnecessary I/O.
1630 Convert objects that are not standing inside active blocks to static.
1632 If m_known_by_count != 0, active object is not deleted, but static
1633 data is still updated.
1635 If force_delete is set, active object is deleted nevertheless. It
1636 shall only be set so in the destructor of the environment.
1638 void ServerEnvironment::deactivateFarObjects(bool force_delete)
1640 core::list<u16> objects_to_remove;
1641 for(core::map<u16, ServerActiveObject*>::Iterator
1642 i = m_active_objects.getIterator();
1643 i.atEnd()==false; i++)
1645 ServerActiveObject* obj = i.getNode()->getValue();
1648 // Do not deactivate if static data creation not allowed
1649 if(!force_delete && !obj->isStaticAllowed())
1652 // If pending deactivation, let removeRemovedObjects() do it
1653 if(!force_delete && obj->m_pending_deactivation)
1656 u16 id = i.getNode()->getKey();
1657 v3f objectpos = obj->getBasePosition();
1659 // The block in which the object resides in
1660 v3s16 blockpos_o = getNodeBlockPos(floatToInt(objectpos, BS));
1662 // If block is active, don't remove
1663 if(!force_delete && m_active_blocks.contains(blockpos_o))
1666 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
1667 <<"deactivating object id="<<id<<" on inactive block "
1668 <<PP(blockpos_o)<<std::endl;
1670 // If known by some client, don't immediately delete.
1671 bool pending_delete = (obj->m_known_by_count > 0 && !force_delete);
1674 Update the static data
1677 if(obj->isStaticAllowed())
1679 // Create new static object
1680 std::string staticdata_new = obj->getStaticData();
1681 StaticObject s_obj(obj->getType(), objectpos, staticdata_new);
1683 bool stays_in_same_block = false;
1684 bool data_changed = true;
1686 if(obj->m_static_exists){
1687 if(obj->m_static_block == blockpos_o)
1688 stays_in_same_block = true;
1690 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
1692 core::map<u16, StaticObject>::Node *n =
1693 block->m_static_objects.m_active.find(id);
1695 StaticObject static_old = n->getValue();
1697 float save_movem = obj->getMinimumSavedMovement();
1699 if(static_old.data == staticdata_new &&
1700 (static_old.pos - objectpos).getLength() < save_movem)
1701 data_changed = false;
1703 errorstream<<"ServerEnvironment::deactivateFarObjects(): "
1704 <<"id="<<id<<" m_static_exists=true but "
1705 <<"static data doesn't actually exist in "
1706 <<PP(obj->m_static_block)<<std::endl;
1710 bool shall_be_written = (!stays_in_same_block || data_changed);
1712 // Delete old static object
1713 if(obj->m_static_exists)
1715 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
1718 block->m_static_objects.remove(id);
1719 obj->m_static_exists = false;
1720 // Only mark block as modified if data changed considerably
1721 if(shall_be_written)
1722 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1723 "deactivateFarObjects: Static data "
1724 "changed considerably");
1728 // Add to the block where the object is located in
1729 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1730 // Get or generate the block
1731 MapBlock *block = m_map->emergeBlock(blockpos);
1735 if(block->m_static_objects.m_stored.size() >= 49){
1736 errorstream<<"ServerEnv: Trying to store id="<<obj->getId()
1737 <<" statically but block "<<PP(blockpos)
1738 <<" already contains "
1739 <<block->m_static_objects.m_stored.size()
1740 <<" (over 49) objects."
1741 <<" Forcing delete."<<std::endl;
1742 force_delete = true;
1744 u16 new_id = pending_delete ? id : 0;
1745 block->m_static_objects.insert(new_id, s_obj);
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");
1753 obj->m_static_exists = true;
1754 obj->m_static_block = block->getPos();
1759 errorstream<<"ServerEnv: Could not find or generate "
1760 <<"a block for storing id="<<obj->getId()
1761 <<" statically"<<std::endl;
1768 If known by some client, set pending deactivation.
1769 Otherwise delete it immediately.
1772 if(pending_delete && !force_delete)
1774 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
1775 <<"object id="<<id<<" is known by clients"
1776 <<"; not deleting yet"<<std::endl;
1778 obj->m_pending_deactivation = true;
1782 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
1783 <<"object id="<<id<<" is not known by clients"
1784 <<"; deleting"<<std::endl;
1786 // Tell the object about removal
1787 obj->removingFromEnvironment();
1788 // Deregister in scripting api
1789 scriptapi_rm_object_reference(m_lua, obj);
1791 // Delete active object
1792 if(obj->environmentDeletes())
1794 // Id to be removed from m_active_objects
1795 objects_to_remove.push_back(id);
1798 // Remove references from m_active_objects
1799 for(core::list<u16>::Iterator i = objects_to_remove.begin();
1800 i != objects_to_remove.end(); i++)
1802 m_active_objects.remove(*i);
1809 #include "clientsimpleobject.h"
1815 ClientEnvironment::ClientEnvironment(ClientMap *map, scene::ISceneManager *smgr,
1816 ITextureSource *texturesource, IGameDef *gamedef,
1817 IrrlichtDevice *irr):
1820 m_texturesource(texturesource),
1826 ClientEnvironment::~ClientEnvironment()
1828 // delete active objects
1829 for(core::map<u16, ClientActiveObject*>::Iterator
1830 i = m_active_objects.getIterator();
1831 i.atEnd()==false; i++)
1833 delete i.getNode()->getValue();
1836 for(core::list<ClientSimpleObject*>::Iterator
1837 i = m_simple_objects.begin(); i != m_simple_objects.end(); i++)
1846 Map & ClientEnvironment::getMap()
1851 ClientMap & ClientEnvironment::getClientMap()
1856 void ClientEnvironment::addPlayer(Player *player)
1858 DSTACK(__FUNCTION_NAME);
1860 It is a failure if player is local and there already is a local
1863 assert(!(player->isLocal() == true && getLocalPlayer() != NULL));
1865 Environment::addPlayer(player);
1868 LocalPlayer * ClientEnvironment::getLocalPlayer()
1870 for(core::list<Player*>::Iterator i = m_players.begin();
1871 i != m_players.end(); i++)
1873 Player *player = *i;
1874 if(player->isLocal())
1875 return (LocalPlayer*)player;
1880 void ClientEnvironment::step(float dtime)
1882 DSTACK(__FUNCTION_NAME);
1884 /* Step time of day */
1885 stepTimeOfDay(dtime);
1887 // Get some settings
1888 bool free_move = g_settings->getBool("free_move");
1891 LocalPlayer *lplayer = getLocalPlayer();
1893 // collision info queue
1894 core::list<CollisionInfo> player_collisions;
1897 Get the speed the player is going
1899 bool is_climbing = lplayer->is_climbing;
1901 f32 player_speed = lplayer->getSpeed().getLength();
1904 Maximum position increment
1906 //f32 position_max_increment = 0.05*BS;
1907 f32 position_max_increment = 0.1*BS;
1909 // Maximum time increment (for collision detection etc)
1910 // time = distance / speed
1911 f32 dtime_max_increment = 1;
1912 if(player_speed > 0.001)
1913 dtime_max_increment = position_max_increment / player_speed;
1915 // Maximum time increment is 10ms or lower
1916 if(dtime_max_increment > 0.01)
1917 dtime_max_increment = 0.01;
1919 // Don't allow overly huge dtime
1923 f32 dtime_downcount = dtime;
1926 Stuff that has a maximum time increment
1935 if(dtime_downcount > dtime_max_increment)
1937 dtime_part = dtime_max_increment;
1938 dtime_downcount -= dtime_part;
1942 dtime_part = dtime_downcount;
1944 Setting this to 0 (no -=dtime_part) disables an infinite loop
1945 when dtime_part is so small that dtime_downcount -= dtime_part
1948 dtime_downcount = 0;
1956 v3f lplayerpos = lplayer->getPosition();
1959 if(free_move == false && is_climbing == false)
1962 v3f speed = lplayer->getSpeed();
1963 if(lplayer->swimming_up == false)
1964 speed.Y -= 9.81 * BS * dtime_part * 2;
1967 if(lplayer->in_water_stable || lplayer->in_water)
1969 f32 max_down = 2.0*BS;
1970 if(speed.Y < -max_down) speed.Y = -max_down;
1973 if(speed.getLength() > max)
1975 speed = speed / speed.getLength() * max;
1979 lplayer->setSpeed(speed);
1984 This also does collision detection.
1986 lplayer->move(dtime_part, *m_map, position_max_increment,
1987 &player_collisions);
1990 while(dtime_downcount > 0.001);
1992 //std::cout<<"Looped "<<loopcount<<" times."<<std::endl;
1994 for(core::list<CollisionInfo>::Iterator
1995 i = player_collisions.begin();
1996 i != player_collisions.end(); i++)
1998 CollisionInfo &info = *i;
1999 if(info.t == COLLISION_FALL)
2001 //f32 tolerance = BS*10; // 2 without damage
2002 //f32 tolerance = BS*12; // 3 without damage
2003 f32 tolerance = BS*14; // 5 without damage
2005 if(info.speed > tolerance)
2007 f32 damage_f = (info.speed - tolerance)/BS*factor;
2008 u16 damage = (u16)(damage_f+0.5);
2010 damageLocalPlayer(damage, true);
2016 A quick draft of lava damage
2018 if(m_lava_hurt_interval.step(dtime, 1.0))
2020 v3f pf = lplayer->getPosition();
2022 // Feet, middle and head
2023 v3s16 p1 = floatToInt(pf + v3f(0, BS*0.1, 0), BS);
2024 MapNode n1 = m_map->getNodeNoEx(p1);
2025 v3s16 p2 = floatToInt(pf + v3f(0, BS*0.8, 0), BS);
2026 MapNode n2 = m_map->getNodeNoEx(p2);
2027 v3s16 p3 = floatToInt(pf + v3f(0, BS*1.6, 0), BS);
2028 MapNode n3 = m_map->getNodeNoEx(p2);
2030 u32 damage_per_second = 0;
2031 damage_per_second = MYMAX(damage_per_second,
2032 m_gamedef->ndef()->get(n1).damage_per_second);
2033 damage_per_second = MYMAX(damage_per_second,
2034 m_gamedef->ndef()->get(n2).damage_per_second);
2035 damage_per_second = MYMAX(damage_per_second,
2036 m_gamedef->ndef()->get(n3).damage_per_second);
2038 if(damage_per_second != 0)
2040 damageLocalPlayer(damage_per_second, true);
2045 Stuff that can be done in an arbitarily large dtime
2047 for(core::list<Player*>::Iterator i = m_players.begin();
2048 i != m_players.end(); i++)
2050 Player *player = *i;
2051 v3f playerpos = player->getPosition();
2054 Handle non-local players
2056 if(player->isLocal() == false)
2059 player->move(dtime, *m_map, 100*BS);
2063 // Update lighting on all players on client
2064 u8 light = LIGHT_MAX;
2067 v3s16 p = player->getLightPosition();
2068 MapNode n = m_map->getNode(p);
2069 light = n.getLightBlend(getDayNightRatio(), m_gamedef->ndef());
2071 catch(InvalidPositionException &e){
2072 light = blend_light(getDayNightRatio(), LIGHT_SUN, 0);
2074 player->light = light;
2078 Step active objects and update lighting of them
2081 for(core::map<u16, ClientActiveObject*>::Iterator
2082 i = m_active_objects.getIterator();
2083 i.atEnd()==false; i++)
2085 ClientActiveObject* obj = i.getNode()->getValue();
2087 obj->step(dtime, this);
2089 if(m_active_object_light_update_interval.step(dtime, 0.21))
2095 v3s16 p = obj->getLightPosition();
2096 MapNode n = m_map->getNode(p);
2097 light = n.getLightBlend(getDayNightRatio(), m_gamedef->ndef());
2099 catch(InvalidPositionException &e){
2100 light = blend_light(getDayNightRatio(), LIGHT_SUN, 0);
2102 obj->updateLight(light);
2107 Step and handle simple objects
2109 for(core::list<ClientSimpleObject*>::Iterator
2110 i = m_simple_objects.begin(); i != m_simple_objects.end();)
2112 ClientSimpleObject *simple = *i;
2113 core::list<ClientSimpleObject*>::Iterator cur = i;
2115 simple->step(dtime);
2116 if(simple->m_to_be_removed){
2118 m_simple_objects.erase(cur);
2123 void ClientEnvironment::addSimpleObject(ClientSimpleObject *simple)
2125 m_simple_objects.push_back(simple);
2128 ClientActiveObject* ClientEnvironment::getActiveObject(u16 id)
2130 core::map<u16, ClientActiveObject*>::Node *n;
2131 n = m_active_objects.find(id);
2134 return n->getValue();
2137 bool isFreeClientActiveObjectId(u16 id,
2138 core::map<u16, ClientActiveObject*> &objects)
2143 for(core::map<u16, ClientActiveObject*>::Iterator
2144 i = objects.getIterator();
2145 i.atEnd()==false; i++)
2147 if(i.getNode()->getKey() == id)
2153 u16 getFreeClientActiveObjectId(
2154 core::map<u16, ClientActiveObject*> &objects)
2159 if(isFreeClientActiveObjectId(new_id, objects))
2169 u16 ClientEnvironment::addActiveObject(ClientActiveObject *object)
2172 if(object->getId() == 0)
2174 u16 new_id = getFreeClientActiveObjectId(m_active_objects);
2177 infostream<<"ClientEnvironment::addActiveObject(): "
2178 <<"no free ids available"<<std::endl;
2182 object->setId(new_id);
2184 if(isFreeClientActiveObjectId(object->getId(), m_active_objects) == false)
2186 infostream<<"ClientEnvironment::addActiveObject(): "
2187 <<"id is not free ("<<object->getId()<<")"<<std::endl;
2191 infostream<<"ClientEnvironment::addActiveObject(): "
2192 <<"added (id="<<object->getId()<<")"<<std::endl;
2193 m_active_objects.insert(object->getId(), object);
2194 object->addToScene(m_smgr, m_texturesource, m_irr);
2195 { // Update lighting immediately
2199 v3s16 p = object->getLightPosition();
2200 MapNode n = m_map->getNode(p);
2201 light = n.getLightBlend(getDayNightRatio(), m_gamedef->ndef());
2203 catch(InvalidPositionException &e){
2204 light = blend_light(getDayNightRatio(), LIGHT_SUN, 0);
2206 object->updateLight(light);
2208 return object->getId();
2211 void ClientEnvironment::addActiveObject(u16 id, u8 type,
2212 const std::string &init_data)
2214 ClientActiveObject* obj =
2215 ClientActiveObject::create(type, m_gamedef, this);
2218 infostream<<"ClientEnvironment::addActiveObject(): "
2219 <<"id="<<id<<" type="<<type<<": Couldn't create object"
2228 obj->initialize(init_data);
2230 catch(SerializationError &e)
2232 errorstream<<"ClientEnvironment::addActiveObject():"
2233 <<" id="<<id<<" type="<<type
2234 <<": SerializationError in initialize(),"
2235 <<" init_data="<<serializeJsonString(init_data)
2239 addActiveObject(obj);
2242 void ClientEnvironment::removeActiveObject(u16 id)
2244 verbosestream<<"ClientEnvironment::removeActiveObject(): "
2245 <<"id="<<id<<std::endl;
2246 ClientActiveObject* obj = getActiveObject(id);
2249 infostream<<"ClientEnvironment::removeActiveObject(): "
2250 <<"id="<<id<<" not found"<<std::endl;
2253 obj->removeFromScene();
2255 m_active_objects.remove(id);
2258 void ClientEnvironment::processActiveObjectMessage(u16 id,
2259 const std::string &data)
2261 ClientActiveObject* obj = getActiveObject(id);
2264 infostream<<"ClientEnvironment::processActiveObjectMessage():"
2265 <<" got message for id="<<id<<", which doesn't exist."
2271 obj->processMessage(data);
2273 catch(SerializationError &e)
2275 errorstream<<"ClientEnvironment::processActiveObjectMessage():"
2276 <<" id="<<id<<" type="<<obj->getType()
2277 <<" SerializationError in processMessage(),"
2278 <<" message="<<serializeJsonString(data)
2284 Callbacks for activeobjects
2287 void ClientEnvironment::damageLocalPlayer(u8 damage, bool handle_hp)
2289 LocalPlayer *lplayer = getLocalPlayer();
2293 if(lplayer->hp > damage)
2294 lplayer->hp -= damage;
2299 ClientEnvEvent event;
2300 event.type = CEE_PLAYER_DAMAGE;
2301 event.player_damage.amount = damage;
2302 event.player_damage.send_to_server = handle_hp;
2303 m_client_event_queue.push_back(event);
2307 Client likes to call these
2310 void ClientEnvironment::getActiveObjects(v3f origin, f32 max_d,
2311 core::array<DistanceSortedActiveObject> &dest)
2313 for(core::map<u16, ClientActiveObject*>::Iterator
2314 i = m_active_objects.getIterator();
2315 i.atEnd()==false; i++)
2317 ClientActiveObject* obj = i.getNode()->getValue();
2319 f32 d = (obj->getPosition() - origin).getLength();
2324 DistanceSortedActiveObject dso(obj, d);
2326 dest.push_back(dso);
2330 ClientEnvEvent ClientEnvironment::getClientEvent()
2332 if(m_client_event_queue.size() == 0)
2334 ClientEnvEvent event;
2335 event.type = CEE_NONE;
2338 return m_client_event_queue.pop_front();
2341 #endif // #ifndef SERVER