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 bool changed = block->m_node_metadata->step((float)dtime_s);
781 event.type = MEET_BLOCK_NODE_METADATA_CHANGED;
782 event.p = block->getPos();
783 m_map->dispatchEvent(&event);
785 block->raiseModified(MOD_STATE_WRITE_NEEDED,
786 "node metadata modified in activateBlock");
789 /* Handle ActiveBlockModifiers */
790 ABMHandler abmhandler(m_abms, dtime_s, this, false);
791 abmhandler.apply(block);
794 void ServerEnvironment::addActiveBlockModifier(ActiveBlockModifier *abm)
796 m_abms.push_back(ABMWithState(abm));
799 std::set<u16> ServerEnvironment::getObjectsInsideRadius(v3f pos, float radius)
801 std::set<u16> objects;
802 for(core::map<u16, ServerActiveObject*>::Iterator
803 i = m_active_objects.getIterator();
804 i.atEnd()==false; i++)
806 ServerActiveObject* obj = i.getNode()->getValue();
807 u16 id = i.getNode()->getKey();
808 v3f objectpos = obj->getBasePosition();
809 if(objectpos.getDistanceFrom(pos) > radius)
816 void ServerEnvironment::clearAllObjects()
818 infostream<<"ServerEnvironment::clearAllObjects(): "
819 <<"Removing all active objects"<<std::endl;
820 core::list<u16> objects_to_remove;
821 for(core::map<u16, ServerActiveObject*>::Iterator
822 i = m_active_objects.getIterator();
823 i.atEnd()==false; i++)
825 ServerActiveObject* obj = i.getNode()->getValue();
826 if(obj->getType() == ACTIVEOBJECT_TYPE_PLAYER)
828 u16 id = i.getNode()->getKey();
829 v3f objectpos = obj->getBasePosition();
830 // Delete static object if block is loaded
831 if(obj->m_static_exists){
832 MapBlock *block = m_map->getBlockNoCreateNoEx(obj->m_static_block);
834 block->m_static_objects.remove(id);
835 block->raiseModified(MOD_STATE_WRITE_NEEDED,
837 obj->m_static_exists = false;
840 // If known by some client, don't delete immediately
841 if(obj->m_known_by_count > 0){
842 obj->m_pending_deactivation = true;
843 obj->m_removed = true;
847 // Tell the object about removal
848 obj->removingFromEnvironment();
849 // Deregister in scripting api
850 scriptapi_rm_object_reference(m_lua, obj);
852 // Delete active object
853 if(obj->environmentDeletes())
855 // Id to be removed from m_active_objects
856 objects_to_remove.push_back(id);
858 // Remove references from m_active_objects
859 for(core::list<u16>::Iterator i = objects_to_remove.begin();
860 i != objects_to_remove.end(); i++)
862 m_active_objects.remove(*i);
865 core::list<v3s16> loadable_blocks;
866 infostream<<"ServerEnvironment::clearAllObjects(): "
867 <<"Listing all loadable blocks"<<std::endl;
868 m_map->listAllLoadableBlocks(loadable_blocks);
869 infostream<<"ServerEnvironment::clearAllObjects(): "
870 <<"Done listing all loadable blocks: "
871 <<loadable_blocks.size()
872 <<", now clearing"<<std::endl;
873 u32 report_interval = loadable_blocks.size() / 10;
874 u32 num_blocks_checked = 0;
875 u32 num_blocks_cleared = 0;
876 u32 num_objs_cleared = 0;
877 for(core::list<v3s16>::Iterator i = loadable_blocks.begin();
878 i != loadable_blocks.end(); i++)
881 MapBlock *block = m_map->emergeBlock(p, false);
883 errorstream<<"ServerEnvironment::clearAllObjects(): "
884 <<"Failed to emerge block "<<PP(p)<<std::endl;
887 u32 num_stored = block->m_static_objects.m_stored.size();
888 u32 num_active = block->m_static_objects.m_active.size();
889 if(num_stored != 0 || num_active != 0){
890 block->m_static_objects.m_stored.clear();
891 block->m_static_objects.m_active.clear();
892 block->raiseModified(MOD_STATE_WRITE_NEEDED,
894 num_objs_cleared += num_stored + num_active;
895 num_blocks_cleared++;
897 num_blocks_checked++;
899 if(num_blocks_checked % report_interval == 0){
900 float percent = 100.0 * (float)num_blocks_checked /
901 loadable_blocks.size();
902 infostream<<"ServerEnvironment::clearAllObjects(): "
903 <<"Cleared "<<num_objs_cleared<<" objects"
904 <<" in "<<num_blocks_cleared<<" blocks ("
905 <<percent<<"%)"<<std::endl;
908 infostream<<"ServerEnvironment::clearAllObjects(): "
909 <<"Finished: Cleared "<<num_objs_cleared<<" objects"
910 <<" in "<<num_blocks_cleared<<" blocks"<<std::endl;
913 void ServerEnvironment::step(float dtime)
915 DSTACK(__FUNCTION_NAME);
917 //TimeTaker timer("ServerEnv step");
919 /* Step time of day */
920 stepTimeOfDay(dtime);
926 m_game_time_fraction_counter += dtime;
927 u32 inc_i = (u32)m_game_time_fraction_counter;
928 m_game_time += inc_i;
929 m_game_time_fraction_counter -= (float)inc_i;
936 ScopeProfiler sp(g_profiler, "SEnv: handle players avg", SPT_AVG);
937 for(core::list<Player*>::Iterator i = m_players.begin();
938 i != m_players.end(); i++)
942 // Ignore disconnected players
943 if(player->peer_id == 0)
946 v3f playerpos = player->getPosition();
949 player->move(dtime, *m_map, 100*BS);
954 Manage active block list
956 if(m_active_blocks_management_interval.step(dtime, 2.0))
958 ScopeProfiler sp(g_profiler, "SEnv: manage act. block list avg /2s", SPT_AVG);
960 Get player block positions
962 core::list<v3s16> players_blockpos;
963 for(core::list<Player*>::Iterator
964 i = m_players.begin();
965 i != m_players.end(); i++)
968 // Ignore disconnected players
969 if(player->peer_id == 0)
971 v3s16 blockpos = getNodeBlockPos(
972 floatToInt(player->getPosition(), BS));
973 players_blockpos.push_back(blockpos);
977 Update list of active blocks, collecting changes
979 const s16 active_block_range = g_settings->getS16("active_block_range");
980 core::map<v3s16, bool> blocks_removed;
981 core::map<v3s16, bool> blocks_added;
982 m_active_blocks.update(players_blockpos, active_block_range,
983 blocks_removed, blocks_added);
986 Handle removed blocks
989 // Convert active objects that are no more in active blocks to static
990 deactivateFarObjects(false);
992 for(core::map<v3s16, bool>::Iterator
993 i = blocks_removed.getIterator();
994 i.atEnd()==false; i++)
996 v3s16 p = i.getNode()->getKey();
998 /*infostream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
999 <<") became inactive"<<std::endl;*/
1001 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1005 // Set current time as timestamp (and let it set ChangedFlag)
1006 block->setTimestamp(m_game_time);
1013 for(core::map<v3s16, bool>::Iterator
1014 i = blocks_added.getIterator();
1015 i.atEnd()==false; i++)
1017 v3s16 p = i.getNode()->getKey();
1019 /*infostream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
1020 <<") became active"<<std::endl;*/
1022 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1024 // Block needs to be fetched first
1025 m_emerger->queueBlockEmerge(p, false);
1026 m_active_blocks.m_list.remove(p);
1030 activateBlock(block);
1035 Mess around in active blocks
1037 if(m_active_blocks_nodemetadata_interval.step(dtime, 1.0))
1039 ScopeProfiler sp(g_profiler, "SEnv: mess in act. blocks avg /1s", SPT_AVG);
1043 for(core::map<v3s16, bool>::Iterator
1044 i = m_active_blocks.m_list.getIterator();
1045 i.atEnd()==false; i++)
1047 v3s16 p = i.getNode()->getKey();
1049 /*infostream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
1050 <<") being handled"<<std::endl;*/
1052 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1056 // Reset block usage timer
1057 block->resetUsageTimer();
1059 // Set current time as timestamp
1060 block->setTimestampNoChangedFlag(m_game_time);
1061 // If time has changed much from the one on disk,
1062 // set block to be saved when it is unloaded
1063 if(block->getTimestamp() > block->getDiskTimestamp() + 60)
1064 block->raiseModified(MOD_STATE_WRITE_AT_UNLOAD,
1065 "Timestamp older than 60s (step)");
1067 // Run node metadata
1068 bool changed = block->m_node_metadata->step(dtime);
1072 event.type = MEET_BLOCK_NODE_METADATA_CHANGED;
1074 m_map->dispatchEvent(&event);
1076 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1077 "node metadata modified in step");
1082 const float abm_interval = 1.0;
1083 if(m_active_block_modifier_interval.step(dtime, abm_interval))
1085 ScopeProfiler sp(g_profiler, "SEnv: modify in blocks avg /1s", SPT_AVG);
1086 TimeTaker timer("modify in active blocks");
1088 // Initialize handling of ActiveBlockModifiers
1089 ABMHandler abmhandler(m_abms, abm_interval, this, true);
1091 for(core::map<v3s16, bool>::Iterator
1092 i = m_active_blocks.m_list.getIterator();
1093 i.atEnd()==false; i++)
1095 v3s16 p = i.getNode()->getKey();
1097 /*infostream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
1098 <<") being handled"<<std::endl;*/
1100 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1104 // Set current time as timestamp
1105 block->setTimestampNoChangedFlag(m_game_time);
1107 /* Handle ActiveBlockModifiers */
1108 abmhandler.apply(block);
1111 u32 time_ms = timer.stop(true);
1112 u32 max_time_ms = 200;
1113 if(time_ms > max_time_ms){
1114 infostream<<"WARNING: active block modifiers took "
1115 <<time_ms<<"ms (longer than "
1116 <<max_time_ms<<"ms)"<<std::endl;
1121 Step script environment (run global on_step())
1123 scriptapi_environment_step(m_lua, dtime);
1129 ScopeProfiler sp(g_profiler, "SEnv: step act. objs avg", SPT_AVG);
1130 //TimeTaker timer("Step active objects");
1132 g_profiler->avg("SEnv: num of objects", m_active_objects.size());
1134 // This helps the objects to send data at the same time
1135 bool send_recommended = false;
1136 m_send_recommended_timer += dtime;
1137 if(m_send_recommended_timer > getSendRecommendedInterval())
1139 m_send_recommended_timer -= getSendRecommendedInterval();
1140 send_recommended = true;
1143 for(core::map<u16, ServerActiveObject*>::Iterator
1144 i = m_active_objects.getIterator();
1145 i.atEnd()==false; i++)
1147 ServerActiveObject* obj = i.getNode()->getValue();
1148 // Remove non-peaceful mobs on peaceful mode
1149 if(g_settings->getBool("only_peaceful_mobs")){
1150 if(!obj->isPeaceful())
1151 obj->m_removed = true;
1153 // Don't step if is to be removed or stored statically
1154 if(obj->m_removed || obj->m_pending_deactivation)
1157 obj->step(dtime, send_recommended);
1158 // Read messages from object
1159 while(obj->m_messages_out.size() > 0)
1161 m_active_object_messages.push_back(
1162 obj->m_messages_out.pop_front());
1168 Manage active objects
1170 if(m_object_management_interval.step(dtime, 0.5))
1172 ScopeProfiler sp(g_profiler, "SEnv: remove removed objs avg /.5s", SPT_AVG);
1174 Remove objects that satisfy (m_removed && m_known_by_count==0)
1176 removeRemovedObjects();
1180 ServerActiveObject* ServerEnvironment::getActiveObject(u16 id)
1182 core::map<u16, ServerActiveObject*>::Node *n;
1183 n = m_active_objects.find(id);
1186 return n->getValue();
1189 bool isFreeServerActiveObjectId(u16 id,
1190 core::map<u16, ServerActiveObject*> &objects)
1195 for(core::map<u16, ServerActiveObject*>::Iterator
1196 i = objects.getIterator();
1197 i.atEnd()==false; i++)
1199 if(i.getNode()->getKey() == id)
1205 u16 getFreeServerActiveObjectId(
1206 core::map<u16, ServerActiveObject*> &objects)
1211 if(isFreeServerActiveObjectId(new_id, objects))
1221 u16 ServerEnvironment::addActiveObject(ServerActiveObject *object)
1224 u16 id = addActiveObjectRaw(object, true);
1228 bool ServerEnvironment::addActiveObjectAsStatic(ServerActiveObject *obj)
1232 v3f objectpos = obj->getBasePosition();
1234 // The block in which the object resides in
1235 v3s16 blockpos_o = getNodeBlockPos(floatToInt(objectpos, BS));
1238 Update the static data
1241 // Create new static object
1242 std::string staticdata = obj->getStaticData();
1243 StaticObject s_obj(obj->getType(), objectpos, staticdata);
1244 // Add to the block where the object is located in
1245 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1246 // Get or generate the block
1247 MapBlock *block = m_map->emergeBlock(blockpos);
1249 bool succeeded = false;
1253 block->m_static_objects.insert(0, s_obj);
1254 block->raiseModified(MOD_STATE_WRITE_AT_UNLOAD,
1255 "addActiveObjectAsStatic");
1259 infostream<<"ServerEnvironment::addActiveObjectAsStatic: "
1260 <<"Could not find or generate "
1261 <<"a block for storing static object"<<std::endl;
1265 if(obj->environmentDeletes())
1272 Finds out what new objects have been added to
1273 inside a radius around a position
1275 void ServerEnvironment::getAddedActiveObjects(v3s16 pos, s16 radius,
1276 core::map<u16, bool> ¤t_objects,
1277 core::map<u16, bool> &added_objects)
1279 v3f pos_f = intToFloat(pos, BS);
1280 f32 radius_f = radius * BS;
1282 Go through the object list,
1283 - discard m_removed objects,
1284 - discard objects that are too far away,
1285 - discard objects that are found in current_objects.
1286 - add remaining objects to added_objects
1288 for(core::map<u16, ServerActiveObject*>::Iterator
1289 i = m_active_objects.getIterator();
1290 i.atEnd()==false; i++)
1292 u16 id = i.getNode()->getKey();
1294 ServerActiveObject *object = i.getNode()->getValue();
1297 // Discard if removed
1298 if(object->m_removed)
1300 if(object->unlimitedTransferDistance() == false){
1301 // Discard if too far
1302 f32 distance_f = object->getBasePosition().getDistanceFrom(pos_f);
1303 if(distance_f > radius_f)
1306 // Discard if already on current_objects
1307 core::map<u16, bool>::Node *n;
1308 n = current_objects.find(id);
1311 // Add to added_objects
1312 added_objects.insert(id, false);
1317 Finds out what objects have been removed from
1318 inside a radius around a position
1320 void ServerEnvironment::getRemovedActiveObjects(v3s16 pos, s16 radius,
1321 core::map<u16, bool> ¤t_objects,
1322 core::map<u16, bool> &removed_objects)
1324 v3f pos_f = intToFloat(pos, BS);
1325 f32 radius_f = radius * BS;
1327 Go through current_objects; object is removed if:
1328 - object is not found in m_active_objects (this is actually an
1329 error condition; objects should be set m_removed=true and removed
1330 only after all clients have been informed about removal), or
1331 - object has m_removed=true, or
1332 - object is too far away
1334 for(core::map<u16, bool>::Iterator
1335 i = current_objects.getIterator();
1336 i.atEnd()==false; i++)
1338 u16 id = i.getNode()->getKey();
1339 ServerActiveObject *object = getActiveObject(id);
1342 infostream<<"ServerEnvironment::getRemovedActiveObjects():"
1343 <<" object in current_objects is NULL"<<std::endl;
1344 removed_objects.insert(id, false);
1348 if(object->m_removed)
1350 removed_objects.insert(id, false);
1354 // If transfer distance is unlimited, don't remove
1355 if(object->unlimitedTransferDistance())
1358 f32 distance_f = object->getBasePosition().getDistanceFrom(pos_f);
1360 if(distance_f >= radius_f)
1362 removed_objects.insert(id, false);
1370 ActiveObjectMessage ServerEnvironment::getActiveObjectMessage()
1372 if(m_active_object_messages.size() == 0)
1373 return ActiveObjectMessage(0);
1375 return m_active_object_messages.pop_front();
1379 ************ Private methods *************
1382 u16 ServerEnvironment::addActiveObjectRaw(ServerActiveObject *object,
1386 if(object->getId() == 0){
1387 u16 new_id = getFreeServerActiveObjectId(m_active_objects);
1390 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1391 <<"no free ids available"<<std::endl;
1392 if(object->environmentDeletes())
1396 object->setId(new_id);
1399 verbosestream<<"ServerEnvironment::addActiveObjectRaw(): "
1400 <<"supplied with id "<<object->getId()<<std::endl;
1402 if(isFreeServerActiveObjectId(object->getId(), m_active_objects) == false)
1404 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1405 <<"id is not free ("<<object->getId()<<")"<<std::endl;
1406 if(object->environmentDeletes())
1410 /*infostream<<"ServerEnvironment::addActiveObjectRaw(): "
1411 <<"added (id="<<object->getId()<<")"<<std::endl;*/
1413 m_active_objects.insert(object->getId(), object);
1415 verbosestream<<"ServerEnvironment::addActiveObjectRaw(): "
1416 <<"Added id="<<object->getId()<<"; there are now "
1417 <<m_active_objects.size()<<" active objects."
1420 // Register reference in scripting api (must be done before post-init)
1421 scriptapi_add_object_reference(m_lua, object);
1422 // Post-initialize object
1423 object->addedToEnvironment();
1425 // Add static data to block
1426 if(object->isStaticAllowed())
1428 // Add static object to active static list of the block
1429 v3f objectpos = object->getBasePosition();
1430 std::string staticdata = object->getStaticData();
1431 StaticObject s_obj(object->getType(), objectpos, staticdata);
1432 // Add to the block where the object is located in
1433 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1434 MapBlock *block = m_map->getBlockNoCreateNoEx(blockpos);
1437 block->m_static_objects.m_active.insert(object->getId(), s_obj);
1438 object->m_static_exists = true;
1439 object->m_static_block = blockpos;
1442 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1443 "addActiveObjectRaw");
1446 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1447 <<"could not find block for storing id="<<object->getId()
1448 <<" statically"<<std::endl;
1452 return object->getId();
1456 Remove objects that satisfy (m_removed && m_known_by_count==0)
1458 void ServerEnvironment::removeRemovedObjects()
1460 core::list<u16> objects_to_remove;
1461 for(core::map<u16, ServerActiveObject*>::Iterator
1462 i = m_active_objects.getIterator();
1463 i.atEnd()==false; i++)
1465 u16 id = i.getNode()->getKey();
1466 ServerActiveObject* obj = i.getNode()->getValue();
1467 // This shouldn't happen but check it
1470 infostream<<"NULL object found in ServerEnvironment"
1471 <<" while finding removed objects. id="<<id<<std::endl;
1472 // Id to be removed from m_active_objects
1473 objects_to_remove.push_back(id);
1478 We will delete objects that are marked as removed or thatare
1479 waiting for deletion after deactivation
1481 if(obj->m_removed == false && obj->m_pending_deactivation == false)
1485 Delete static data from block if is marked as removed
1487 if(obj->m_static_exists && obj->m_removed)
1489 MapBlock *block = m_map->emergeBlock(obj->m_static_block);
1492 block->m_static_objects.remove(id);
1493 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1494 "removeRemovedObjects");
1495 obj->m_static_exists = false;
1499 // If m_known_by_count > 0, don't actually remove.
1500 if(obj->m_known_by_count > 0)
1503 // Tell the object about removal
1504 obj->removingFromEnvironment();
1505 // Deregister in scripting api
1506 scriptapi_rm_object_reference(m_lua, obj);
1509 if(obj->environmentDeletes())
1511 // Id to be removed from m_active_objects
1512 objects_to_remove.push_back(id);
1514 // Remove references from m_active_objects
1515 for(core::list<u16>::Iterator i = objects_to_remove.begin();
1516 i != objects_to_remove.end(); i++)
1518 m_active_objects.remove(*i);
1522 static void print_hexdump(std::ostream &o, const std::string &data)
1524 const int linelength = 16;
1525 for(int l=0; ; l++){
1526 int i0 = linelength * l;
1527 bool at_end = false;
1528 int thislinelength = linelength;
1529 if(i0 + thislinelength > (int)data.size()){
1530 thislinelength = data.size() - i0;
1533 for(int di=0; di<linelength; di++){
1536 if(di<thislinelength)
1537 snprintf(buf, 4, "%.2x ", data[i]);
1539 snprintf(buf, 4, " ");
1543 for(int di=0; di<thislinelength; di++){
1557 Convert stored objects from blocks near the players to active.
1559 void ServerEnvironment::activateObjects(MapBlock *block)
1563 // Ignore if no stored objects (to not set changed flag)
1564 if(block->m_static_objects.m_stored.size() == 0)
1566 verbosestream<<"ServerEnvironment::activateObjects(): "
1567 <<"activating objects of block "<<PP(block->getPos())
1568 <<" ("<<block->m_static_objects.m_stored.size()
1569 <<" objects)"<<std::endl;
1570 bool large_amount = (block->m_static_objects.m_stored.size() > 49);
1572 errorstream<<"suspiciously large amount of objects detected: "
1573 <<block->m_static_objects.m_stored.size()<<" in "
1574 <<PP(block->getPos())
1575 <<"; removing all of them."<<std::endl;
1576 // Clear stored list
1577 block->m_static_objects.m_stored.clear();
1578 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1579 "stored list cleared in activateObjects due to "
1580 "large amount of objects");
1583 // A list for objects that couldn't be converted to static for some
1584 // reason. They will be stored back.
1585 core::list<StaticObject> new_stored;
1586 // Loop through stored static objects
1587 for(core::list<StaticObject>::Iterator
1588 i = block->m_static_objects.m_stored.begin();
1589 i != block->m_static_objects.m_stored.end(); i++)
1591 /*infostream<<"Server: Creating an active object from "
1592 <<"static data"<<std::endl;*/
1593 StaticObject &s_obj = *i;
1594 // Create an active object from the data
1595 ServerActiveObject *obj = ServerActiveObject::create
1596 (s_obj.type, this, 0, s_obj.pos, s_obj.data);
1597 // If couldn't create object, store static data back.
1600 errorstream<<"ServerEnvironment::activateObjects(): "
1601 <<"failed to create active object from static object "
1602 <<"in block "<<PP(s_obj.pos/BS)
1603 <<" type="<<(int)s_obj.type<<" data:"<<std::endl;
1604 print_hexdump(verbosestream, s_obj.data);
1606 new_stored.push_back(s_obj);
1609 verbosestream<<"ServerEnvironment::activateObjects(): "
1610 <<"activated static object pos="<<PP(s_obj.pos/BS)
1611 <<" type="<<(int)s_obj.type<<std::endl;
1612 // This will also add the object to the active static list
1613 addActiveObjectRaw(obj, false);
1615 // Clear stored list
1616 block->m_static_objects.m_stored.clear();
1617 // Add leftover failed stuff to stored list
1618 for(core::list<StaticObject>::Iterator
1619 i = new_stored.begin();
1620 i != new_stored.end(); i++)
1622 StaticObject &s_obj = *i;
1623 block->m_static_objects.m_stored.push_back(s_obj);
1626 Note: Block hasn't really been modified here.
1627 The objects have just been activated and moved from the stored
1628 static list to the active static list.
1629 As such, the block is essentially the same.
1630 Thus, do not call block->raiseModified(MOD_STATE_WRITE_NEEDED).
1631 Otherwise there would be a huge amount of unnecessary I/O.
1636 Convert objects that are not standing inside active blocks to static.
1638 If m_known_by_count != 0, active object is not deleted, but static
1639 data is still updated.
1641 If force_delete is set, active object is deleted nevertheless. It
1642 shall only be set so in the destructor of the environment.
1644 void ServerEnvironment::deactivateFarObjects(bool force_delete)
1646 core::list<u16> objects_to_remove;
1647 for(core::map<u16, ServerActiveObject*>::Iterator
1648 i = m_active_objects.getIterator();
1649 i.atEnd()==false; i++)
1651 ServerActiveObject* obj = i.getNode()->getValue();
1654 // Do not deactivate if static data creation not allowed
1655 if(!force_delete && !obj->isStaticAllowed())
1658 // If pending deactivation, let removeRemovedObjects() do it
1659 if(!force_delete && obj->m_pending_deactivation)
1662 u16 id = i.getNode()->getKey();
1663 v3f objectpos = obj->getBasePosition();
1665 // The block in which the object resides in
1666 v3s16 blockpos_o = getNodeBlockPos(floatToInt(objectpos, BS));
1668 // If block is active, don't remove
1669 if(!force_delete && m_active_blocks.contains(blockpos_o))
1672 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
1673 <<"deactivating object id="<<id<<" on inactive block "
1674 <<PP(blockpos_o)<<std::endl;
1676 // If known by some client, don't immediately delete.
1677 bool pending_delete = (obj->m_known_by_count > 0 && !force_delete);
1680 Update the static data
1683 if(obj->isStaticAllowed())
1685 // Create new static object
1686 std::string staticdata_new = obj->getStaticData();
1687 StaticObject s_obj(obj->getType(), objectpos, staticdata_new);
1689 bool stays_in_same_block = false;
1690 bool data_changed = true;
1692 if(obj->m_static_exists){
1693 if(obj->m_static_block == blockpos_o)
1694 stays_in_same_block = true;
1696 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
1698 core::map<u16, StaticObject>::Node *n =
1699 block->m_static_objects.m_active.find(id);
1701 StaticObject static_old = n->getValue();
1703 float save_movem = obj->getMinimumSavedMovement();
1705 if(static_old.data == staticdata_new &&
1706 (static_old.pos - objectpos).getLength() < save_movem)
1707 data_changed = false;
1709 errorstream<<"ServerEnvironment::deactivateFarObjects(): "
1710 <<"id="<<id<<" m_static_exists=true but "
1711 <<"static data doesn't actually exist in "
1712 <<PP(obj->m_static_block)<<std::endl;
1716 bool shall_be_written = (!stays_in_same_block || data_changed);
1718 // Delete old static object
1719 if(obj->m_static_exists)
1721 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
1724 block->m_static_objects.remove(id);
1725 obj->m_static_exists = false;
1726 // Only mark block as modified if data changed considerably
1727 if(shall_be_written)
1728 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1729 "deactivateFarObjects: Static data "
1730 "changed considerably");
1734 // Add to the block where the object is located in
1735 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1736 // Get or generate the block
1737 MapBlock *block = m_map->emergeBlock(blockpos);
1741 if(block->m_static_objects.m_stored.size() >= 49){
1742 errorstream<<"ServerEnv: Trying to store id="<<obj->getId()
1743 <<" statically but block "<<PP(blockpos)
1744 <<" already contains "
1745 <<block->m_static_objects.m_stored.size()
1746 <<" (over 49) objects."
1747 <<" Forcing delete."<<std::endl;
1748 force_delete = true;
1750 u16 new_id = pending_delete ? id : 0;
1751 block->m_static_objects.insert(new_id, s_obj);
1753 // Only mark block as modified if data changed considerably
1754 if(shall_be_written)
1755 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1756 "deactivateFarObjects: Static data "
1757 "changed considerably");
1759 obj->m_static_exists = true;
1760 obj->m_static_block = block->getPos();
1765 errorstream<<"ServerEnv: Could not find or generate "
1766 <<"a block for storing id="<<obj->getId()
1767 <<" statically"<<std::endl;
1774 If known by some client, set pending deactivation.
1775 Otherwise delete it immediately.
1778 if(pending_delete && !force_delete)
1780 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
1781 <<"object id="<<id<<" is known by clients"
1782 <<"; not deleting yet"<<std::endl;
1784 obj->m_pending_deactivation = true;
1788 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
1789 <<"object id="<<id<<" is not known by clients"
1790 <<"; deleting"<<std::endl;
1792 // Tell the object about removal
1793 obj->removingFromEnvironment();
1794 // Deregister in scripting api
1795 scriptapi_rm_object_reference(m_lua, obj);
1797 // Delete active object
1798 if(obj->environmentDeletes())
1800 // Id to be removed from m_active_objects
1801 objects_to_remove.push_back(id);
1804 // Remove references from m_active_objects
1805 for(core::list<u16>::Iterator i = objects_to_remove.begin();
1806 i != objects_to_remove.end(); i++)
1808 m_active_objects.remove(*i);
1815 #include "clientsimpleobject.h"
1821 ClientEnvironment::ClientEnvironment(ClientMap *map, scene::ISceneManager *smgr,
1822 ITextureSource *texturesource, IGameDef *gamedef,
1823 IrrlichtDevice *irr):
1826 m_texturesource(texturesource),
1832 ClientEnvironment::~ClientEnvironment()
1834 // delete active objects
1835 for(core::map<u16, ClientActiveObject*>::Iterator
1836 i = m_active_objects.getIterator();
1837 i.atEnd()==false; i++)
1839 delete i.getNode()->getValue();
1842 for(core::list<ClientSimpleObject*>::Iterator
1843 i = m_simple_objects.begin(); i != m_simple_objects.end(); i++)
1852 Map & ClientEnvironment::getMap()
1857 ClientMap & ClientEnvironment::getClientMap()
1862 void ClientEnvironment::addPlayer(Player *player)
1864 DSTACK(__FUNCTION_NAME);
1866 It is a failure if player is local and there already is a local
1869 assert(!(player->isLocal() == true && getLocalPlayer() != NULL));
1871 Environment::addPlayer(player);
1874 LocalPlayer * ClientEnvironment::getLocalPlayer()
1876 for(core::list<Player*>::Iterator i = m_players.begin();
1877 i != m_players.end(); i++)
1879 Player *player = *i;
1880 if(player->isLocal())
1881 return (LocalPlayer*)player;
1886 void ClientEnvironment::step(float dtime)
1888 DSTACK(__FUNCTION_NAME);
1890 /* Step time of day */
1891 stepTimeOfDay(dtime);
1893 // Get some settings
1894 bool fly_allowed = m_gamedef->checkLocalPrivilege("fly");
1895 bool free_move = fly_allowed && g_settings->getBool("free_move");
1898 LocalPlayer *lplayer = getLocalPlayer();
1900 // collision info queue
1901 core::list<CollisionInfo> player_collisions;
1904 Get the speed the player is going
1906 bool is_climbing = lplayer->is_climbing;
1908 f32 player_speed = lplayer->getSpeed().getLength();
1911 Maximum position increment
1913 //f32 position_max_increment = 0.05*BS;
1914 f32 position_max_increment = 0.1*BS;
1916 // Maximum time increment (for collision detection etc)
1917 // time = distance / speed
1918 f32 dtime_max_increment = 1;
1919 if(player_speed > 0.001)
1920 dtime_max_increment = position_max_increment / player_speed;
1922 // Maximum time increment is 10ms or lower
1923 if(dtime_max_increment > 0.01)
1924 dtime_max_increment = 0.01;
1926 // Don't allow overly huge dtime
1930 f32 dtime_downcount = dtime;
1933 Stuff that has a maximum time increment
1942 if(dtime_downcount > dtime_max_increment)
1944 dtime_part = dtime_max_increment;
1945 dtime_downcount -= dtime_part;
1949 dtime_part = dtime_downcount;
1951 Setting this to 0 (no -=dtime_part) disables an infinite loop
1952 when dtime_part is so small that dtime_downcount -= dtime_part
1955 dtime_downcount = 0;
1963 v3f lplayerpos = lplayer->getPosition();
1966 if(free_move == false && is_climbing == false)
1969 v3f speed = lplayer->getSpeed();
1970 if(lplayer->swimming_up == false)
1971 speed.Y -= 9.81 * BS * dtime_part * 2;
1974 if(lplayer->in_water_stable || lplayer->in_water)
1976 f32 max_down = 2.0*BS;
1977 if(speed.Y < -max_down) speed.Y = -max_down;
1980 if(speed.getLength() > max)
1982 speed = speed / speed.getLength() * max;
1986 lplayer->setSpeed(speed);
1991 This also does collision detection.
1993 lplayer->move(dtime_part, *m_map, position_max_increment,
1994 &player_collisions);
1997 while(dtime_downcount > 0.001);
1999 //std::cout<<"Looped "<<loopcount<<" times."<<std::endl;
2001 for(core::list<CollisionInfo>::Iterator
2002 i = player_collisions.begin();
2003 i != player_collisions.end(); i++)
2005 CollisionInfo &info = *i;
2006 if(info.t == COLLISION_FALL)
2008 //f32 tolerance = BS*10; // 2 without damage
2009 //f32 tolerance = BS*12; // 3 without damage
2010 f32 tolerance = BS*14; // 5 without damage
2012 if(info.speed > tolerance)
2014 f32 damage_f = (info.speed - tolerance)/BS*factor;
2015 u16 damage = (u16)(damage_f+0.5);
2017 damageLocalPlayer(damage, true);
2023 A quick draft of lava damage
2025 if(m_lava_hurt_interval.step(dtime, 1.0))
2027 v3f pf = lplayer->getPosition();
2029 // Feet, middle and head
2030 v3s16 p1 = floatToInt(pf + v3f(0, BS*0.1, 0), BS);
2031 MapNode n1 = m_map->getNodeNoEx(p1);
2032 v3s16 p2 = floatToInt(pf + v3f(0, BS*0.8, 0), BS);
2033 MapNode n2 = m_map->getNodeNoEx(p2);
2034 v3s16 p3 = floatToInt(pf + v3f(0, BS*1.6, 0), BS);
2035 MapNode n3 = m_map->getNodeNoEx(p2);
2037 u32 damage_per_second = 0;
2038 damage_per_second = MYMAX(damage_per_second,
2039 m_gamedef->ndef()->get(n1).damage_per_second);
2040 damage_per_second = MYMAX(damage_per_second,
2041 m_gamedef->ndef()->get(n2).damage_per_second);
2042 damage_per_second = MYMAX(damage_per_second,
2043 m_gamedef->ndef()->get(n3).damage_per_second);
2045 if(damage_per_second != 0)
2047 damageLocalPlayer(damage_per_second, true);
2052 Stuff that can be done in an arbitarily large dtime
2054 for(core::list<Player*>::Iterator i = m_players.begin();
2055 i != m_players.end(); i++)
2057 Player *player = *i;
2058 v3f playerpos = player->getPosition();
2061 Handle non-local players
2063 if(player->isLocal() == false)
2066 player->move(dtime, *m_map, 100*BS);
2070 // Update lighting on all players on client
2071 u8 light = LIGHT_MAX;
2074 v3s16 p = player->getLightPosition();
2075 MapNode n = m_map->getNode(p);
2076 light = n.getLightBlend(getDayNightRatio(), m_gamedef->ndef());
2078 catch(InvalidPositionException &e){
2079 light = blend_light(getDayNightRatio(), LIGHT_SUN, 0);
2081 player->light = light;
2085 Step active objects and update lighting of them
2088 for(core::map<u16, ClientActiveObject*>::Iterator
2089 i = m_active_objects.getIterator();
2090 i.atEnd()==false; i++)
2092 ClientActiveObject* obj = i.getNode()->getValue();
2094 obj->step(dtime, this);
2096 if(m_active_object_light_update_interval.step(dtime, 0.21))
2102 v3s16 p = obj->getLightPosition();
2103 MapNode n = m_map->getNode(p);
2104 light = n.getLightBlend(getDayNightRatio(), m_gamedef->ndef());
2106 catch(InvalidPositionException &e){
2107 light = blend_light(getDayNightRatio(), LIGHT_SUN, 0);
2109 obj->updateLight(light);
2114 Step and handle simple objects
2116 for(core::list<ClientSimpleObject*>::Iterator
2117 i = m_simple_objects.begin(); i != m_simple_objects.end();)
2119 ClientSimpleObject *simple = *i;
2120 core::list<ClientSimpleObject*>::Iterator cur = i;
2122 simple->step(dtime);
2123 if(simple->m_to_be_removed){
2125 m_simple_objects.erase(cur);
2130 void ClientEnvironment::addSimpleObject(ClientSimpleObject *simple)
2132 m_simple_objects.push_back(simple);
2135 ClientActiveObject* ClientEnvironment::getActiveObject(u16 id)
2137 core::map<u16, ClientActiveObject*>::Node *n;
2138 n = m_active_objects.find(id);
2141 return n->getValue();
2144 bool isFreeClientActiveObjectId(u16 id,
2145 core::map<u16, ClientActiveObject*> &objects)
2150 for(core::map<u16, ClientActiveObject*>::Iterator
2151 i = objects.getIterator();
2152 i.atEnd()==false; i++)
2154 if(i.getNode()->getKey() == id)
2160 u16 getFreeClientActiveObjectId(
2161 core::map<u16, ClientActiveObject*> &objects)
2166 if(isFreeClientActiveObjectId(new_id, objects))
2176 u16 ClientEnvironment::addActiveObject(ClientActiveObject *object)
2179 if(object->getId() == 0)
2181 u16 new_id = getFreeClientActiveObjectId(m_active_objects);
2184 infostream<<"ClientEnvironment::addActiveObject(): "
2185 <<"no free ids available"<<std::endl;
2189 object->setId(new_id);
2191 if(isFreeClientActiveObjectId(object->getId(), m_active_objects) == false)
2193 infostream<<"ClientEnvironment::addActiveObject(): "
2194 <<"id is not free ("<<object->getId()<<")"<<std::endl;
2198 infostream<<"ClientEnvironment::addActiveObject(): "
2199 <<"added (id="<<object->getId()<<")"<<std::endl;
2200 m_active_objects.insert(object->getId(), object);
2201 object->addToScene(m_smgr, m_texturesource, m_irr);
2202 { // Update lighting immediately
2206 v3s16 p = object->getLightPosition();
2207 MapNode n = m_map->getNode(p);
2208 light = n.getLightBlend(getDayNightRatio(), m_gamedef->ndef());
2210 catch(InvalidPositionException &e){
2211 light = blend_light(getDayNightRatio(), LIGHT_SUN, 0);
2213 object->updateLight(light);
2215 return object->getId();
2218 void ClientEnvironment::addActiveObject(u16 id, u8 type,
2219 const std::string &init_data)
2221 ClientActiveObject* obj =
2222 ClientActiveObject::create(type, m_gamedef, this);
2225 infostream<<"ClientEnvironment::addActiveObject(): "
2226 <<"id="<<id<<" type="<<type<<": Couldn't create object"
2235 obj->initialize(init_data);
2237 catch(SerializationError &e)
2239 errorstream<<"ClientEnvironment::addActiveObject():"
2240 <<" id="<<id<<" type="<<type
2241 <<": SerializationError in initialize(),"
2242 <<" init_data="<<serializeJsonString(init_data)
2246 addActiveObject(obj);
2249 void ClientEnvironment::removeActiveObject(u16 id)
2251 verbosestream<<"ClientEnvironment::removeActiveObject(): "
2252 <<"id="<<id<<std::endl;
2253 ClientActiveObject* obj = getActiveObject(id);
2256 infostream<<"ClientEnvironment::removeActiveObject(): "
2257 <<"id="<<id<<" not found"<<std::endl;
2260 obj->removeFromScene();
2262 m_active_objects.remove(id);
2265 void ClientEnvironment::processActiveObjectMessage(u16 id,
2266 const std::string &data)
2268 ClientActiveObject* obj = getActiveObject(id);
2271 infostream<<"ClientEnvironment::processActiveObjectMessage():"
2272 <<" got message for id="<<id<<", which doesn't exist."
2278 obj->processMessage(data);
2280 catch(SerializationError &e)
2282 errorstream<<"ClientEnvironment::processActiveObjectMessage():"
2283 <<" id="<<id<<" type="<<obj->getType()
2284 <<" SerializationError in processMessage(),"
2285 <<" message="<<serializeJsonString(data)
2291 Callbacks for activeobjects
2294 void ClientEnvironment::damageLocalPlayer(u8 damage, bool handle_hp)
2296 LocalPlayer *lplayer = getLocalPlayer();
2300 if(lplayer->hp > damage)
2301 lplayer->hp -= damage;
2306 ClientEnvEvent event;
2307 event.type = CEE_PLAYER_DAMAGE;
2308 event.player_damage.amount = damage;
2309 event.player_damage.send_to_server = handle_hp;
2310 m_client_event_queue.push_back(event);
2314 Client likes to call these
2317 void ClientEnvironment::getActiveObjects(v3f origin, f32 max_d,
2318 core::array<DistanceSortedActiveObject> &dest)
2320 for(core::map<u16, ClientActiveObject*>::Iterator
2321 i = m_active_objects.getIterator();
2322 i.atEnd()==false; i++)
2324 ClientActiveObject* obj = i.getNode()->getValue();
2326 f32 d = (obj->getPosition() - origin).getLength();
2331 DistanceSortedActiveObject dso(obj, d);
2333 dest.push_back(dso);
2337 ClientEnvEvent ClientEnvironment::getClientEvent()
2339 if(m_client_event_queue.size() == 0)
2341 ClientEnvEvent event;
2342 event.type = CEE_NONE;
2345 return m_client_event_queue.pop_front();
2348 #endif // #ifndef SERVER