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;
636 float intervals = actual_interval / trigger_interval;
639 float chance = abm->getTriggerChance();
644 aabm.chance = chance / intervals;
648 std::set<std::string> required_neighbors_s
649 = abm->getRequiredNeighbors();
650 for(std::set<std::string>::iterator
651 i = required_neighbors_s.begin();
652 i != required_neighbors_s.end(); i++)
654 ndef->getIds(*i, aabm.required_neighbors);
657 std::set<std::string> contents_s = abm->getTriggerContents();
658 for(std::set<std::string>::iterator
659 i = contents_s.begin(); i != contents_s.end(); i++)
661 std::set<content_t> ids;
662 ndef->getIds(*i, ids);
663 for(std::set<content_t>::const_iterator k = ids.begin();
667 std::map<content_t, std::list<ActiveABM> >::iterator j;
669 if(j == m_aabms.end()){
670 std::list<ActiveABM> aabmlist;
671 m_aabms[c] = aabmlist;
674 j->second.push_back(aabm);
679 void apply(MapBlock *block)
684 ServerMap *map = &m_env->getServerMap();
687 for(p0.X=0; p0.X<MAP_BLOCKSIZE; p0.X++)
688 for(p0.Y=0; p0.Y<MAP_BLOCKSIZE; p0.Y++)
689 for(p0.Z=0; p0.Z<MAP_BLOCKSIZE; p0.Z++)
691 MapNode n = block->getNodeNoEx(p0);
692 content_t c = n.getContent();
693 v3s16 p = p0 + block->getPosRelative();
695 std::map<content_t, std::list<ActiveABM> >::iterator j;
697 if(j == m_aabms.end())
700 for(std::list<ActiveABM>::iterator
701 i = j->second.begin(); i != j->second.end(); i++)
703 if(myrand() % i->chance != 0)
707 if(!i->required_neighbors.empty())
710 for(p1.X = p.X-1; p1.X <= p.X+1; p1.X++)
711 for(p1.Y = p.Y-1; p1.Y <= p.Y+1; p1.Y++)
712 for(p1.Z = p.Z-1; p1.Z <= p.Z+1; p1.Z++)
716 MapNode n = map->getNodeNoEx(p1);
717 content_t c = n.getContent();
718 std::set<content_t>::const_iterator k;
719 k = i->required_neighbors.find(c);
720 if(k != i->required_neighbors.end()){
724 // No required neighbor found
729 // Find out how many objects the block contains
730 u32 active_object_count = block->m_static_objects.m_active.size();
731 // Find out how many objects this and all the neighbors contain
732 u32 active_object_count_wider = 0;
733 for(s16 x=-1; x<=1; x++)
734 for(s16 y=-1; y<=1; y++)
735 for(s16 z=-1; z<=1; z++)
737 MapBlock *block2 = map->getBlockNoCreateNoEx(
738 block->getPos() + v3s16(x,y,z));
741 active_object_count_wider +=
742 block2->m_static_objects.m_active.size()
743 + block2->m_static_objects.m_stored.size();
746 // Call all the trigger variations
747 i->abm->trigger(m_env, p, n);
748 i->abm->trigger(m_env, p, n,
749 active_object_count, active_object_count_wider);
755 void ServerEnvironment::activateBlock(MapBlock *block, u32 additional_dtime)
757 // Get time difference
759 u32 stamp = block->getTimestamp();
760 if(m_game_time > stamp && stamp != BLOCK_TIMESTAMP_UNDEFINED)
761 dtime_s = m_game_time - block->getTimestamp();
762 dtime_s += additional_dtime;
764 /*infostream<<"ServerEnvironment::activateBlock(): block timestamp: "
765 <<stamp<<", game time: "<<m_game_time<<std::endl;*/
767 // Set current time as timestamp
768 block->setTimestampNoChangedFlag(m_game_time);
770 /*infostream<<"ServerEnvironment::activateBlock(): block is "
771 <<dtime_s<<" seconds old."<<std::endl;*/
773 // Activate stored objects
774 activateObjects(block);
777 std::map<v3s16, f32> elapsed_timers =
778 block->m_node_timers.step((float)dtime_s);
779 if(!elapsed_timers.empty())
780 errorstream<<"Node timers don't work yet!"<<std::endl;
782 /* Handle ActiveBlockModifiers */
783 ABMHandler abmhandler(m_abms, dtime_s, this, false);
784 abmhandler.apply(block);
787 void ServerEnvironment::addActiveBlockModifier(ActiveBlockModifier *abm)
789 m_abms.push_back(ABMWithState(abm));
792 std::set<u16> ServerEnvironment::getObjectsInsideRadius(v3f pos, float radius)
794 std::set<u16> objects;
795 for(core::map<u16, ServerActiveObject*>::Iterator
796 i = m_active_objects.getIterator();
797 i.atEnd()==false; i++)
799 ServerActiveObject* obj = i.getNode()->getValue();
800 u16 id = i.getNode()->getKey();
801 v3f objectpos = obj->getBasePosition();
802 if(objectpos.getDistanceFrom(pos) > radius)
809 void ServerEnvironment::clearAllObjects()
811 infostream<<"ServerEnvironment::clearAllObjects(): "
812 <<"Removing all active objects"<<std::endl;
813 core::list<u16> objects_to_remove;
814 for(core::map<u16, ServerActiveObject*>::Iterator
815 i = m_active_objects.getIterator();
816 i.atEnd()==false; i++)
818 ServerActiveObject* obj = i.getNode()->getValue();
819 if(obj->getType() == ACTIVEOBJECT_TYPE_PLAYER)
821 u16 id = i.getNode()->getKey();
822 v3f objectpos = obj->getBasePosition();
823 // Delete static object if block is loaded
824 if(obj->m_static_exists){
825 MapBlock *block = m_map->getBlockNoCreateNoEx(obj->m_static_block);
827 block->m_static_objects.remove(id);
828 block->raiseModified(MOD_STATE_WRITE_NEEDED,
830 obj->m_static_exists = false;
833 // If known by some client, don't delete immediately
834 if(obj->m_known_by_count > 0){
835 obj->m_pending_deactivation = true;
836 obj->m_removed = true;
840 // Tell the object about removal
841 obj->removingFromEnvironment();
842 // Deregister in scripting api
843 scriptapi_rm_object_reference(m_lua, obj);
845 // Delete active object
846 if(obj->environmentDeletes())
848 // Id to be removed from m_active_objects
849 objects_to_remove.push_back(id);
851 // Remove references from m_active_objects
852 for(core::list<u16>::Iterator i = objects_to_remove.begin();
853 i != objects_to_remove.end(); i++)
855 m_active_objects.remove(*i);
858 core::list<v3s16> loadable_blocks;
859 infostream<<"ServerEnvironment::clearAllObjects(): "
860 <<"Listing all loadable blocks"<<std::endl;
861 m_map->listAllLoadableBlocks(loadable_blocks);
862 infostream<<"ServerEnvironment::clearAllObjects(): "
863 <<"Done listing all loadable blocks: "
864 <<loadable_blocks.size()
865 <<", now clearing"<<std::endl;
866 u32 report_interval = loadable_blocks.size() / 10;
867 u32 num_blocks_checked = 0;
868 u32 num_blocks_cleared = 0;
869 u32 num_objs_cleared = 0;
870 for(core::list<v3s16>::Iterator i = loadable_blocks.begin();
871 i != loadable_blocks.end(); i++)
874 MapBlock *block = m_map->emergeBlock(p, false);
876 errorstream<<"ServerEnvironment::clearAllObjects(): "
877 <<"Failed to emerge block "<<PP(p)<<std::endl;
880 u32 num_stored = block->m_static_objects.m_stored.size();
881 u32 num_active = block->m_static_objects.m_active.size();
882 if(num_stored != 0 || num_active != 0){
883 block->m_static_objects.m_stored.clear();
884 block->m_static_objects.m_active.clear();
885 block->raiseModified(MOD_STATE_WRITE_NEEDED,
887 num_objs_cleared += num_stored + num_active;
888 num_blocks_cleared++;
890 num_blocks_checked++;
892 if(num_blocks_checked % report_interval == 0){
893 float percent = 100.0 * (float)num_blocks_checked /
894 loadable_blocks.size();
895 infostream<<"ServerEnvironment::clearAllObjects(): "
896 <<"Cleared "<<num_objs_cleared<<" objects"
897 <<" in "<<num_blocks_cleared<<" blocks ("
898 <<percent<<"%)"<<std::endl;
901 infostream<<"ServerEnvironment::clearAllObjects(): "
902 <<"Finished: Cleared "<<num_objs_cleared<<" objects"
903 <<" in "<<num_blocks_cleared<<" blocks"<<std::endl;
906 void ServerEnvironment::step(float dtime)
908 DSTACK(__FUNCTION_NAME);
910 //TimeTaker timer("ServerEnv step");
912 /* Step time of day */
913 stepTimeOfDay(dtime);
919 m_game_time_fraction_counter += dtime;
920 u32 inc_i = (u32)m_game_time_fraction_counter;
921 m_game_time += inc_i;
922 m_game_time_fraction_counter -= (float)inc_i;
929 ScopeProfiler sp(g_profiler, "SEnv: handle players avg", SPT_AVG);
930 for(core::list<Player*>::Iterator i = m_players.begin();
931 i != m_players.end(); i++)
935 // Ignore disconnected players
936 if(player->peer_id == 0)
939 v3f playerpos = player->getPosition();
942 player->move(dtime, *m_map, 100*BS);
947 Manage active block list
949 if(m_active_blocks_management_interval.step(dtime, 2.0))
951 ScopeProfiler sp(g_profiler, "SEnv: manage act. block list avg /2s", SPT_AVG);
953 Get player block positions
955 core::list<v3s16> players_blockpos;
956 for(core::list<Player*>::Iterator
957 i = m_players.begin();
958 i != m_players.end(); i++)
961 // Ignore disconnected players
962 if(player->peer_id == 0)
964 v3s16 blockpos = getNodeBlockPos(
965 floatToInt(player->getPosition(), BS));
966 players_blockpos.push_back(blockpos);
970 Update list of active blocks, collecting changes
972 const s16 active_block_range = g_settings->getS16("active_block_range");
973 core::map<v3s16, bool> blocks_removed;
974 core::map<v3s16, bool> blocks_added;
975 m_active_blocks.update(players_blockpos, active_block_range,
976 blocks_removed, blocks_added);
979 Handle removed blocks
982 // Convert active objects that are no more in active blocks to static
983 deactivateFarObjects(false);
985 for(core::map<v3s16, bool>::Iterator
986 i = blocks_removed.getIterator();
987 i.atEnd()==false; i++)
989 v3s16 p = i.getNode()->getKey();
991 /*infostream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
992 <<") became inactive"<<std::endl;*/
994 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
998 // Set current time as timestamp (and let it set ChangedFlag)
999 block->setTimestamp(m_game_time);
1006 for(core::map<v3s16, bool>::Iterator
1007 i = blocks_added.getIterator();
1008 i.atEnd()==false; i++)
1010 v3s16 p = i.getNode()->getKey();
1012 /*infostream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
1013 <<") became active"<<std::endl;*/
1015 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1017 // Block needs to be fetched first
1018 m_emerger->queueBlockEmerge(p, false);
1019 m_active_blocks.m_list.remove(p);
1023 activateBlock(block);
1028 Mess around in active blocks
1030 if(m_active_blocks_nodemetadata_interval.step(dtime, 1.0))
1032 ScopeProfiler sp(g_profiler, "SEnv: mess in act. blocks avg /1s", SPT_AVG);
1036 for(core::map<v3s16, bool>::Iterator
1037 i = m_active_blocks.m_list.getIterator();
1038 i.atEnd()==false; i++)
1040 v3s16 p = i.getNode()->getKey();
1042 /*infostream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
1043 <<") being handled"<<std::endl;*/
1045 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1049 // Reset block usage timer
1050 block->resetUsageTimer();
1052 // Set current time as timestamp
1053 block->setTimestampNoChangedFlag(m_game_time);
1054 // If time has changed much from the one on disk,
1055 // set block to be saved when it is unloaded
1056 if(block->getTimestamp() > block->getDiskTimestamp() + 60)
1057 block->raiseModified(MOD_STATE_WRITE_AT_UNLOAD,
1058 "Timestamp older than 60s (step)");
1061 std::map<v3s16, f32> elapsed_timers =
1062 block->m_node_timers.step(dtime);
1063 if(!elapsed_timers.empty())
1064 errorstream<<"Node timers don't work yet!"<<std::endl;
1068 const float abm_interval = 1.0;
1069 if(m_active_block_modifier_interval.step(dtime, abm_interval))
1071 ScopeProfiler sp(g_profiler, "SEnv: modify in blocks avg /1s", SPT_AVG);
1072 TimeTaker timer("modify in active blocks");
1074 // Initialize handling of ActiveBlockModifiers
1075 ABMHandler abmhandler(m_abms, abm_interval, this, true);
1077 for(core::map<v3s16, bool>::Iterator
1078 i = m_active_blocks.m_list.getIterator();
1079 i.atEnd()==false; i++)
1081 v3s16 p = i.getNode()->getKey();
1083 /*infostream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
1084 <<") being handled"<<std::endl;*/
1086 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1090 // Set current time as timestamp
1091 block->setTimestampNoChangedFlag(m_game_time);
1093 /* Handle ActiveBlockModifiers */
1094 abmhandler.apply(block);
1097 u32 time_ms = timer.stop(true);
1098 u32 max_time_ms = 200;
1099 if(time_ms > max_time_ms){
1100 infostream<<"WARNING: active block modifiers took "
1101 <<time_ms<<"ms (longer than "
1102 <<max_time_ms<<"ms)"<<std::endl;
1107 Step script environment (run global on_step())
1109 scriptapi_environment_step(m_lua, dtime);
1115 ScopeProfiler sp(g_profiler, "SEnv: step act. objs avg", SPT_AVG);
1116 //TimeTaker timer("Step active objects");
1118 g_profiler->avg("SEnv: num of objects", m_active_objects.size());
1120 // This helps the objects to send data at the same time
1121 bool send_recommended = false;
1122 m_send_recommended_timer += dtime;
1123 if(m_send_recommended_timer > getSendRecommendedInterval())
1125 m_send_recommended_timer -= getSendRecommendedInterval();
1126 send_recommended = true;
1129 for(core::map<u16, ServerActiveObject*>::Iterator
1130 i = m_active_objects.getIterator();
1131 i.atEnd()==false; i++)
1133 ServerActiveObject* obj = i.getNode()->getValue();
1134 // Remove non-peaceful mobs on peaceful mode
1135 if(g_settings->getBool("only_peaceful_mobs")){
1136 if(!obj->isPeaceful())
1137 obj->m_removed = true;
1139 // Don't step if is to be removed or stored statically
1140 if(obj->m_removed || obj->m_pending_deactivation)
1143 obj->step(dtime, send_recommended);
1144 // Read messages from object
1145 while(obj->m_messages_out.size() > 0)
1147 m_active_object_messages.push_back(
1148 obj->m_messages_out.pop_front());
1154 Manage active objects
1156 if(m_object_management_interval.step(dtime, 0.5))
1158 ScopeProfiler sp(g_profiler, "SEnv: remove removed objs avg /.5s", SPT_AVG);
1160 Remove objects that satisfy (m_removed && m_known_by_count==0)
1162 removeRemovedObjects();
1166 ServerActiveObject* ServerEnvironment::getActiveObject(u16 id)
1168 core::map<u16, ServerActiveObject*>::Node *n;
1169 n = m_active_objects.find(id);
1172 return n->getValue();
1175 bool isFreeServerActiveObjectId(u16 id,
1176 core::map<u16, ServerActiveObject*> &objects)
1181 for(core::map<u16, ServerActiveObject*>::Iterator
1182 i = objects.getIterator();
1183 i.atEnd()==false; i++)
1185 if(i.getNode()->getKey() == id)
1191 u16 getFreeServerActiveObjectId(
1192 core::map<u16, ServerActiveObject*> &objects)
1197 if(isFreeServerActiveObjectId(new_id, objects))
1207 u16 ServerEnvironment::addActiveObject(ServerActiveObject *object)
1210 u16 id = addActiveObjectRaw(object, true);
1214 bool ServerEnvironment::addActiveObjectAsStatic(ServerActiveObject *obj)
1218 v3f objectpos = obj->getBasePosition();
1220 // The block in which the object resides in
1221 v3s16 blockpos_o = getNodeBlockPos(floatToInt(objectpos, BS));
1224 Update the static data
1227 // Create new static object
1228 std::string staticdata = obj->getStaticData();
1229 StaticObject s_obj(obj->getType(), objectpos, staticdata);
1230 // Add to the block where the object is located in
1231 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1232 // Get or generate the block
1233 MapBlock *block = m_map->emergeBlock(blockpos);
1235 bool succeeded = false;
1239 block->m_static_objects.insert(0, s_obj);
1240 block->raiseModified(MOD_STATE_WRITE_AT_UNLOAD,
1241 "addActiveObjectAsStatic");
1245 infostream<<"ServerEnvironment::addActiveObjectAsStatic: "
1246 <<"Could not find or generate "
1247 <<"a block for storing static object"<<std::endl;
1251 if(obj->environmentDeletes())
1258 Finds out what new objects have been added to
1259 inside a radius around a position
1261 void ServerEnvironment::getAddedActiveObjects(v3s16 pos, s16 radius,
1262 core::map<u16, bool> ¤t_objects,
1263 core::map<u16, bool> &added_objects)
1265 v3f pos_f = intToFloat(pos, BS);
1266 f32 radius_f = radius * BS;
1268 Go through the object list,
1269 - discard m_removed objects,
1270 - discard objects that are too far away,
1271 - discard objects that are found in current_objects.
1272 - add remaining objects to added_objects
1274 for(core::map<u16, ServerActiveObject*>::Iterator
1275 i = m_active_objects.getIterator();
1276 i.atEnd()==false; i++)
1278 u16 id = i.getNode()->getKey();
1280 ServerActiveObject *object = i.getNode()->getValue();
1283 // Discard if removed
1284 if(object->m_removed)
1286 if(object->unlimitedTransferDistance() == false){
1287 // Discard if too far
1288 f32 distance_f = object->getBasePosition().getDistanceFrom(pos_f);
1289 if(distance_f > radius_f)
1292 // Discard if already on current_objects
1293 core::map<u16, bool>::Node *n;
1294 n = current_objects.find(id);
1297 // Add to added_objects
1298 added_objects.insert(id, false);
1303 Finds out what objects have been removed from
1304 inside a radius around a position
1306 void ServerEnvironment::getRemovedActiveObjects(v3s16 pos, s16 radius,
1307 core::map<u16, bool> ¤t_objects,
1308 core::map<u16, bool> &removed_objects)
1310 v3f pos_f = intToFloat(pos, BS);
1311 f32 radius_f = radius * BS;
1313 Go through current_objects; object is removed if:
1314 - object is not found in m_active_objects (this is actually an
1315 error condition; objects should be set m_removed=true and removed
1316 only after all clients have been informed about removal), or
1317 - object has m_removed=true, or
1318 - object is too far away
1320 for(core::map<u16, bool>::Iterator
1321 i = current_objects.getIterator();
1322 i.atEnd()==false; i++)
1324 u16 id = i.getNode()->getKey();
1325 ServerActiveObject *object = getActiveObject(id);
1328 infostream<<"ServerEnvironment::getRemovedActiveObjects():"
1329 <<" object in current_objects is NULL"<<std::endl;
1330 removed_objects.insert(id, false);
1334 if(object->m_removed)
1336 removed_objects.insert(id, false);
1340 // If transfer distance is unlimited, don't remove
1341 if(object->unlimitedTransferDistance())
1344 f32 distance_f = object->getBasePosition().getDistanceFrom(pos_f);
1346 if(distance_f >= radius_f)
1348 removed_objects.insert(id, false);
1356 ActiveObjectMessage ServerEnvironment::getActiveObjectMessage()
1358 if(m_active_object_messages.size() == 0)
1359 return ActiveObjectMessage(0);
1361 return m_active_object_messages.pop_front();
1365 ************ Private methods *************
1368 u16 ServerEnvironment::addActiveObjectRaw(ServerActiveObject *object,
1372 if(object->getId() == 0){
1373 u16 new_id = getFreeServerActiveObjectId(m_active_objects);
1376 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1377 <<"no free ids available"<<std::endl;
1378 if(object->environmentDeletes())
1382 object->setId(new_id);
1385 verbosestream<<"ServerEnvironment::addActiveObjectRaw(): "
1386 <<"supplied with id "<<object->getId()<<std::endl;
1388 if(isFreeServerActiveObjectId(object->getId(), m_active_objects) == false)
1390 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1391 <<"id is not free ("<<object->getId()<<")"<<std::endl;
1392 if(object->environmentDeletes())
1396 /*infostream<<"ServerEnvironment::addActiveObjectRaw(): "
1397 <<"added (id="<<object->getId()<<")"<<std::endl;*/
1399 m_active_objects.insert(object->getId(), object);
1401 verbosestream<<"ServerEnvironment::addActiveObjectRaw(): "
1402 <<"Added id="<<object->getId()<<"; there are now "
1403 <<m_active_objects.size()<<" active objects."
1406 // Register reference in scripting api (must be done before post-init)
1407 scriptapi_add_object_reference(m_lua, object);
1408 // Post-initialize object
1409 object->addedToEnvironment();
1411 // Add static data to block
1412 if(object->isStaticAllowed())
1414 // Add static object to active static list of the block
1415 v3f objectpos = object->getBasePosition();
1416 std::string staticdata = object->getStaticData();
1417 StaticObject s_obj(object->getType(), objectpos, staticdata);
1418 // Add to the block where the object is located in
1419 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1420 MapBlock *block = m_map->getBlockNoCreateNoEx(blockpos);
1423 block->m_static_objects.m_active.insert(object->getId(), s_obj);
1424 object->m_static_exists = true;
1425 object->m_static_block = blockpos;
1428 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1429 "addActiveObjectRaw");
1432 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1433 <<"could not find block for storing id="<<object->getId()
1434 <<" statically"<<std::endl;
1438 return object->getId();
1442 Remove objects that satisfy (m_removed && m_known_by_count==0)
1444 void ServerEnvironment::removeRemovedObjects()
1446 core::list<u16> objects_to_remove;
1447 for(core::map<u16, ServerActiveObject*>::Iterator
1448 i = m_active_objects.getIterator();
1449 i.atEnd()==false; i++)
1451 u16 id = i.getNode()->getKey();
1452 ServerActiveObject* obj = i.getNode()->getValue();
1453 // This shouldn't happen but check it
1456 infostream<<"NULL object found in ServerEnvironment"
1457 <<" while finding removed objects. id="<<id<<std::endl;
1458 // Id to be removed from m_active_objects
1459 objects_to_remove.push_back(id);
1464 We will delete objects that are marked as removed or thatare
1465 waiting for deletion after deactivation
1467 if(obj->m_removed == false && obj->m_pending_deactivation == false)
1471 Delete static data from block if is marked as removed
1473 if(obj->m_static_exists && obj->m_removed)
1475 MapBlock *block = m_map->emergeBlock(obj->m_static_block);
1478 block->m_static_objects.remove(id);
1479 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1480 "removeRemovedObjects");
1481 obj->m_static_exists = false;
1485 // If m_known_by_count > 0, don't actually remove.
1486 if(obj->m_known_by_count > 0)
1489 // Tell the object about removal
1490 obj->removingFromEnvironment();
1491 // Deregister in scripting api
1492 scriptapi_rm_object_reference(m_lua, obj);
1495 if(obj->environmentDeletes())
1497 // Id to be removed from m_active_objects
1498 objects_to_remove.push_back(id);
1500 // Remove references from m_active_objects
1501 for(core::list<u16>::Iterator i = objects_to_remove.begin();
1502 i != objects_to_remove.end(); i++)
1504 m_active_objects.remove(*i);
1508 static void print_hexdump(std::ostream &o, const std::string &data)
1510 const int linelength = 16;
1511 for(int l=0; ; l++){
1512 int i0 = linelength * l;
1513 bool at_end = false;
1514 int thislinelength = linelength;
1515 if(i0 + thislinelength > (int)data.size()){
1516 thislinelength = data.size() - i0;
1519 for(int di=0; di<linelength; di++){
1522 if(di<thislinelength)
1523 snprintf(buf, 4, "%.2x ", data[i]);
1525 snprintf(buf, 4, " ");
1529 for(int di=0; di<thislinelength; di++){
1543 Convert stored objects from blocks near the players to active.
1545 void ServerEnvironment::activateObjects(MapBlock *block)
1549 // Ignore if no stored objects (to not set changed flag)
1550 if(block->m_static_objects.m_stored.size() == 0)
1552 verbosestream<<"ServerEnvironment::activateObjects(): "
1553 <<"activating objects of block "<<PP(block->getPos())
1554 <<" ("<<block->m_static_objects.m_stored.size()
1555 <<" objects)"<<std::endl;
1556 bool large_amount = (block->m_static_objects.m_stored.size() > 49);
1558 errorstream<<"suspiciously large amount of objects detected: "
1559 <<block->m_static_objects.m_stored.size()<<" in "
1560 <<PP(block->getPos())
1561 <<"; removing all of them."<<std::endl;
1562 // Clear stored list
1563 block->m_static_objects.m_stored.clear();
1564 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1565 "stored list cleared in activateObjects due to "
1566 "large amount of objects");
1569 // A list for objects that couldn't be converted to static for some
1570 // reason. They will be stored back.
1571 core::list<StaticObject> new_stored;
1572 // Loop through stored static objects
1573 for(core::list<StaticObject>::Iterator
1574 i = block->m_static_objects.m_stored.begin();
1575 i != block->m_static_objects.m_stored.end(); i++)
1577 /*infostream<<"Server: Creating an active object from "
1578 <<"static data"<<std::endl;*/
1579 StaticObject &s_obj = *i;
1580 // Create an active object from the data
1581 ServerActiveObject *obj = ServerActiveObject::create
1582 (s_obj.type, this, 0, s_obj.pos, s_obj.data);
1583 // If couldn't create object, store static data back.
1586 errorstream<<"ServerEnvironment::activateObjects(): "
1587 <<"failed to create active object from static object "
1588 <<"in block "<<PP(s_obj.pos/BS)
1589 <<" type="<<(int)s_obj.type<<" data:"<<std::endl;
1590 print_hexdump(verbosestream, s_obj.data);
1592 new_stored.push_back(s_obj);
1595 verbosestream<<"ServerEnvironment::activateObjects(): "
1596 <<"activated static object pos="<<PP(s_obj.pos/BS)
1597 <<" type="<<(int)s_obj.type<<std::endl;
1598 // This will also add the object to the active static list
1599 addActiveObjectRaw(obj, false);
1601 // Clear stored list
1602 block->m_static_objects.m_stored.clear();
1603 // Add leftover failed stuff to stored list
1604 for(core::list<StaticObject>::Iterator
1605 i = new_stored.begin();
1606 i != new_stored.end(); i++)
1608 StaticObject &s_obj = *i;
1609 block->m_static_objects.m_stored.push_back(s_obj);
1612 Note: Block hasn't really been modified here.
1613 The objects have just been activated and moved from the stored
1614 static list to the active static list.
1615 As such, the block is essentially the same.
1616 Thus, do not call block->raiseModified(MOD_STATE_WRITE_NEEDED).
1617 Otherwise there would be a huge amount of unnecessary I/O.
1622 Convert objects that are not standing inside active blocks to static.
1624 If m_known_by_count != 0, active object is not deleted, but static
1625 data is still updated.
1627 If force_delete is set, active object is deleted nevertheless. It
1628 shall only be set so in the destructor of the environment.
1630 void ServerEnvironment::deactivateFarObjects(bool force_delete)
1632 core::list<u16> objects_to_remove;
1633 for(core::map<u16, ServerActiveObject*>::Iterator
1634 i = m_active_objects.getIterator();
1635 i.atEnd()==false; i++)
1637 ServerActiveObject* obj = i.getNode()->getValue();
1640 // Do not deactivate if static data creation not allowed
1641 if(!force_delete && !obj->isStaticAllowed())
1644 // If pending deactivation, let removeRemovedObjects() do it
1645 if(!force_delete && obj->m_pending_deactivation)
1648 u16 id = i.getNode()->getKey();
1649 v3f objectpos = obj->getBasePosition();
1651 // The block in which the object resides in
1652 v3s16 blockpos_o = getNodeBlockPos(floatToInt(objectpos, BS));
1654 // If block is active, don't remove
1655 if(!force_delete && m_active_blocks.contains(blockpos_o))
1658 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
1659 <<"deactivating object id="<<id<<" on inactive block "
1660 <<PP(blockpos_o)<<std::endl;
1662 // If known by some client, don't immediately delete.
1663 bool pending_delete = (obj->m_known_by_count > 0 && !force_delete);
1666 Update the static data
1669 if(obj->isStaticAllowed())
1671 // Create new static object
1672 std::string staticdata_new = obj->getStaticData();
1673 StaticObject s_obj(obj->getType(), objectpos, staticdata_new);
1675 bool stays_in_same_block = false;
1676 bool data_changed = true;
1678 if(obj->m_static_exists){
1679 if(obj->m_static_block == blockpos_o)
1680 stays_in_same_block = true;
1682 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
1684 core::map<u16, StaticObject>::Node *n =
1685 block->m_static_objects.m_active.find(id);
1687 StaticObject static_old = n->getValue();
1689 float save_movem = obj->getMinimumSavedMovement();
1691 if(static_old.data == staticdata_new &&
1692 (static_old.pos - objectpos).getLength() < save_movem)
1693 data_changed = false;
1695 errorstream<<"ServerEnvironment::deactivateFarObjects(): "
1696 <<"id="<<id<<" m_static_exists=true but "
1697 <<"static data doesn't actually exist in "
1698 <<PP(obj->m_static_block)<<std::endl;
1702 bool shall_be_written = (!stays_in_same_block || data_changed);
1704 // Delete old static object
1705 if(obj->m_static_exists)
1707 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
1710 block->m_static_objects.remove(id);
1711 obj->m_static_exists = false;
1712 // Only mark block as modified if data changed considerably
1713 if(shall_be_written)
1714 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1715 "deactivateFarObjects: Static data "
1716 "changed considerably");
1720 // Add to the block where the object is located in
1721 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1722 // Get or generate the block
1723 MapBlock *block = m_map->emergeBlock(blockpos);
1727 if(block->m_static_objects.m_stored.size() >= 49){
1728 errorstream<<"ServerEnv: Trying to store id="<<obj->getId()
1729 <<" statically but block "<<PP(blockpos)
1730 <<" already contains "
1731 <<block->m_static_objects.m_stored.size()
1732 <<" (over 49) objects."
1733 <<" Forcing delete."<<std::endl;
1734 force_delete = true;
1736 u16 new_id = pending_delete ? id : 0;
1737 // If static counterpart already exists, remove it first.
1738 // This shouldn't happen, but happens rarely for some
1739 // unknown reason. Unsuccessful attempts have been made to
1740 // find said reason.
1741 if(new_id && block->m_static_objects.m_active.find(new_id)){
1742 infostream<<"ServerEnv: WARNING: Performing hack #83274"
1744 block->m_static_objects.remove(new_id);
1746 block->m_static_objects.insert(new_id, s_obj);
1748 // Only mark block as modified if data changed considerably
1749 if(shall_be_written)
1750 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1751 "deactivateFarObjects: Static data "
1752 "changed considerably");
1754 obj->m_static_exists = true;
1755 obj->m_static_block = block->getPos();
1760 errorstream<<"ServerEnv: Could not find or generate "
1761 <<"a block for storing id="<<obj->getId()
1762 <<" statically"<<std::endl;
1769 If known by some client, set pending deactivation.
1770 Otherwise delete it immediately.
1773 if(pending_delete && !force_delete)
1775 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
1776 <<"object id="<<id<<" is known by clients"
1777 <<"; not deleting yet"<<std::endl;
1779 obj->m_pending_deactivation = true;
1783 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
1784 <<"object id="<<id<<" is not known by clients"
1785 <<"; deleting"<<std::endl;
1787 // Tell the object about removal
1788 obj->removingFromEnvironment();
1789 // Deregister in scripting api
1790 scriptapi_rm_object_reference(m_lua, obj);
1792 // Delete active object
1793 if(obj->environmentDeletes())
1795 // Id to be removed from m_active_objects
1796 objects_to_remove.push_back(id);
1799 // Remove references from m_active_objects
1800 for(core::list<u16>::Iterator i = objects_to_remove.begin();
1801 i != objects_to_remove.end(); i++)
1803 m_active_objects.remove(*i);
1810 #include "clientsimpleobject.h"
1816 ClientEnvironment::ClientEnvironment(ClientMap *map, scene::ISceneManager *smgr,
1817 ITextureSource *texturesource, IGameDef *gamedef,
1818 IrrlichtDevice *irr):
1821 m_texturesource(texturesource),
1827 ClientEnvironment::~ClientEnvironment()
1829 // delete active objects
1830 for(core::map<u16, ClientActiveObject*>::Iterator
1831 i = m_active_objects.getIterator();
1832 i.atEnd()==false; i++)
1834 delete i.getNode()->getValue();
1837 for(core::list<ClientSimpleObject*>::Iterator
1838 i = m_simple_objects.begin(); i != m_simple_objects.end(); i++)
1847 Map & ClientEnvironment::getMap()
1852 ClientMap & ClientEnvironment::getClientMap()
1857 void ClientEnvironment::addPlayer(Player *player)
1859 DSTACK(__FUNCTION_NAME);
1861 It is a failure if player is local and there already is a local
1864 assert(!(player->isLocal() == true && getLocalPlayer() != NULL));
1866 Environment::addPlayer(player);
1869 LocalPlayer * ClientEnvironment::getLocalPlayer()
1871 for(core::list<Player*>::Iterator i = m_players.begin();
1872 i != m_players.end(); i++)
1874 Player *player = *i;
1875 if(player->isLocal())
1876 return (LocalPlayer*)player;
1881 void ClientEnvironment::step(float dtime)
1883 DSTACK(__FUNCTION_NAME);
1885 /* Step time of day */
1886 stepTimeOfDay(dtime);
1888 // Get some settings
1889 bool fly_allowed = m_gamedef->checkLocalPrivilege("fly");
1890 bool free_move = fly_allowed && g_settings->getBool("free_move");
1893 LocalPlayer *lplayer = getLocalPlayer();
1895 // collision info queue
1896 core::list<CollisionInfo> player_collisions;
1899 Get the speed the player is going
1901 bool is_climbing = lplayer->is_climbing;
1903 f32 player_speed = lplayer->getSpeed().getLength();
1906 Maximum position increment
1908 //f32 position_max_increment = 0.05*BS;
1909 f32 position_max_increment = 0.1*BS;
1911 // Maximum time increment (for collision detection etc)
1912 // time = distance / speed
1913 f32 dtime_max_increment = 1;
1914 if(player_speed > 0.001)
1915 dtime_max_increment = position_max_increment / player_speed;
1917 // Maximum time increment is 10ms or lower
1918 if(dtime_max_increment > 0.01)
1919 dtime_max_increment = 0.01;
1921 // Don't allow overly huge dtime
1925 f32 dtime_downcount = dtime;
1928 Stuff that has a maximum time increment
1937 if(dtime_downcount > dtime_max_increment)
1939 dtime_part = dtime_max_increment;
1940 dtime_downcount -= dtime_part;
1944 dtime_part = dtime_downcount;
1946 Setting this to 0 (no -=dtime_part) disables an infinite loop
1947 when dtime_part is so small that dtime_downcount -= dtime_part
1950 dtime_downcount = 0;
1958 v3f lplayerpos = lplayer->getPosition();
1961 if(free_move == false && is_climbing == false)
1964 v3f speed = lplayer->getSpeed();
1965 if(lplayer->swimming_up == false)
1966 speed.Y -= 9.81 * BS * dtime_part * 2;
1969 if(lplayer->in_water_stable || lplayer->in_water)
1971 f32 max_down = 2.0*BS;
1972 if(speed.Y < -max_down) speed.Y = -max_down;
1975 if(speed.getLength() > max)
1977 speed = speed / speed.getLength() * max;
1981 lplayer->setSpeed(speed);
1986 This also does collision detection.
1988 lplayer->move(dtime_part, *m_map, position_max_increment,
1989 &player_collisions);
1992 while(dtime_downcount > 0.001);
1994 //std::cout<<"Looped "<<loopcount<<" times."<<std::endl;
1996 for(core::list<CollisionInfo>::Iterator
1997 i = player_collisions.begin();
1998 i != player_collisions.end(); i++)
2000 CollisionInfo &info = *i;
2001 if(info.t == COLLISION_FALL)
2003 //f32 tolerance = BS*10; // 2 without damage
2004 //f32 tolerance = BS*12; // 3 without damage
2005 f32 tolerance = BS*14; // 5 without damage
2007 if(info.speed > tolerance)
2009 f32 damage_f = (info.speed - tolerance)/BS*factor;
2010 u16 damage = (u16)(damage_f+0.5);
2012 damageLocalPlayer(damage, true);
2018 A quick draft of lava damage
2020 if(m_lava_hurt_interval.step(dtime, 1.0))
2022 v3f pf = lplayer->getPosition();
2024 // Feet, middle and head
2025 v3s16 p1 = floatToInt(pf + v3f(0, BS*0.1, 0), BS);
2026 MapNode n1 = m_map->getNodeNoEx(p1);
2027 v3s16 p2 = floatToInt(pf + v3f(0, BS*0.8, 0), BS);
2028 MapNode n2 = m_map->getNodeNoEx(p2);
2029 v3s16 p3 = floatToInt(pf + v3f(0, BS*1.6, 0), BS);
2030 MapNode n3 = m_map->getNodeNoEx(p2);
2032 u32 damage_per_second = 0;
2033 damage_per_second = MYMAX(damage_per_second,
2034 m_gamedef->ndef()->get(n1).damage_per_second);
2035 damage_per_second = MYMAX(damage_per_second,
2036 m_gamedef->ndef()->get(n2).damage_per_second);
2037 damage_per_second = MYMAX(damage_per_second,
2038 m_gamedef->ndef()->get(n3).damage_per_second);
2040 if(damage_per_second != 0)
2042 damageLocalPlayer(damage_per_second, true);
2047 Stuff that can be done in an arbitarily large dtime
2049 for(core::list<Player*>::Iterator i = m_players.begin();
2050 i != m_players.end(); i++)
2052 Player *player = *i;
2053 v3f playerpos = player->getPosition();
2056 Handle non-local players
2058 if(player->isLocal() == false)
2061 player->move(dtime, *m_map, 100*BS);
2065 // Update lighting on all players on client
2066 u8 light = LIGHT_MAX;
2069 v3s16 p = player->getLightPosition();
2070 MapNode n = m_map->getNode(p);
2071 light = n.getLightBlend(getDayNightRatio(), m_gamedef->ndef());
2073 catch(InvalidPositionException &e){
2074 light = blend_light(getDayNightRatio(), LIGHT_SUN, 0);
2076 player->light = light;
2080 Step active objects and update lighting of them
2083 for(core::map<u16, ClientActiveObject*>::Iterator
2084 i = m_active_objects.getIterator();
2085 i.atEnd()==false; i++)
2087 ClientActiveObject* obj = i.getNode()->getValue();
2089 obj->step(dtime, this);
2091 if(m_active_object_light_update_interval.step(dtime, 0.21))
2097 v3s16 p = obj->getLightPosition();
2098 MapNode n = m_map->getNode(p);
2099 light = n.getLightBlend(getDayNightRatio(), m_gamedef->ndef());
2101 catch(InvalidPositionException &e){
2102 light = blend_light(getDayNightRatio(), LIGHT_SUN, 0);
2104 obj->updateLight(light);
2109 Step and handle simple objects
2111 for(core::list<ClientSimpleObject*>::Iterator
2112 i = m_simple_objects.begin(); i != m_simple_objects.end();)
2114 ClientSimpleObject *simple = *i;
2115 core::list<ClientSimpleObject*>::Iterator cur = i;
2117 simple->step(dtime);
2118 if(simple->m_to_be_removed){
2120 m_simple_objects.erase(cur);
2125 void ClientEnvironment::addSimpleObject(ClientSimpleObject *simple)
2127 m_simple_objects.push_back(simple);
2130 ClientActiveObject* ClientEnvironment::getActiveObject(u16 id)
2132 core::map<u16, ClientActiveObject*>::Node *n;
2133 n = m_active_objects.find(id);
2136 return n->getValue();
2139 bool isFreeClientActiveObjectId(u16 id,
2140 core::map<u16, ClientActiveObject*> &objects)
2145 for(core::map<u16, ClientActiveObject*>::Iterator
2146 i = objects.getIterator();
2147 i.atEnd()==false; i++)
2149 if(i.getNode()->getKey() == id)
2155 u16 getFreeClientActiveObjectId(
2156 core::map<u16, ClientActiveObject*> &objects)
2161 if(isFreeClientActiveObjectId(new_id, objects))
2171 u16 ClientEnvironment::addActiveObject(ClientActiveObject *object)
2174 if(object->getId() == 0)
2176 u16 new_id = getFreeClientActiveObjectId(m_active_objects);
2179 infostream<<"ClientEnvironment::addActiveObject(): "
2180 <<"no free ids available"<<std::endl;
2184 object->setId(new_id);
2186 if(isFreeClientActiveObjectId(object->getId(), m_active_objects) == false)
2188 infostream<<"ClientEnvironment::addActiveObject(): "
2189 <<"id is not free ("<<object->getId()<<")"<<std::endl;
2193 infostream<<"ClientEnvironment::addActiveObject(): "
2194 <<"added (id="<<object->getId()<<")"<<std::endl;
2195 m_active_objects.insert(object->getId(), object);
2196 object->addToScene(m_smgr, m_texturesource, m_irr);
2197 { // Update lighting immediately
2201 v3s16 p = object->getLightPosition();
2202 MapNode n = m_map->getNode(p);
2203 light = n.getLightBlend(getDayNightRatio(), m_gamedef->ndef());
2205 catch(InvalidPositionException &e){
2206 light = blend_light(getDayNightRatio(), LIGHT_SUN, 0);
2208 object->updateLight(light);
2210 return object->getId();
2213 void ClientEnvironment::addActiveObject(u16 id, u8 type,
2214 const std::string &init_data)
2216 ClientActiveObject* obj =
2217 ClientActiveObject::create(type, m_gamedef, this);
2220 infostream<<"ClientEnvironment::addActiveObject(): "
2221 <<"id="<<id<<" type="<<type<<": Couldn't create object"
2230 obj->initialize(init_data);
2232 catch(SerializationError &e)
2234 errorstream<<"ClientEnvironment::addActiveObject():"
2235 <<" id="<<id<<" type="<<type
2236 <<": SerializationError in initialize(),"
2237 <<" init_data="<<serializeJsonString(init_data)
2241 addActiveObject(obj);
2244 void ClientEnvironment::removeActiveObject(u16 id)
2246 verbosestream<<"ClientEnvironment::removeActiveObject(): "
2247 <<"id="<<id<<std::endl;
2248 ClientActiveObject* obj = getActiveObject(id);
2251 infostream<<"ClientEnvironment::removeActiveObject(): "
2252 <<"id="<<id<<" not found"<<std::endl;
2255 obj->removeFromScene();
2257 m_active_objects.remove(id);
2260 void ClientEnvironment::processActiveObjectMessage(u16 id,
2261 const std::string &data)
2263 ClientActiveObject* obj = getActiveObject(id);
2266 infostream<<"ClientEnvironment::processActiveObjectMessage():"
2267 <<" got message for id="<<id<<", which doesn't exist."
2273 obj->processMessage(data);
2275 catch(SerializationError &e)
2277 errorstream<<"ClientEnvironment::processActiveObjectMessage():"
2278 <<" id="<<id<<" type="<<obj->getType()
2279 <<" SerializationError in processMessage(),"
2280 <<" message="<<serializeJsonString(data)
2286 Callbacks for activeobjects
2289 void ClientEnvironment::damageLocalPlayer(u8 damage, bool handle_hp)
2291 LocalPlayer *lplayer = getLocalPlayer();
2295 if(lplayer->hp > damage)
2296 lplayer->hp -= damage;
2301 ClientEnvEvent event;
2302 event.type = CEE_PLAYER_DAMAGE;
2303 event.player_damage.amount = damage;
2304 event.player_damage.send_to_server = handle_hp;
2305 m_client_event_queue.push_back(event);
2309 Client likes to call these
2312 void ClientEnvironment::getActiveObjects(v3f origin, f32 max_d,
2313 core::array<DistanceSortedActiveObject> &dest)
2315 for(core::map<u16, ClientActiveObject*>::Iterator
2316 i = m_active_objects.getIterator();
2317 i.atEnd()==false; i++)
2319 ClientActiveObject* obj = i.getNode()->getValue();
2321 f32 d = (obj->getPosition() - origin).getLength();
2326 DistanceSortedActiveObject dso(obj, d);
2328 dest.push_back(dso);
2332 ClientEnvEvent ClientEnvironment::getClientEvent()
2334 if(m_client_event_queue.size() == 0)
2336 ClientEnvEvent event;
2337 event.type = CEE_NONE;
2340 return m_client_event_queue.pop_front();
2343 #endif // #ifndef SERVER