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"
42 #include "localplayer.h"
44 #include "daynightratio.h"
46 #define PP(x) "("<<(x).X<<","<<(x).Y<<","<<(x).Z<<")"
48 Environment::Environment():
50 m_time_of_day_f(9000./24000),
51 m_time_of_day_speed(0),
56 Environment::~Environment()
59 for(core::list<Player*>::Iterator i = m_players.begin();
60 i != m_players.end(); i++)
66 void Environment::addPlayer(Player *player)
68 DSTACK(__FUNCTION_NAME);
70 Check that peer_ids are unique.
71 Also check that names are unique.
72 Exception: there can be multiple players with peer_id=0
74 // If peer id is non-zero, it has to be unique.
75 if(player->peer_id != 0)
76 assert(getPlayer(player->peer_id) == NULL);
77 // Name has to be unique.
78 assert(getPlayer(player->getName()) == NULL);
80 m_players.push_back(player);
83 void Environment::removePlayer(u16 peer_id)
85 DSTACK(__FUNCTION_NAME);
87 for(core::list<Player*>::Iterator i = m_players.begin();
88 i != m_players.end(); i++)
91 if(player->peer_id != peer_id)
96 // See if there is an another one
97 // (shouldn't be, but just to be sure)
102 Player * Environment::getPlayer(u16 peer_id)
104 for(core::list<Player*>::Iterator i = m_players.begin();
105 i != m_players.end(); i++)
108 if(player->peer_id == peer_id)
114 Player * Environment::getPlayer(const char *name)
116 for(core::list<Player*>::Iterator i = m_players.begin();
117 i != m_players.end(); i++)
120 if(strcmp(player->getName(), name) == 0)
126 Player * Environment::getRandomConnectedPlayer()
128 core::list<Player*> connected_players = getPlayers(true);
129 u32 chosen_one = myrand() % connected_players.size();
131 for(core::list<Player*>::Iterator
132 i = connected_players.begin();
133 i != connected_players.end(); i++)
145 Player * Environment::getNearestConnectedPlayer(v3f pos)
147 core::list<Player*> connected_players = getPlayers(true);
149 Player *nearest_player = NULL;
150 for(core::list<Player*>::Iterator
151 i = connected_players.begin();
152 i != connected_players.end(); i++)
155 f32 d = player->getPosition().getDistanceFrom(pos);
156 if(d < nearest_d || nearest_player == NULL)
159 nearest_player = player;
162 return nearest_player;
165 core::list<Player*> Environment::getPlayers()
170 core::list<Player*> Environment::getPlayers(bool ignore_disconnected)
172 core::list<Player*> newlist;
173 for(core::list<Player*>::Iterator
174 i = m_players.begin();
175 i != m_players.end(); i++)
179 if(ignore_disconnected)
181 // Ignore disconnected players
182 if(player->peer_id == 0)
186 newlist.push_back(player);
191 void Environment::printPlayers(std::ostream &o)
193 o<<"Players in environment:"<<std::endl;
194 for(core::list<Player*>::Iterator i = m_players.begin();
195 i != m_players.end(); i++)
198 o<<"Player peer_id="<<player->peer_id<<std::endl;
202 u32 Environment::getDayNightRatio()
204 return time_to_daynight_ratio(m_time_of_day);
207 void Environment::stepTimeOfDay(float dtime)
209 m_time_counter += dtime;
210 f32 speed = m_time_of_day_speed * 24000./(24.*3600);
211 u32 units = (u32)(m_time_counter*speed);
212 m_time_counter -= (f32)units / speed;
216 if(m_time_of_day + units >= 24000)
218 m_time_of_day = (m_time_of_day + units) % 24000;
220 m_time_of_day_f = (float)m_time_of_day / 24000.0;
223 m_time_of_day_f += m_time_of_day_speed/24/3600*dtime;
224 if(m_time_of_day_f > 1.0)
225 m_time_of_day_f -= 1.0;
226 if(m_time_of_day_f < 0.0)
227 m_time_of_day_f += 1.0;
235 ABMWithState::ABMWithState(ActiveBlockModifier *abm_):
239 // Initialize timer to random value to spread processing
240 float itv = abm->getTriggerInterval();
241 itv = MYMAX(0.001, itv); // No less than 1ms
242 int minval = MYMAX(-0.51*itv, -60); // Clamp to
243 int maxval = MYMIN(0.51*itv, 60); // +-60 seconds
244 timer = myrand_range(minval, maxval);
251 void fillRadiusBlock(v3s16 p0, s16 r, core::map<v3s16, bool> &list)
254 for(p.X=p0.X-r; p.X<=p0.X+r; p.X++)
255 for(p.Y=p0.Y-r; p.Y<=p0.Y+r; p.Y++)
256 for(p.Z=p0.Z-r; p.Z<=p0.Z+r; p.Z++)
263 void ActiveBlockList::update(core::list<v3s16> &active_positions,
265 core::map<v3s16, bool> &blocks_removed,
266 core::map<v3s16, bool> &blocks_added)
271 core::map<v3s16, bool> newlist;
272 for(core::list<v3s16>::Iterator i = active_positions.begin();
273 i != active_positions.end(); i++)
275 fillRadiusBlock(*i, radius, newlist);
279 Find out which blocks on the old list are not on the new list
281 // Go through old list
282 for(core::map<v3s16, bool>::Iterator i = m_list.getIterator();
283 i.atEnd()==false; i++)
285 v3s16 p = i.getNode()->getKey();
286 // If not on new list, it's been removed
287 if(newlist.find(p) == NULL)
288 blocks_removed.insert(p, true);
292 Find out which blocks on the new list are not on the old list
294 // Go through new list
295 for(core::map<v3s16, bool>::Iterator i = newlist.getIterator();
296 i.atEnd()==false; i++)
298 v3s16 p = i.getNode()->getKey();
299 // If not on old list, it's been added
300 if(m_list.find(p) == NULL)
301 blocks_added.insert(p, true);
308 for(core::map<v3s16, bool>::Iterator i = newlist.getIterator();
309 i.atEnd()==false; i++)
311 v3s16 p = i.getNode()->getKey();
312 m_list.insert(p, true);
320 ServerEnvironment::ServerEnvironment(ServerMap *map, lua_State *L,
321 IGameDef *gamedef, IBackgroundBlockEmerger *emerger):
326 m_random_spawn_timer(3),
327 m_send_recommended_timer(0),
329 m_game_time_fraction_counter(0)
333 ServerEnvironment::~ServerEnvironment()
335 // Clear active block list.
336 // This makes the next one delete all active objects.
337 m_active_blocks.clear();
339 // Convert all objects to static and delete the active objects
340 deactivateFarObjects(true);
345 // Delete ActiveBlockModifiers
346 for(core::list<ABMWithState>::Iterator
347 i = m_abms.begin(); i != m_abms.end(); i++){
352 void ServerEnvironment::serializePlayers(const std::string &savedir)
354 std::string players_path = savedir + "/players";
355 fs::CreateDir(players_path);
357 core::map<Player*, bool> saved_players;
359 std::vector<fs::DirListNode> player_files = fs::GetDirListing(players_path);
360 for(u32 i=0; i<player_files.size(); i++)
362 if(player_files[i].dir)
365 // Full path to this file
366 std::string path = players_path + "/" + player_files[i].name;
368 //infostream<<"Checking player file "<<path<<std::endl;
370 // Load player to see what is its name
371 RemotePlayer testplayer(m_gamedef);
373 // Open file and deserialize
374 std::ifstream is(path.c_str(), std::ios_base::binary);
375 if(is.good() == false)
377 infostream<<"Failed to read "<<path<<std::endl;
380 testplayer.deSerialize(is);
383 //infostream<<"Loaded test player with name "<<testplayer.getName()<<std::endl;
385 // Search for the player
386 std::string playername = testplayer.getName();
387 Player *player = getPlayer(playername.c_str());
390 infostream<<"Didn't find matching player, ignoring file "<<path<<std::endl;
394 //infostream<<"Found matching player, overwriting."<<std::endl;
396 // OK, found. Save player there.
398 // Open file and serialize
399 std::ofstream os(path.c_str(), std::ios_base::binary);
400 if(os.good() == false)
402 infostream<<"Failed to overwrite "<<path<<std::endl;
405 player->serialize(os);
406 saved_players.insert(player, true);
410 for(core::list<Player*>::Iterator i = m_players.begin();
411 i != m_players.end(); i++)
414 if(saved_players.find(player) != NULL)
416 /*infostream<<"Player "<<player->getName()
417 <<" was already saved."<<std::endl;*/
420 std::string playername = player->getName();
421 // Don't save unnamed player
424 //infostream<<"Not saving unnamed player."<<std::endl;
430 if(string_allowed(playername, PLAYERNAME_ALLOWED_CHARS) == false)
431 playername = "player";
432 std::string path = players_path + "/" + playername;
434 for(u32 i=0; i<1000; i++)
436 if(fs::PathExists(path) == false)
441 path = players_path + "/" + playername + itos(i);
445 infostream<<"Didn't find free file for player"<<std::endl;
450 /*infostream<<"Saving player "<<player->getName()<<" to "
452 // Open file and serialize
453 std::ofstream os(path.c_str(), std::ios_base::binary);
454 if(os.good() == false)
456 infostream<<"Failed to overwrite "<<path<<std::endl;
459 player->serialize(os);
460 saved_players.insert(player, true);
464 //infostream<<"Saved "<<saved_players.size()<<" players."<<std::endl;
467 void ServerEnvironment::deSerializePlayers(const std::string &savedir)
469 std::string players_path = savedir + "/players";
471 core::map<Player*, bool> saved_players;
473 std::vector<fs::DirListNode> player_files = fs::GetDirListing(players_path);
474 for(u32 i=0; i<player_files.size(); i++)
476 if(player_files[i].dir)
479 // Full path to this file
480 std::string path = players_path + "/" + player_files[i].name;
482 //infostream<<"Checking player file "<<path<<std::endl;
484 // Load player to see what is its name
485 RemotePlayer testplayer(m_gamedef);
487 // Open file and deserialize
488 std::ifstream is(path.c_str(), std::ios_base::binary);
489 if(is.good() == false)
491 infostream<<"Failed to read "<<path<<std::endl;
494 testplayer.deSerialize(is);
497 if(!string_allowed(testplayer.getName(), PLAYERNAME_ALLOWED_CHARS))
499 infostream<<"Not loading player with invalid name: "
500 <<testplayer.getName()<<std::endl;
503 /*infostream<<"Loaded test player with name "<<testplayer.getName()
506 // Search for the player
507 std::string playername = testplayer.getName();
508 Player *player = getPlayer(playername.c_str());
509 bool newplayer = false;
512 //infostream<<"Is a new player"<<std::endl;
513 player = new RemotePlayer(m_gamedef);
519 verbosestream<<"Reading player "<<testplayer.getName()<<" from "
521 // Open file and deserialize
522 std::ifstream is(path.c_str(), std::ios_base::binary);
523 if(is.good() == false)
525 infostream<<"Failed to read "<<path<<std::endl;
528 player->deSerialize(is);
538 void ServerEnvironment::saveMeta(const std::string &savedir)
540 std::string path = savedir + "/env_meta.txt";
542 // Open file and serialize
543 std::ofstream os(path.c_str(), std::ios_base::binary);
544 if(os.good() == false)
546 infostream<<"ServerEnvironment::saveMeta(): Failed to open "
548 throw SerializationError("Couldn't save env meta");
552 args.setU64("game_time", m_game_time);
553 args.setU64("time_of_day", getTimeOfDay());
558 void ServerEnvironment::loadMeta(const std::string &savedir)
560 std::string path = savedir + "/env_meta.txt";
562 // Open file and deserialize
563 std::ifstream is(path.c_str(), std::ios_base::binary);
564 if(is.good() == false)
566 infostream<<"ServerEnvironment::loadMeta(): Failed to open "
568 throw SerializationError("Couldn't load env meta");
576 throw SerializationError
577 ("ServerEnvironment::loadMeta(): EnvArgsEnd not found");
579 std::getline(is, line);
580 std::string trimmedline = trim(line);
581 if(trimmedline == "EnvArgsEnd")
583 args.parseConfigLine(line);
587 m_game_time = args.getU64("game_time");
588 }catch(SettingNotFoundException &e){
589 // Getting this is crucial, otherwise timestamps are useless
590 throw SerializationError("Couldn't load env meta game_time");
594 m_time_of_day = args.getU64("time_of_day");
595 }catch(SettingNotFoundException &e){
596 // This is not as important
597 m_time_of_day = 9000;
603 ActiveBlockModifier *abm;
605 std::set<content_t> required_neighbors;
611 ServerEnvironment *m_env;
612 std::map<content_t, std::list<ActiveABM> > m_aabms;
614 ABMHandler(core::list<ABMWithState> &abms,
615 float dtime_s, ServerEnvironment *env,
621 INodeDefManager *ndef = env->getGameDef()->ndef();
622 for(core::list<ABMWithState>::Iterator
623 i = abms.begin(); i != abms.end(); i++){
624 ActiveBlockModifier *abm = i->abm;
625 float trigger_interval = abm->getTriggerInterval();
626 if(trigger_interval < 0.001)
627 trigger_interval = 0.001;
628 float actual_interval = dtime_s;
631 if(i->timer < trigger_interval)
633 i->timer -= trigger_interval;
634 actual_interval = trigger_interval;
638 float intervals = actual_interval / trigger_interval;
639 float chance = abm->getTriggerChance();
642 aabm.chance = 1.0 / pow((float)1.0/chance, (float)intervals);
646 std::set<std::string> required_neighbors_s
647 = abm->getRequiredNeighbors();
648 for(std::set<std::string>::iterator
649 i = required_neighbors_s.begin();
650 i != required_neighbors_s.end(); i++)
652 ndef->getIds(*i, aabm.required_neighbors);
655 std::set<std::string> contents_s = abm->getTriggerContents();
656 for(std::set<std::string>::iterator
657 i = contents_s.begin(); i != contents_s.end(); i++)
659 std::set<content_t> ids;
660 ndef->getIds(*i, ids);
661 for(std::set<content_t>::const_iterator k = ids.begin();
665 std::map<content_t, std::list<ActiveABM> >::iterator j;
667 if(j == m_aabms.end()){
668 std::list<ActiveABM> aabmlist;
669 m_aabms[c] = aabmlist;
672 j->second.push_back(aabm);
677 void apply(MapBlock *block)
682 ServerMap *map = &m_env->getServerMap();
685 for(p0.X=0; p0.X<MAP_BLOCKSIZE; p0.X++)
686 for(p0.Y=0; p0.Y<MAP_BLOCKSIZE; p0.Y++)
687 for(p0.Z=0; p0.Z<MAP_BLOCKSIZE; p0.Z++)
689 MapNode n = block->getNodeNoEx(p0);
690 content_t c = n.getContent();
691 v3s16 p = p0 + block->getPosRelative();
693 std::map<content_t, std::list<ActiveABM> >::iterator j;
695 if(j == m_aabms.end())
698 for(std::list<ActiveABM>::iterator
699 i = j->second.begin(); i != j->second.end(); i++)
701 if(myrand() % i->chance != 0)
705 if(!i->required_neighbors.empty())
708 for(p1.X = p.X-1; p1.X <= p.X+1; p1.X++)
709 for(p1.Y = p.Y-1; p1.Y <= p.Y+1; p1.Y++)
710 for(p1.Z = p.Z-1; p1.Z <= p.Z+1; p1.Z++)
714 MapNode n = map->getNodeNoEx(p1);
715 content_t c = n.getContent();
716 std::set<content_t>::const_iterator k;
717 k = i->required_neighbors.find(c);
718 if(k != i->required_neighbors.end()){
722 // No required neighbor found
727 // Find out how many objects the block contains
728 u32 active_object_count = block->m_static_objects.m_active.size();
729 // Find out how many objects this and all the neighbors contain
730 u32 active_object_count_wider = 0;
731 for(s16 x=-1; x<=1; x++)
732 for(s16 y=-1; y<=1; y++)
733 for(s16 z=-1; z<=1; z++)
735 MapBlock *block2 = map->getBlockNoCreateNoEx(
736 block->getPos() + v3s16(x,y,z));
739 active_object_count_wider +=
740 block2->m_static_objects.m_active.size()
741 + block2->m_static_objects.m_stored.size();
744 // Call all the trigger variations
745 i->abm->trigger(m_env, p, n);
746 i->abm->trigger(m_env, p, n,
747 active_object_count, active_object_count_wider);
753 void ServerEnvironment::activateBlock(MapBlock *block, u32 additional_dtime)
755 // Get time difference
757 u32 stamp = block->getTimestamp();
758 if(m_game_time > stamp && stamp != BLOCK_TIMESTAMP_UNDEFINED)
759 dtime_s = m_game_time - block->getTimestamp();
760 dtime_s += additional_dtime;
762 /*infostream<<"ServerEnvironment::activateBlock(): block timestamp: "
763 <<stamp<<", game time: "<<m_game_time<<std::endl;*/
765 // Set current time as timestamp
766 block->setTimestampNoChangedFlag(m_game_time);
768 /*infostream<<"ServerEnvironment::activateBlock(): block is "
769 <<dtime_s<<" seconds old."<<std::endl;*/
771 // Activate stored objects
772 activateObjects(block);
775 bool changed = block->m_node_metadata->step((float)dtime_s);
779 event.type = MEET_BLOCK_NODE_METADATA_CHANGED;
780 event.p = block->getPos();
781 m_map->dispatchEvent(&event);
783 block->raiseModified(MOD_STATE_WRITE_NEEDED,
784 "node metadata modified in activateBlock");
787 /* Handle ActiveBlockModifiers */
788 ABMHandler abmhandler(m_abms, dtime_s, this, false);
789 abmhandler.apply(block);
792 void ServerEnvironment::addActiveBlockModifier(ActiveBlockModifier *abm)
794 m_abms.push_back(ABMWithState(abm));
797 std::set<u16> ServerEnvironment::getObjectsInsideRadius(v3f pos, float radius)
799 std::set<u16> objects;
800 for(core::map<u16, ServerActiveObject*>::Iterator
801 i = m_active_objects.getIterator();
802 i.atEnd()==false; i++)
804 ServerActiveObject* obj = i.getNode()->getValue();
805 u16 id = i.getNode()->getKey();
806 v3f objectpos = obj->getBasePosition();
807 if(objectpos.getDistanceFrom(pos) > radius)
814 void ServerEnvironment::clearAllObjects()
816 infostream<<"ServerEnvironment::clearAllObjects(): "
817 <<"Removing all active objects"<<std::endl;
818 core::list<u16> objects_to_remove;
819 for(core::map<u16, ServerActiveObject*>::Iterator
820 i = m_active_objects.getIterator();
821 i.atEnd()==false; i++)
823 ServerActiveObject* obj = i.getNode()->getValue();
824 if(obj->getType() == ACTIVEOBJECT_TYPE_PLAYER)
826 u16 id = i.getNode()->getKey();
827 v3f objectpos = obj->getBasePosition();
828 // Delete static object if block is loaded
829 if(obj->m_static_exists){
830 MapBlock *block = m_map->getBlockNoCreateNoEx(obj->m_static_block);
832 block->m_static_objects.remove(id);
833 block->raiseModified(MOD_STATE_WRITE_NEEDED,
835 obj->m_static_exists = false;
838 // If known by some client, don't delete immediately
839 if(obj->m_known_by_count > 0){
840 obj->m_pending_deactivation = true;
841 obj->m_removed = true;
845 // Tell the object about removal
846 obj->removingFromEnvironment();
847 // Deregister in scripting api
848 scriptapi_rm_object_reference(m_lua, obj);
850 // Delete active object
851 if(obj->environmentDeletes())
853 // Id to be removed from m_active_objects
854 objects_to_remove.push_back(id);
856 // Remove references from m_active_objects
857 for(core::list<u16>::Iterator i = objects_to_remove.begin();
858 i != objects_to_remove.end(); i++)
860 m_active_objects.remove(*i);
863 core::list<v3s16> loadable_blocks;
864 infostream<<"ServerEnvironment::clearAllObjects(): "
865 <<"Listing all loadable blocks"<<std::endl;
866 m_map->listAllLoadableBlocks(loadable_blocks);
867 infostream<<"ServerEnvironment::clearAllObjects(): "
868 <<"Done listing all loadable blocks: "
869 <<loadable_blocks.size()
870 <<", now clearing"<<std::endl;
871 u32 report_interval = loadable_blocks.size() / 10;
872 u32 num_blocks_checked = 0;
873 u32 num_blocks_cleared = 0;
874 u32 num_objs_cleared = 0;
875 for(core::list<v3s16>::Iterator i = loadable_blocks.begin();
876 i != loadable_blocks.end(); i++)
879 MapBlock *block = m_map->emergeBlock(p, false);
881 errorstream<<"ServerEnvironment::clearAllObjects(): "
882 <<"Failed to emerge block "<<PP(p)<<std::endl;
885 u32 num_stored = block->m_static_objects.m_stored.size();
886 u32 num_active = block->m_static_objects.m_active.size();
887 if(num_stored != 0 || num_active != 0){
888 block->m_static_objects.m_stored.clear();
889 block->m_static_objects.m_active.clear();
890 block->raiseModified(MOD_STATE_WRITE_NEEDED,
892 num_objs_cleared += num_stored + num_active;
893 num_blocks_cleared++;
895 num_blocks_checked++;
897 if(num_blocks_checked % report_interval == 0){
898 float percent = 100.0 * (float)num_blocks_checked /
899 loadable_blocks.size();
900 infostream<<"ServerEnvironment::clearAllObjects(): "
901 <<"Cleared "<<num_objs_cleared<<" objects"
902 <<" in "<<num_blocks_cleared<<" blocks ("
903 <<percent<<"%)"<<std::endl;
906 infostream<<"ServerEnvironment::clearAllObjects(): "
907 <<"Finished: Cleared "<<num_objs_cleared<<" objects"
908 <<" in "<<num_blocks_cleared<<" blocks"<<std::endl;
911 void ServerEnvironment::step(float dtime)
913 DSTACK(__FUNCTION_NAME);
915 //TimeTaker timer("ServerEnv step");
917 /* Step time of day */
918 stepTimeOfDay(dtime);
924 m_game_time_fraction_counter += dtime;
925 u32 inc_i = (u32)m_game_time_fraction_counter;
926 m_game_time += inc_i;
927 m_game_time_fraction_counter -= (float)inc_i;
934 ScopeProfiler sp(g_profiler, "SEnv: handle players avg", SPT_AVG);
935 for(core::list<Player*>::Iterator i = m_players.begin();
936 i != m_players.end(); i++)
940 // Ignore disconnected players
941 if(player->peer_id == 0)
944 v3f playerpos = player->getPosition();
947 player->move(dtime, *m_map, 100*BS);
952 Manage active block list
954 if(m_active_blocks_management_interval.step(dtime, 2.0))
956 ScopeProfiler sp(g_profiler, "SEnv: manage act. block list avg /2s", SPT_AVG);
958 Get player block positions
960 core::list<v3s16> players_blockpos;
961 for(core::list<Player*>::Iterator
962 i = m_players.begin();
963 i != m_players.end(); i++)
966 // Ignore disconnected players
967 if(player->peer_id == 0)
969 v3s16 blockpos = getNodeBlockPos(
970 floatToInt(player->getPosition(), BS));
971 players_blockpos.push_back(blockpos);
975 Update list of active blocks, collecting changes
977 const s16 active_block_range = g_settings->getS16("active_block_range");
978 core::map<v3s16, bool> blocks_removed;
979 core::map<v3s16, bool> blocks_added;
980 m_active_blocks.update(players_blockpos, active_block_range,
981 blocks_removed, blocks_added);
984 Handle removed blocks
987 // Convert active objects that are no more in active blocks to static
988 deactivateFarObjects(false);
990 for(core::map<v3s16, bool>::Iterator
991 i = blocks_removed.getIterator();
992 i.atEnd()==false; i++)
994 v3s16 p = i.getNode()->getKey();
996 /*infostream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
997 <<") became inactive"<<std::endl;*/
999 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1003 // Set current time as timestamp (and let it set ChangedFlag)
1004 block->setTimestamp(m_game_time);
1011 for(core::map<v3s16, bool>::Iterator
1012 i = blocks_added.getIterator();
1013 i.atEnd()==false; i++)
1015 v3s16 p = i.getNode()->getKey();
1017 /*infostream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
1018 <<") became active"<<std::endl;*/
1020 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1022 // Block needs to be fetched first
1023 m_emerger->queueBlockEmerge(p, false);
1024 m_active_blocks.m_list.remove(p);
1028 activateBlock(block);
1033 Mess around in active blocks
1035 if(m_active_blocks_nodemetadata_interval.step(dtime, 1.0))
1037 ScopeProfiler sp(g_profiler, "SEnv: mess in act. blocks avg /1s", SPT_AVG);
1041 for(core::map<v3s16, bool>::Iterator
1042 i = m_active_blocks.m_list.getIterator();
1043 i.atEnd()==false; i++)
1045 v3s16 p = i.getNode()->getKey();
1047 /*infostream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
1048 <<") being handled"<<std::endl;*/
1050 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1054 // Reset block usage timer
1055 block->resetUsageTimer();
1057 // Set current time as timestamp
1058 block->setTimestampNoChangedFlag(m_game_time);
1059 // If time has changed much from the one on disk,
1060 // set block to be saved when it is unloaded
1061 if(block->getTimestamp() > block->getDiskTimestamp() + 60)
1062 block->raiseModified(MOD_STATE_WRITE_AT_UNLOAD,
1063 "Timestamp older than 60s (step)");
1065 // Run node metadata
1066 bool changed = block->m_node_metadata->step(dtime);
1070 event.type = MEET_BLOCK_NODE_METADATA_CHANGED;
1072 m_map->dispatchEvent(&event);
1074 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1075 "node metadata modified in step");
1080 const float abm_interval = 1.0;
1081 if(m_active_block_modifier_interval.step(dtime, abm_interval))
1083 ScopeProfiler sp(g_profiler, "SEnv: modify in blocks avg /1s", SPT_AVG);
1084 TimeTaker timer("modify in active blocks");
1086 // Initialize handling of ActiveBlockModifiers
1087 ABMHandler abmhandler(m_abms, abm_interval, this, true);
1089 for(core::map<v3s16, bool>::Iterator
1090 i = m_active_blocks.m_list.getIterator();
1091 i.atEnd()==false; i++)
1093 v3s16 p = i.getNode()->getKey();
1095 /*infostream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
1096 <<") being handled"<<std::endl;*/
1098 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1102 // Set current time as timestamp
1103 block->setTimestampNoChangedFlag(m_game_time);
1105 /* Handle ActiveBlockModifiers */
1106 abmhandler.apply(block);
1109 u32 time_ms = timer.stop(true);
1110 u32 max_time_ms = 200;
1111 if(time_ms > max_time_ms){
1112 infostream<<"WARNING: active block modifiers took "
1113 <<time_ms<<"ms (longer than "
1114 <<max_time_ms<<"ms)"<<std::endl;
1119 Step script environment (run global on_step())
1121 scriptapi_environment_step(m_lua, dtime);
1127 ScopeProfiler sp(g_profiler, "SEnv: step act. objs avg", SPT_AVG);
1128 //TimeTaker timer("Step active objects");
1130 g_profiler->avg("SEnv: num of objects", m_active_objects.size());
1132 // This helps the objects to send data at the same time
1133 bool send_recommended = false;
1134 m_send_recommended_timer += dtime;
1135 if(m_send_recommended_timer > getSendRecommendedInterval())
1137 m_send_recommended_timer -= getSendRecommendedInterval();
1138 send_recommended = true;
1141 for(core::map<u16, ServerActiveObject*>::Iterator
1142 i = m_active_objects.getIterator();
1143 i.atEnd()==false; i++)
1145 ServerActiveObject* obj = i.getNode()->getValue();
1146 // Remove non-peaceful mobs on peaceful mode
1147 if(g_settings->getBool("only_peaceful_mobs")){
1148 if(!obj->isPeaceful())
1149 obj->m_removed = true;
1151 // Don't step if is to be removed or stored statically
1152 if(obj->m_removed || obj->m_pending_deactivation)
1155 obj->step(dtime, send_recommended);
1156 // Read messages from object
1157 while(obj->m_messages_out.size() > 0)
1159 m_active_object_messages.push_back(
1160 obj->m_messages_out.pop_front());
1166 Manage active objects
1168 if(m_object_management_interval.step(dtime, 0.5))
1170 ScopeProfiler sp(g_profiler, "SEnv: remove removed objs avg /.5s", SPT_AVG);
1172 Remove objects that satisfy (m_removed && m_known_by_count==0)
1174 removeRemovedObjects();
1178 ServerActiveObject* ServerEnvironment::getActiveObject(u16 id)
1180 core::map<u16, ServerActiveObject*>::Node *n;
1181 n = m_active_objects.find(id);
1184 return n->getValue();
1187 bool isFreeServerActiveObjectId(u16 id,
1188 core::map<u16, ServerActiveObject*> &objects)
1193 for(core::map<u16, ServerActiveObject*>::Iterator
1194 i = objects.getIterator();
1195 i.atEnd()==false; i++)
1197 if(i.getNode()->getKey() == id)
1203 u16 getFreeServerActiveObjectId(
1204 core::map<u16, ServerActiveObject*> &objects)
1209 if(isFreeServerActiveObjectId(new_id, objects))
1219 u16 ServerEnvironment::addActiveObject(ServerActiveObject *object)
1222 u16 id = addActiveObjectRaw(object, true);
1226 bool ServerEnvironment::addActiveObjectAsStatic(ServerActiveObject *obj)
1230 v3f objectpos = obj->getBasePosition();
1232 // The block in which the object resides in
1233 v3s16 blockpos_o = getNodeBlockPos(floatToInt(objectpos, BS));
1236 Update the static data
1239 // Create new static object
1240 std::string staticdata = obj->getStaticData();
1241 StaticObject s_obj(obj->getType(), objectpos, staticdata);
1242 // Add to the block where the object is located in
1243 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1244 // Get or generate the block
1245 MapBlock *block = m_map->emergeBlock(blockpos);
1247 bool succeeded = false;
1251 block->m_static_objects.insert(0, s_obj);
1252 block->raiseModified(MOD_STATE_WRITE_AT_UNLOAD,
1253 "addActiveObjectAsStatic");
1257 infostream<<"ServerEnvironment::addActiveObjectAsStatic: "
1258 <<"Could not find or generate "
1259 <<"a block for storing static object"<<std::endl;
1263 if(obj->environmentDeletes())
1270 Finds out what new objects have been added to
1271 inside a radius around a position
1273 void ServerEnvironment::getAddedActiveObjects(v3s16 pos, s16 radius,
1274 core::map<u16, bool> ¤t_objects,
1275 core::map<u16, bool> &added_objects)
1277 v3f pos_f = intToFloat(pos, BS);
1278 f32 radius_f = radius * BS;
1280 Go through the object list,
1281 - discard m_removed objects,
1282 - discard objects that are too far away,
1283 - discard objects that are found in current_objects.
1284 - add remaining objects to added_objects
1286 for(core::map<u16, ServerActiveObject*>::Iterator
1287 i = m_active_objects.getIterator();
1288 i.atEnd()==false; i++)
1290 u16 id = i.getNode()->getKey();
1292 ServerActiveObject *object = i.getNode()->getValue();
1295 // Discard if removed
1296 if(object->m_removed)
1298 if(object->unlimitedTransferDistance() == false){
1299 // Discard if too far
1300 f32 distance_f = object->getBasePosition().getDistanceFrom(pos_f);
1301 if(distance_f > radius_f)
1304 // Discard if already on current_objects
1305 core::map<u16, bool>::Node *n;
1306 n = current_objects.find(id);
1309 // Add to added_objects
1310 added_objects.insert(id, false);
1315 Finds out what objects have been removed from
1316 inside a radius around a position
1318 void ServerEnvironment::getRemovedActiveObjects(v3s16 pos, s16 radius,
1319 core::map<u16, bool> ¤t_objects,
1320 core::map<u16, bool> &removed_objects)
1322 v3f pos_f = intToFloat(pos, BS);
1323 f32 radius_f = radius * BS;
1325 Go through current_objects; object is removed if:
1326 - object is not found in m_active_objects (this is actually an
1327 error condition; objects should be set m_removed=true and removed
1328 only after all clients have been informed about removal), or
1329 - object has m_removed=true, or
1330 - object is too far away
1332 for(core::map<u16, bool>::Iterator
1333 i = current_objects.getIterator();
1334 i.atEnd()==false; i++)
1336 u16 id = i.getNode()->getKey();
1337 ServerActiveObject *object = getActiveObject(id);
1340 infostream<<"ServerEnvironment::getRemovedActiveObjects():"
1341 <<" object in current_objects is NULL"<<std::endl;
1342 removed_objects.insert(id, false);
1346 if(object->m_removed)
1348 removed_objects.insert(id, false);
1352 // If transfer distance is unlimited, don't remove
1353 if(object->unlimitedTransferDistance())
1356 f32 distance_f = object->getBasePosition().getDistanceFrom(pos_f);
1358 if(distance_f >= radius_f)
1360 removed_objects.insert(id, false);
1368 ActiveObjectMessage ServerEnvironment::getActiveObjectMessage()
1370 if(m_active_object_messages.size() == 0)
1371 return ActiveObjectMessage(0);
1373 return m_active_object_messages.pop_front();
1377 ************ Private methods *************
1380 u16 ServerEnvironment::addActiveObjectRaw(ServerActiveObject *object,
1384 if(object->getId() == 0){
1385 u16 new_id = getFreeServerActiveObjectId(m_active_objects);
1388 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1389 <<"no free ids available"<<std::endl;
1390 if(object->environmentDeletes())
1394 object->setId(new_id);
1397 verbosestream<<"ServerEnvironment::addActiveObjectRaw(): "
1398 <<"supplied with id "<<object->getId()<<std::endl;
1400 if(isFreeServerActiveObjectId(object->getId(), m_active_objects) == false)
1402 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1403 <<"id is not free ("<<object->getId()<<")"<<std::endl;
1404 if(object->environmentDeletes())
1408 /*infostream<<"ServerEnvironment::addActiveObjectRaw(): "
1409 <<"added (id="<<object->getId()<<")"<<std::endl;*/
1411 m_active_objects.insert(object->getId(), object);
1413 verbosestream<<"ServerEnvironment::addActiveObjectRaw(): "
1414 <<"Added id="<<object->getId()<<"; there are now "
1415 <<m_active_objects.size()<<" active objects."
1418 // Register reference in scripting api (must be done before post-init)
1419 scriptapi_add_object_reference(m_lua, object);
1420 // Post-initialize object
1421 object->addedToEnvironment();
1423 // Add static data to block
1424 if(object->isStaticAllowed())
1426 // Add static object to active static list of the block
1427 v3f objectpos = object->getBasePosition();
1428 std::string staticdata = object->getStaticData();
1429 StaticObject s_obj(object->getType(), objectpos, staticdata);
1430 // Add to the block where the object is located in
1431 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1432 MapBlock *block = m_map->getBlockNoCreateNoEx(blockpos);
1435 block->m_static_objects.m_active.insert(object->getId(), s_obj);
1436 object->m_static_exists = true;
1437 object->m_static_block = blockpos;
1440 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1441 "addActiveObjectRaw");
1444 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1445 <<"could not find block for storing id="<<object->getId()
1446 <<" statically"<<std::endl;
1450 return object->getId();
1454 Remove objects that satisfy (m_removed && m_known_by_count==0)
1456 void ServerEnvironment::removeRemovedObjects()
1458 core::list<u16> objects_to_remove;
1459 for(core::map<u16, ServerActiveObject*>::Iterator
1460 i = m_active_objects.getIterator();
1461 i.atEnd()==false; i++)
1463 u16 id = i.getNode()->getKey();
1464 ServerActiveObject* obj = i.getNode()->getValue();
1465 // This shouldn't happen but check it
1468 infostream<<"NULL object found in ServerEnvironment"
1469 <<" while finding removed objects. id="<<id<<std::endl;
1470 // Id to be removed from m_active_objects
1471 objects_to_remove.push_back(id);
1476 We will delete objects that are marked as removed or thatare
1477 waiting for deletion after deactivation
1479 if(obj->m_removed == false && obj->m_pending_deactivation == false)
1483 Delete static data from block if is marked as removed
1485 if(obj->m_static_exists && obj->m_removed)
1487 MapBlock *block = m_map->emergeBlock(obj->m_static_block);
1490 block->m_static_objects.remove(id);
1491 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1492 "removeRemovedObjects");
1493 obj->m_static_exists = false;
1497 // If m_known_by_count > 0, don't actually remove.
1498 if(obj->m_known_by_count > 0)
1501 // Tell the object about removal
1502 obj->removingFromEnvironment();
1503 // Deregister in scripting api
1504 scriptapi_rm_object_reference(m_lua, obj);
1507 if(obj->environmentDeletes())
1509 // Id to be removed from m_active_objects
1510 objects_to_remove.push_back(id);
1512 // Remove references from m_active_objects
1513 for(core::list<u16>::Iterator i = objects_to_remove.begin();
1514 i != objects_to_remove.end(); i++)
1516 m_active_objects.remove(*i);
1520 static void print_hexdump(std::ostream &o, const std::string &data)
1522 const int linelength = 16;
1523 for(int l=0; ; l++){
1524 int i0 = linelength * l;
1525 bool at_end = false;
1526 int thislinelength = linelength;
1527 if(i0 + thislinelength > (int)data.size()){
1528 thislinelength = data.size() - i0;
1531 for(int di=0; di<linelength; di++){
1534 if(di<thislinelength)
1535 snprintf(buf, 4, "%.2x ", data[i]);
1537 snprintf(buf, 4, " ");
1541 for(int di=0; di<thislinelength; di++){
1555 Convert stored objects from blocks near the players to active.
1557 void ServerEnvironment::activateObjects(MapBlock *block)
1561 // Ignore if no stored objects (to not set changed flag)
1562 if(block->m_static_objects.m_stored.size() == 0)
1564 verbosestream<<"ServerEnvironment::activateObjects(): "
1565 <<"activating objects of block "<<PP(block->getPos())
1566 <<" ("<<block->m_static_objects.m_stored.size()
1567 <<" objects)"<<std::endl;
1568 bool large_amount = (block->m_static_objects.m_stored.size() > 49);
1570 errorstream<<"suspiciously large amount of objects detected: "
1571 <<block->m_static_objects.m_stored.size()<<" in "
1572 <<PP(block->getPos())
1573 <<"; removing all of them."<<std::endl;
1574 // Clear stored list
1575 block->m_static_objects.m_stored.clear();
1576 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1577 "stored list cleared in activateObjects due to "
1578 "large amount of objects");
1581 // A list for objects that couldn't be converted to static for some
1582 // reason. They will be stored back.
1583 core::list<StaticObject> new_stored;
1584 // Loop through stored static objects
1585 for(core::list<StaticObject>::Iterator
1586 i = block->m_static_objects.m_stored.begin();
1587 i != block->m_static_objects.m_stored.end(); i++)
1589 /*infostream<<"Server: Creating an active object from "
1590 <<"static data"<<std::endl;*/
1591 StaticObject &s_obj = *i;
1592 // Create an active object from the data
1593 ServerActiveObject *obj = ServerActiveObject::create
1594 (s_obj.type, this, 0, s_obj.pos, s_obj.data);
1595 // If couldn't create object, store static data back.
1598 errorstream<<"ServerEnvironment::activateObjects(): "
1599 <<"failed to create active object from static object "
1600 <<"in block "<<PP(s_obj.pos/BS)
1601 <<" type="<<(int)s_obj.type<<" data:"<<std::endl;
1602 print_hexdump(verbosestream, s_obj.data);
1604 new_stored.push_back(s_obj);
1607 verbosestream<<"ServerEnvironment::activateObjects(): "
1608 <<"activated static object pos="<<PP(s_obj.pos/BS)
1609 <<" type="<<(int)s_obj.type<<std::endl;
1610 // This will also add the object to the active static list
1611 addActiveObjectRaw(obj, false);
1613 // Clear stored list
1614 block->m_static_objects.m_stored.clear();
1615 // Add leftover failed stuff to stored list
1616 for(core::list<StaticObject>::Iterator
1617 i = new_stored.begin();
1618 i != new_stored.end(); i++)
1620 StaticObject &s_obj = *i;
1621 block->m_static_objects.m_stored.push_back(s_obj);
1624 Note: Block hasn't really been modified here.
1625 The objects have just been activated and moved from the stored
1626 static list to the active static list.
1627 As such, the block is essentially the same.
1628 Thus, do not call block->raiseModified(MOD_STATE_WRITE_NEEDED).
1629 Otherwise there would be a huge amount of unnecessary I/O.
1634 Convert objects that are not standing inside active blocks to static.
1636 If m_known_by_count != 0, active object is not deleted, but static
1637 data is still updated.
1639 If force_delete is set, active object is deleted nevertheless. It
1640 shall only be set so in the destructor of the environment.
1642 void ServerEnvironment::deactivateFarObjects(bool force_delete)
1644 core::list<u16> objects_to_remove;
1645 for(core::map<u16, ServerActiveObject*>::Iterator
1646 i = m_active_objects.getIterator();
1647 i.atEnd()==false; i++)
1649 ServerActiveObject* obj = i.getNode()->getValue();
1652 // Do not deactivate if static data creation not allowed
1653 if(!force_delete && !obj->isStaticAllowed())
1656 // If pending deactivation, let removeRemovedObjects() do it
1657 if(!force_delete && obj->m_pending_deactivation)
1660 u16 id = i.getNode()->getKey();
1661 v3f objectpos = obj->getBasePosition();
1663 // The block in which the object resides in
1664 v3s16 blockpos_o = getNodeBlockPos(floatToInt(objectpos, BS));
1666 // If block is active, don't remove
1667 if(!force_delete && m_active_blocks.contains(blockpos_o))
1670 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
1671 <<"deactivating object id="<<id<<" on inactive block "
1672 <<PP(blockpos_o)<<std::endl;
1674 // If known by some client, don't immediately delete.
1675 bool pending_delete = (obj->m_known_by_count > 0 && !force_delete);
1678 Update the static data
1681 if(obj->isStaticAllowed())
1683 // Create new static object
1684 std::string staticdata_new = obj->getStaticData();
1685 StaticObject s_obj(obj->getType(), objectpos, staticdata_new);
1687 bool stays_in_same_block = false;
1688 bool data_changed = true;
1690 if(obj->m_static_exists){
1691 if(obj->m_static_block == blockpos_o)
1692 stays_in_same_block = true;
1694 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
1696 core::map<u16, StaticObject>::Node *n =
1697 block->m_static_objects.m_active.find(id);
1699 StaticObject static_old = n->getValue();
1701 float save_movem = obj->getMinimumSavedMovement();
1703 if(static_old.data == staticdata_new &&
1704 (static_old.pos - objectpos).getLength() < save_movem)
1705 data_changed = false;
1707 errorstream<<"ServerEnvironment::deactivateFarObjects(): "
1708 <<"id="<<id<<" m_static_exists=true but "
1709 <<"static data doesn't actually exist in "
1710 <<PP(obj->m_static_block)<<std::endl;
1714 bool shall_be_written = (!stays_in_same_block || data_changed);
1716 // Delete old static object
1717 if(obj->m_static_exists)
1719 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
1722 block->m_static_objects.remove(id);
1723 obj->m_static_exists = false;
1724 // Only mark block as modified if data changed considerably
1725 if(shall_be_written)
1726 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1727 "deactivateFarObjects: Static data "
1728 "changed considerably");
1732 // Add to the block where the object is located in
1733 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1734 // Get or generate the block
1735 MapBlock *block = m_map->emergeBlock(blockpos);
1739 if(block->m_static_objects.m_stored.size() >= 49){
1740 errorstream<<"ServerEnv: Trying to store id="<<obj->getId()
1741 <<" statically but block "<<PP(blockpos)
1742 <<" already contains "
1743 <<block->m_static_objects.m_stored.size()
1744 <<" (over 49) objects."
1745 <<" Forcing delete."<<std::endl;
1746 force_delete = true;
1748 u16 new_id = pending_delete ? id : 0;
1749 block->m_static_objects.insert(new_id, s_obj);
1751 // Only mark block as modified if data changed considerably
1752 if(shall_be_written)
1753 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1754 "deactivateFarObjects: Static data "
1755 "changed considerably");
1757 obj->m_static_exists = true;
1758 obj->m_static_block = block->getPos();
1763 errorstream<<"ServerEnv: Could not find or generate "
1764 <<"a block for storing id="<<obj->getId()
1765 <<" statically"<<std::endl;
1772 If known by some client, set pending deactivation.
1773 Otherwise delete it immediately.
1776 if(pending_delete && !force_delete)
1778 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
1779 <<"object id="<<id<<" is known by clients"
1780 <<"; not deleting yet"<<std::endl;
1782 obj->m_pending_deactivation = true;
1786 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
1787 <<"object id="<<id<<" is not known by clients"
1788 <<"; deleting"<<std::endl;
1790 // Tell the object about removal
1791 obj->removingFromEnvironment();
1792 // Deregister in scripting api
1793 scriptapi_rm_object_reference(m_lua, obj);
1795 // Delete active object
1796 if(obj->environmentDeletes())
1798 // Id to be removed from m_active_objects
1799 objects_to_remove.push_back(id);
1802 // Remove references from m_active_objects
1803 for(core::list<u16>::Iterator i = objects_to_remove.begin();
1804 i != objects_to_remove.end(); i++)
1806 m_active_objects.remove(*i);
1813 #include "clientsimpleobject.h"
1819 ClientEnvironment::ClientEnvironment(ClientMap *map, scene::ISceneManager *smgr,
1820 ITextureSource *texturesource, IGameDef *gamedef,
1821 IrrlichtDevice *irr):
1824 m_texturesource(texturesource),
1830 ClientEnvironment::~ClientEnvironment()
1832 // delete active objects
1833 for(core::map<u16, ClientActiveObject*>::Iterator
1834 i = m_active_objects.getIterator();
1835 i.atEnd()==false; i++)
1837 delete i.getNode()->getValue();
1840 for(core::list<ClientSimpleObject*>::Iterator
1841 i = m_simple_objects.begin(); i != m_simple_objects.end(); i++)
1850 Map & ClientEnvironment::getMap()
1855 ClientMap & ClientEnvironment::getClientMap()
1860 void ClientEnvironment::addPlayer(Player *player)
1862 DSTACK(__FUNCTION_NAME);
1864 It is a failure if player is local and there already is a local
1867 assert(!(player->isLocal() == true && getLocalPlayer() != NULL));
1869 Environment::addPlayer(player);
1872 LocalPlayer * ClientEnvironment::getLocalPlayer()
1874 for(core::list<Player*>::Iterator i = m_players.begin();
1875 i != m_players.end(); i++)
1877 Player *player = *i;
1878 if(player->isLocal())
1879 return (LocalPlayer*)player;
1884 void ClientEnvironment::step(float dtime)
1886 DSTACK(__FUNCTION_NAME);
1888 /* Step time of day */
1889 stepTimeOfDay(dtime);
1891 // Get some settings
1892 bool fly_allowed = m_gamedef->checkLocalPrivilege("fly");
1893 bool free_move = fly_allowed && g_settings->getBool("free_move");
1896 LocalPlayer *lplayer = getLocalPlayer();
1898 // collision info queue
1899 core::list<CollisionInfo> player_collisions;
1902 Get the speed the player is going
1904 bool is_climbing = lplayer->is_climbing;
1906 f32 player_speed = lplayer->getSpeed().getLength();
1909 Maximum position increment
1911 //f32 position_max_increment = 0.05*BS;
1912 f32 position_max_increment = 0.1*BS;
1914 // Maximum time increment (for collision detection etc)
1915 // time = distance / speed
1916 f32 dtime_max_increment = 1;
1917 if(player_speed > 0.001)
1918 dtime_max_increment = position_max_increment / player_speed;
1920 // Maximum time increment is 10ms or lower
1921 if(dtime_max_increment > 0.01)
1922 dtime_max_increment = 0.01;
1924 // Don't allow overly huge dtime
1928 f32 dtime_downcount = dtime;
1931 Stuff that has a maximum time increment
1940 if(dtime_downcount > dtime_max_increment)
1942 dtime_part = dtime_max_increment;
1943 dtime_downcount -= dtime_part;
1947 dtime_part = dtime_downcount;
1949 Setting this to 0 (no -=dtime_part) disables an infinite loop
1950 when dtime_part is so small that dtime_downcount -= dtime_part
1953 dtime_downcount = 0;
1961 v3f lplayerpos = lplayer->getPosition();
1964 if(free_move == false && is_climbing == false)
1967 v3f speed = lplayer->getSpeed();
1968 if(lplayer->swimming_up == false)
1969 speed.Y -= 9.81 * BS * dtime_part * 2;
1972 if(lplayer->in_water_stable || lplayer->in_water)
1974 f32 max_down = 2.0*BS;
1975 if(speed.Y < -max_down) speed.Y = -max_down;
1978 if(speed.getLength() > max)
1980 speed = speed / speed.getLength() * max;
1984 lplayer->setSpeed(speed);
1989 This also does collision detection.
1991 lplayer->move(dtime_part, *m_map, position_max_increment,
1992 &player_collisions);
1995 while(dtime_downcount > 0.001);
1997 //std::cout<<"Looped "<<loopcount<<" times."<<std::endl;
1999 for(core::list<CollisionInfo>::Iterator
2000 i = player_collisions.begin();
2001 i != player_collisions.end(); i++)
2003 CollisionInfo &info = *i;
2004 if(info.t == COLLISION_FALL)
2006 //f32 tolerance = BS*10; // 2 without damage
2007 //f32 tolerance = BS*12; // 3 without damage
2008 f32 tolerance = BS*14; // 5 without damage
2010 if(info.speed > tolerance)
2012 f32 damage_f = (info.speed - tolerance)/BS*factor;
2013 u16 damage = (u16)(damage_f+0.5);
2015 damageLocalPlayer(damage, true);
2021 A quick draft of lava damage
2023 if(m_lava_hurt_interval.step(dtime, 1.0))
2025 v3f pf = lplayer->getPosition();
2027 // Feet, middle and head
2028 v3s16 p1 = floatToInt(pf + v3f(0, BS*0.1, 0), BS);
2029 MapNode n1 = m_map->getNodeNoEx(p1);
2030 v3s16 p2 = floatToInt(pf + v3f(0, BS*0.8, 0), BS);
2031 MapNode n2 = m_map->getNodeNoEx(p2);
2032 v3s16 p3 = floatToInt(pf + v3f(0, BS*1.6, 0), BS);
2033 MapNode n3 = m_map->getNodeNoEx(p2);
2035 u32 damage_per_second = 0;
2036 damage_per_second = MYMAX(damage_per_second,
2037 m_gamedef->ndef()->get(n1).damage_per_second);
2038 damage_per_second = MYMAX(damage_per_second,
2039 m_gamedef->ndef()->get(n2).damage_per_second);
2040 damage_per_second = MYMAX(damage_per_second,
2041 m_gamedef->ndef()->get(n3).damage_per_second);
2043 if(damage_per_second != 0)
2045 damageLocalPlayer(damage_per_second, true);
2050 Stuff that can be done in an arbitarily large dtime
2052 for(core::list<Player*>::Iterator i = m_players.begin();
2053 i != m_players.end(); i++)
2055 Player *player = *i;
2056 v3f playerpos = player->getPosition();
2059 Handle non-local players
2061 if(player->isLocal() == false)
2064 player->move(dtime, *m_map, 100*BS);
2068 // Update lighting on all players on client
2069 u8 light = LIGHT_MAX;
2072 v3s16 p = player->getLightPosition();
2073 MapNode n = m_map->getNode(p);
2074 light = n.getLightBlend(getDayNightRatio(), m_gamedef->ndef());
2076 catch(InvalidPositionException &e){
2077 light = blend_light(getDayNightRatio(), LIGHT_SUN, 0);
2079 player->light = light;
2083 Step active objects and update lighting of them
2086 for(core::map<u16, ClientActiveObject*>::Iterator
2087 i = m_active_objects.getIterator();
2088 i.atEnd()==false; i++)
2090 ClientActiveObject* obj = i.getNode()->getValue();
2092 obj->step(dtime, this);
2094 if(m_active_object_light_update_interval.step(dtime, 0.21))
2100 v3s16 p = obj->getLightPosition();
2101 MapNode n = m_map->getNode(p);
2102 light = n.getLightBlend(getDayNightRatio(), m_gamedef->ndef());
2104 catch(InvalidPositionException &e){
2105 light = blend_light(getDayNightRatio(), LIGHT_SUN, 0);
2107 obj->updateLight(light);
2112 Step and handle simple objects
2114 for(core::list<ClientSimpleObject*>::Iterator
2115 i = m_simple_objects.begin(); i != m_simple_objects.end();)
2117 ClientSimpleObject *simple = *i;
2118 core::list<ClientSimpleObject*>::Iterator cur = i;
2120 simple->step(dtime);
2121 if(simple->m_to_be_removed){
2123 m_simple_objects.erase(cur);
2128 void ClientEnvironment::addSimpleObject(ClientSimpleObject *simple)
2130 m_simple_objects.push_back(simple);
2133 ClientActiveObject* ClientEnvironment::getActiveObject(u16 id)
2135 core::map<u16, ClientActiveObject*>::Node *n;
2136 n = m_active_objects.find(id);
2139 return n->getValue();
2142 bool isFreeClientActiveObjectId(u16 id,
2143 core::map<u16, ClientActiveObject*> &objects)
2148 for(core::map<u16, ClientActiveObject*>::Iterator
2149 i = objects.getIterator();
2150 i.atEnd()==false; i++)
2152 if(i.getNode()->getKey() == id)
2158 u16 getFreeClientActiveObjectId(
2159 core::map<u16, ClientActiveObject*> &objects)
2164 if(isFreeClientActiveObjectId(new_id, objects))
2174 u16 ClientEnvironment::addActiveObject(ClientActiveObject *object)
2177 if(object->getId() == 0)
2179 u16 new_id = getFreeClientActiveObjectId(m_active_objects);
2182 infostream<<"ClientEnvironment::addActiveObject(): "
2183 <<"no free ids available"<<std::endl;
2187 object->setId(new_id);
2189 if(isFreeClientActiveObjectId(object->getId(), m_active_objects) == false)
2191 infostream<<"ClientEnvironment::addActiveObject(): "
2192 <<"id is not free ("<<object->getId()<<")"<<std::endl;
2196 infostream<<"ClientEnvironment::addActiveObject(): "
2197 <<"added (id="<<object->getId()<<")"<<std::endl;
2198 m_active_objects.insert(object->getId(), object);
2199 object->addToScene(m_smgr, m_texturesource, m_irr);
2200 { // Update lighting immediately
2204 v3s16 p = object->getLightPosition();
2205 MapNode n = m_map->getNode(p);
2206 light = n.getLightBlend(getDayNightRatio(), m_gamedef->ndef());
2208 catch(InvalidPositionException &e){
2209 light = blend_light(getDayNightRatio(), LIGHT_SUN, 0);
2211 object->updateLight(light);
2213 return object->getId();
2216 void ClientEnvironment::addActiveObject(u16 id, u8 type,
2217 const std::string &init_data)
2219 ClientActiveObject* obj =
2220 ClientActiveObject::create(type, m_gamedef, this);
2223 infostream<<"ClientEnvironment::addActiveObject(): "
2224 <<"id="<<id<<" type="<<type<<": Couldn't create object"
2233 obj->initialize(init_data);
2235 catch(SerializationError &e)
2237 errorstream<<"ClientEnvironment::addActiveObject():"
2238 <<" id="<<id<<" type="<<type
2239 <<": SerializationError in initialize(),"
2240 <<" init_data="<<serializeJsonString(init_data)
2244 addActiveObject(obj);
2247 void ClientEnvironment::removeActiveObject(u16 id)
2249 verbosestream<<"ClientEnvironment::removeActiveObject(): "
2250 <<"id="<<id<<std::endl;
2251 ClientActiveObject* obj = getActiveObject(id);
2254 infostream<<"ClientEnvironment::removeActiveObject(): "
2255 <<"id="<<id<<" not found"<<std::endl;
2258 obj->removeFromScene();
2260 m_active_objects.remove(id);
2263 void ClientEnvironment::processActiveObjectMessage(u16 id,
2264 const std::string &data)
2266 ClientActiveObject* obj = getActiveObject(id);
2269 infostream<<"ClientEnvironment::processActiveObjectMessage():"
2270 <<" got message for id="<<id<<", which doesn't exist."
2276 obj->processMessage(data);
2278 catch(SerializationError &e)
2280 errorstream<<"ClientEnvironment::processActiveObjectMessage():"
2281 <<" id="<<id<<" type="<<obj->getType()
2282 <<" SerializationError in processMessage(),"
2283 <<" message="<<serializeJsonString(data)
2289 Callbacks for activeobjects
2292 void ClientEnvironment::damageLocalPlayer(u8 damage, bool handle_hp)
2294 LocalPlayer *lplayer = getLocalPlayer();
2298 if(lplayer->hp > damage)
2299 lplayer->hp -= damage;
2304 ClientEnvEvent event;
2305 event.type = CEE_PLAYER_DAMAGE;
2306 event.player_damage.amount = damage;
2307 event.player_damage.send_to_server = handle_hp;
2308 m_client_event_queue.push_back(event);
2312 Client likes to call these
2315 void ClientEnvironment::getActiveObjects(v3f origin, f32 max_d,
2316 core::array<DistanceSortedActiveObject> &dest)
2318 for(core::map<u16, ClientActiveObject*>::Iterator
2319 i = m_active_objects.getIterator();
2320 i.atEnd()==false; i++)
2322 ClientActiveObject* obj = i.getNode()->getValue();
2324 f32 d = (obj->getPosition() - origin).getLength();
2329 DistanceSortedActiveObject dso(obj, d);
2331 dest.push_back(dso);
2335 ClientEnvEvent ClientEnvironment::getClientEvent()
2337 if(m_client_event_queue.size() == 0)
2339 ClientEnvEvent event;
2340 event.type = CEE_NONE;
2343 return m_client_event_queue.pop_front();
2346 #endif // #ifndef SERVER